Skip to content

Commit ecf0736

Browse files
authored
Merge pull request #73 from mrepol742/master
Improve rate limiter and fix routing and UI bugs
2 parents 9fa6362 + 3e727d4 commit ecf0736

16 files changed

Lines changed: 180 additions & 141 deletions

File tree

app/api/conversations/[id]/presence/route.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@ export async function PATCH(
1212
}
1313

1414
const { id: conversationId } = await params;
15-
const body = await req.json().catch(() => ({}));
16-
const { markRead } = body as { markRead?: boolean };
15+
const { mark_read } = await req.json();
1716

1817
const timestamp = new Date();
1918

20-
const data: { lastSeenAt: Date; lastReadAt?: Date } = {
21-
lastSeenAt: timestamp,
19+
const data: { last_seen_at: Date; last_read_at?: Date } = {
20+
last_seen_at: timestamp,
2221
};
23-
if (markRead) {
24-
data.lastReadAt = timestamp;
22+
if (mark_read) {
23+
data.last_read_at = timestamp;
2524
}
2625

2726
await prisma.conversationParticipant.updateMany({

app/api/conversations/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ export async function POST(req: Request) {
4747
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
4848
}
4949

50-
const { otherUserId, otherUserEmail } = await req.json();
50+
const { other_user_id, other_user_email } = await req.json();
5151

52-
if (!otherUserId) {
52+
if (!other_user_id) {
5353
return NextResponse.json(
54-
{ error: "otherUserId is required." },
54+
{ error: "other_user_id is required." },
5555
{ status: 400 },
5656
);
5757
}
@@ -71,8 +71,8 @@ export async function POST(req: Request) {
7171
last_read_at: timestamp,
7272
},
7373
{
74-
user_id: otherUserId,
75-
email: otherUserEmail ?? "",
74+
user_id: other_user_id,
75+
email: other_user_email ?? "",
7676
last_seen_at: EPOCH,
7777
last_read_at: EPOCH,
7878
},

app/api/messages/route.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ export async function POST(req: Request) {
5858
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
5959
}
6060

61-
const { conversationId, text, attachments } = await req.json();
61+
const { conversation_id, text, attachments } = await req.json();
6262

6363
if (
64-
!conversationId ||
64+
!conversation_id ||
6565
(!text?.trim() && (!attachments || attachments.length === 0))
6666
) {
6767
return NextResponse.json(
@@ -73,7 +73,7 @@ export async function POST(req: Request) {
7373
const participant = await prisma.conversationParticipant.findUnique({
7474
where: {
7575
conversation_id_user_id: {
76-
conversation_id: conversationId,
76+
conversation_id,
7777
user_id: session.user.id,
7878
},
7979
},
@@ -85,7 +85,7 @@ export async function POST(req: Request) {
8585

8686
const message = await prisma.message.create({
8787
data: {
88-
conversation_id: conversationId,
88+
conversation_id,
8989
sender_id: session.user.id,
9090
text: text?.trim() ?? "",
9191
attachments: attachments ?? [],
@@ -101,11 +101,11 @@ export async function POST(req: Request) {
101101
created_at: message.created_at.toISOString(),
102102
};
103103

104-
emitter.emit(`chat:${conversationId}`, { type: "message", data: payload });
104+
emitter.emit(`chat:${conversation_id}`, { type: "message", data: payload });
105105

106106
const participants = await prisma.conversationParticipant.findMany({
107107
where: {
108-
conversation_id: conversationId,
108+
conversation_id,
109109
user_id: { not: session.user.id },
110110
},
111111
select: { user_id: true },
@@ -114,7 +114,7 @@ export async function POST(req: Request) {
114114
for (const p of participants) {
115115
emitter.emit(`user:${p.user_id}`, {
116116
type: "new_message",
117-
data: { conversation_id: conversationId, sender_id: session.user.id },
117+
data: { conversation_id, sender_id: session.user.id },
118118
});
119119
}
120120

app/api/wakatime/sync/route.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,55 @@
11
import { NextResponse } from "next/server";
2-
import { getCurrentUser } from "@/app/lib/auth/user";
32
import {
43
saveWakatimeApiKey,
54
syncWakatimeData,
65
validateWakatimeApiKey,
76
} from "@/app/lib/wakatime/sync";
7+
import { auth } from "@/app/lib/auth";
88

9-
export async function GET(request: Request) {
10-
const { user } = await getCurrentUser();
11-
const { searchParams } = new URL(request.url);
12-
const apiKey = searchParams.get("apiKey") || "";
13-
const saveOnly =
14-
searchParams.get("saveOnly") === "1" ||
15-
searchParams.get("saveOnly") === "true";
9+
export async function GET(req: Request) {
10+
const session = await auth();
11+
if (!session?.user?.id) {
12+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
13+
}
1614

17-
const validationError = validateWakatimeApiKey(apiKey);
18-
if (validationError) {
19-
return NextResponse.json({ error: validationError }, { status: 400 });
15+
const result = await syncWakatimeData({
16+
userId: session.user.id,
17+
incomingApiKey: "",
18+
storedApiKey: session.user.wakatime_api_key ?? undefined,
19+
});
20+
21+
if (!result.success && result.status !== 200) {
22+
return NextResponse.json(
23+
{ error: result.error },
24+
{ status: result.status },
25+
);
2026
}
2127

22-
if (!user) {
28+
return NextResponse.json({
29+
success: result.success,
30+
data: result.data,
31+
error: result.error,
32+
});
33+
}
34+
35+
export async function POST(req: Request) {
36+
const session = await auth();
37+
if (!session?.user?.id) {
2338
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
2439
}
2540

26-
if (saveOnly) {
27-
const result = await saveWakatimeApiKey({ userId: user.id, apiKey });
41+
const { api_key, save_only } = await req.json();
42+
43+
const validationError = validateWakatimeApiKey(api_key);
44+
if (validationError) {
45+
return NextResponse.json({ error: validationError }, { status: 400 });
46+
}
47+
48+
if (save_only) {
49+
const result = await saveWakatimeApiKey({
50+
userId: session.user.id,
51+
apiKey: api_key,
52+
});
2853

2954
if (!result.success) {
3055
return NextResponse.json(
@@ -37,9 +62,9 @@ export async function GET(request: Request) {
3762
}
3863

3964
const result = await syncWakatimeData({
40-
userId: user.id,
41-
incomingApiKey: apiKey,
42-
storedApiKey: user.wakatime_api_key,
65+
userId: session.user.id,
66+
incomingApiKey: api_key,
67+
storedApiKey: session.user.wakatime_api_key,
4368
});
4469

4570
if (!result.success && result.status !== 200) {

app/components/Chat.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -302,19 +302,18 @@ export default function Chat({ user }: { user: ChatUserShape }) {
302302
: undefined;
303303

304304
const activeLabel = isGlobalActive
305-
? "Global Chat"
305+
? "Global"
306306
: activeOtherUser?.email?.split("@")[0] || "Unknown";
307307

308308
const activeSublabel = isGlobalActive
309-
? "Public Channel"
309+
? "Worldwide"
310310
: activeOtherUserOnline
311311
? "Online"
312312
: "Offline";
313313

314-
const activeSublabelClass =
315-
activeOtherUserOnline || isGlobalActive
316-
? "text-emerald-600"
317-
: "text-gray-500";
314+
const activeSublabelClass = activeOtherUserOnline
315+
? "text-emerald-600"
316+
: "text-gray-500";
318317

319318
const typingIndicatorText = activeTypingState
320319
? isGlobalActive
@@ -355,15 +354,15 @@ export default function Chat({ user }: { user: ChatUserShape }) {
355354
attachments={allMediaAttachments}
356355
onChange={setMediaViewer}
357356
/>
358-
<div className="flex h-screen w-full bg-transparent text-gray-900 overflow-hidden relative">
357+
<div className="flex h-220 sm:h-screen w-full bg-transparent text-gray-900 overflow-hidden relative">
359358
{/* Left Sidebar */}
360359
<div
361-
className={`w-full md:w-[300px] flex-shrink-0 border-r border-gray-200 flex flex-col bg-white md:bg-transparent z-20 absolute md:relative h-full transition-transform duration-300 ${conversationId ? "-translate-x-full md:translate-x-0" : "translate-x-0"}`}
360+
className={`w-full md:w-[300px] flex-shrink-0 border-r border-gray-200 flex flex-col bg-white z-20 absolute md:relative h-full transition-transform duration-300 ${conversationId ? "-translate-x-full md:translate-x-0" : "translate-x-0"}`}
362361
>
363362
<div className="p-5 border-b border-gray-200">
364363
<div className="flex items-center justify-between mb-4">
365364
<h2 className="text-lg font-bold text-gray-700 tracking-tight">
366-
Message category
365+
Messages
367366
</h2>
368367
<button
369368
onClick={() => setShowModal(true)}
@@ -492,7 +491,7 @@ export default function Chat({ user }: { user: ChatUserShape }) {
492491
{conversationId ? (
493492
<>
494493
{/* Header */}
495-
<div className="h-[72px] flex items-center justify-between px-4 sm:px-6 border-b border-gray-200 bg-white/[0.01] z-10 flex-shrink-0">
494+
<div className="h-[72px] flex items-center justify-between px-4 sm:px-6 border-b border-gray-200 bg-white z-10 flex-shrink-0">
496495
<div className="flex items-center gap-2 sm:gap-3.5">
497496
<button
498497
onClick={() => setConversationId(null)}
@@ -503,16 +502,6 @@ export default function Chat({ user }: { user: ChatUserShape }) {
503502
className="w-3.5 h-3.5"
504503
/>
505504
</button>
506-
<div className="relative">
507-
<div
508-
className={`flex justify-center items-center w-11 h-11 rounded-full text-[16px] font-bold shadow-sm ${isGlobalActive ? "bg-blue-500/15 text-blue-600 border border-blue-500/30" : "bg-neutral-800 text-gray-700 border border-gray-200"}`}
509-
>
510-
{activeInitials}
511-
</div>
512-
{!isGlobalActive && activeOtherUserOnline && (
513-
<div className="absolute bottom-0.5 right-0.5 w-3 h-3 bg-emerald-400 border-[2px] border-transparent rounded-full"></div>
514-
)}
515-
</div>
516505
<div>
517506
<h2 className="text-[16px] font-bold text-gray-700 leading-tight">
518507
{activeLabel}
@@ -682,7 +671,7 @@ export default function Chat({ user }: { user: ChatUserShape }) {
682671
{/* Right Sidebar */}
683672
{conversationId && (
684673
<div
685-
className={`w-full sm:w-[320px] flex-shrink-0 border-l border-gray-200 flex flex-col absolute right-0 top-0 bottom-0 h-full z-40 bg-white md:bg-white xl:bg-transparent xl:relative xl:transform-none transition-transform duration-300 ${showRightSidebar ? "translate-x-0" : "translate-x-full xl:translate-x-0 xl:hidden"}`}
674+
className={`w-full sm:w-[320px] flex-shrink-0 border-l border-gray-200 flex flex-col absolute right-0 top-0 bottom-0 h-full z-40 bg-white xl:bg-transparent xl:relative xl:transform-none transition-transform duration-300 ${showRightSidebar ? "translate-x-0" : "translate-x-full xl:translate-x-0 xl:hidden"}`}
686675
>
687676
<div className="absolute top-4 right-4 xl:hidden">
688677
<button
@@ -839,9 +828,11 @@ export default function Chat({ user }: { user: ChatUserShape }) {
839828
)}
840829

841830
{showModal && (
842-
<div className="fixed inset-0 flex items-center justify-center bg-black/70 z-50 backdrop-blur-sm">
843-
<div className="glass-card w-[400px] p-6">
844-
<h3 className="text-lg font-bold text-gray-900 mb-4">New Message</h3>
831+
<div className="fixed p-5 inset-0 flex items-center justify-center bg-black/70 z-50 backdrop-blur-sm">
832+
<div className="glass-card p-8">
833+
<h3 className="text-lg font-bold text-gray-900 mb-4">
834+
New Message
835+
</h3>
845836
<input
846837
value={search}
847838
onChange={(e) => setSearch(e.target.value)}

app/components/auth/VerifyWakatime.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ export default function VerifyWakatime() {
4343

4444
const verifyWakatimePromise = new Promise<void>(async (resolve, reject) => {
4545
try {
46-
const wakatimeSyncResponse = await fetch(
47-
`/api/wakatime/sync?apiKey=${encodeURIComponent(apiKey)}`,
48-
);
46+
const wakatimeSyncResponse = await fetch(`/api/wakatime/sync`, {
47+
method: "POST",
48+
headers: { "Content-Type": "application/json" },
49+
body: JSON.stringify({ api_key: apiKey, save_only: true }),
50+
});
4951
if (!wakatimeSyncResponse.ok)
5052
throw new Error("Failed to sync Wakatime.");
5153

@@ -86,7 +88,12 @@ export default function VerifyWakatime() {
8688
href="/"
8789
className="flex items-center gap-3 w-fit hover:opacity-80 transition"
8890
>
89-
<Image src="/apple-touch-icon.png" alt="Devpulse Logo" width={40} height={40} />
91+
<Image
92+
src="/apple-touch-icon.png"
93+
alt="Devpulse Logo"
94+
width={40}
95+
height={40}
96+
/>
9097
<span className="text-2xl font-bold tracking-tight text-white">
9198
Devpulse
9299
</span>
@@ -152,7 +159,12 @@ export default function VerifyWakatime() {
152159
href="/"
153160
className="lg:hidden flex items-center justify-center gap-3 mb-10"
154161
>
155-
<Image src="/apple-touch-icon.png" alt="Devpulse Logo" width={40} height={40} />
162+
<Image
163+
src="/apple-touch-icon.png"
164+
alt="Devpulse Logo"
165+
width={40}
166+
height={40}
167+
/>
156168
<h2 className="text-3xl font-bold text-gray-900">Devpulse</h2>
157169
</Link>
158170

app/components/chat/Conversations.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,14 @@ export default function Conversations({
4646
key={idx}
4747
type="button"
4848
onClick={() => setConversationId(conv.id)}
49-
className={`w-full flex items-center gap-3.5 p-3 rounded-xl transition-all text-left ${
49+
className={`w-full flex items-center bg-gray-100 gap-3.5 p-3 rounded-xl transition-all text-left ${
5050
isActive
51-
? "bg-gray-100 border border-gray-200 shadow-sm"
51+
? "bg-gray-100 border border-gray-300"
5252
: "hover:bg-gray-100 border border-transparent opacity-80 hover:opacity-100"
5353
}`}
5454
>
5555
<div className="relative flex-shrink-0">
56-
<div
57-
className={`flex justify-center items-center w-[38px] h-[38px] rounded-full text-[14px] font-bold transition-all border ${
58-
isGlobal
59-
? "bg-blue-500/15 text-blue-600 border-blue-500/30"
60-
: "bg-neutral-800 text-gray-700 border-gray-200 shadow-sm"
61-
}`}
62-
>
56+
<div className="flex justify-center items-center w-[38px] h-[38px] rounded-full text-[14px] font-bold transition-all border bg-white-800 text-gray-500 border-gray-200">
6357
{initials}
6458
</div>
6559
{!isGlobal && isOnline && (

app/components/chat/Messages.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ export default function Messages({
225225
<div
226226
className={`px-5 py-3 text-[14px] leading-relaxed break-words break-all overflow-x-hidden ${
227227
isSelf
228-
? "bg-blue-600 border border-blue-500/50 text-gray-900 rounded-2xl rounded-br-sm shadow-sm"
229-
: "bg-[rgba(15,15,40,0.6)] border border-blue-200 text-gray-700 rounded-2xl rounded-bl-sm"
228+
? "bg-blue-500 text-white rounded-2xl rounded-br-sm shadow-sm"
229+
: "bg-white border border-gray-200 text-gray-500 rounded-2xl rounded-bl-sm"
230230
}`}
231231
>
232232
<div className="prose prose-invert prose-sm max-w-none break-words break-all whitespace-pre-wrap leading-[1.6]">

app/components/chat/hooks/useChatConversationActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ export function useChatConversationActions({
7171
method: "POST",
7272
headers: { "Content-Type": "application/json" },
7373
body: JSON.stringify({
74-
otheruser_id: otherUser.user_id,
75-
otherUserEmail: otherUser.email,
74+
other_user_id: otherUser.user_id,
75+
other_user_email: otherUser.email,
7676
}),
7777
});
7878

app/components/chat/hooks/useChatMessageComposer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function useChatMessageComposer({
8686
method: "POST",
8787
headers: { "Content-Type": "application/json" },
8888
body: JSON.stringify({
89-
conversationId: targetConversationId,
89+
conversation_id: targetConversationId,
9090
text: outgoingText,
9191
attachments: [],
9292
}),

0 commit comments

Comments
 (0)