Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/(dashboard)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ export default function SettingsPage() {
{/* Settings Navigation Sidebar */}
<div className="lg:col-span-1">
<SettingsSidebar
// @ts-ignore
activeSection={params.settings_tab}
setActiveSection={(v) =>
// @ts-ignore
setParams({ ...params, settings_tab: v })
Comment on lines +27 to 31
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid @ts-ignore — fix the underlying type mismatch instead.

Using @ts-ignore suppresses TypeScript errors without resolving them, which defeats the purpose of having typed props. The mismatch likely stems from params.settings_tab being typed as string (from nuqs) while SettingsSidebar and SettingsContent expect SettingsTab.

Consider typing the nuqs schema to align with SettingsTab:

// In nuqs/index.ts, import and use SettingsTab for better type inference
settings_tab: parseAsStringEnum<SettingsTab>([...]).withDefault("profile")

Or cast at the usage site with a type assertion rather than ignoring:

activeSection={params.settings_tab as SettingsTab}

Also applies to: 39-40

🤖 Prompt for AI Agents
In @app/(dashboard)/settings/page.tsx around lines 27-31, The current @ts-ignore
hides a type mismatch between params.settings_tab and the
SettingsSidebar/SettingsContent props; fix by aligning types instead of
ignoring: update your nuqs schema to parse settings_tab as the SettingsTab enum
(e.g., in nuqs/index.ts use parseAsStringEnum<SettingsTab>(...) with a default)
so params.settings_tab is typed correctly, or at the usage site replace the
@ts-ignore with an explicit assertion activeSection={params.settings_tab as
SettingsTab}; remove the @ts-ignore lines and ensure both SettingsSidebar and
SettingsContent accept SettingsTab typed values.

}
/>
Expand All @@ -34,6 +36,7 @@ export default function SettingsPage() {
{/* Main Content Area */}
<div className="lg:col-span-3">
<SettingsContent
// @ts-ignore
activeSection={params.settings_tab}
hasChanges={hasChanges}
setHasChanges={setHasChanges}
Expand Down
5 changes: 3 additions & 2 deletions app/api/settings/profile/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function GET() {
image: true,
showEmail: true,
showLocation: true,
username: true,
},
});

Expand All @@ -31,7 +32,7 @@ export async function GET() {
console.error("Profile GET error:", error);
return NextResponse.json(
{ error: "Failed to fetch profile" },
{ status: 500 },
{ status: 500 }
);
}
}
Expand Down Expand Up @@ -71,7 +72,7 @@ export async function PUT(request: NextRequest) {
console.error("Profile PUT error:", error);
return NextResponse.json(
{ error: "Failed to update profile" },
{ status: 500 },
{ status: 500 }
);
}
}
38 changes: 32 additions & 6 deletions app/api/settings/sessions/[sessionId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import { NextRequest, NextResponse } from "next/server";
import { requireAuth } from "@/lib/auth-utils";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { prisma } from "@/lib/prisma";

export async function DELETE(
request: NextRequest,
{ params }: { params: { sessionId: string } },
{ params }: { params: Promise<{ sessionId: string }> }
) {
try {
const session = await requireAuth();
const session = await auth.api.getSession({ headers: await headers() });

// For demo purposes, just return success
// In production, you'd revoke the specific session
if (!session) {
return NextResponse.json(
{ error: "Unauthorized", success: false },
{ status: 401 }
);
}
const currentToken = session.session.token;
const { sessionId } = await params;
const sessionToRevoke = await prisma.session.findUnique({
where: { id: sessionId },
});

// Prevent revoking current session
if (sessionToRevoke?.token === currentToken) {
return NextResponse.json(
{ error: "Cannot revoke current session" },
{ status: 400 }
);
}
Comment on lines +21 to +31
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing existence check for the session to revoke.

Same issue as in app/api/settings/sessions/route.ts: if the session doesn't exist, sessionToRevoke is null, and the code proceeds to attempt deletion which will throw, resulting in a 500 error instead of a proper 404.

Additionally, consider adding userId: session.user.id to the findUnique query to ensure the session belongs to the authenticated user before comparing tokens.

🔎 Proposed fix
     const { sessionId } = await params;
     const sessionToRevoke = await prisma.session.findUnique({
-      where: { id: sessionId },
+      where: { id: sessionId, userId: session.user.id },
     });

+    if (!sessionToRevoke) {
+      return NextResponse.json(
+        { error: "Session not found" },
+        { status: 404 }
+      );
+    }
+
     // Prevent revoking current session
-    if (sessionToRevoke?.token === currentToken) {
+    if (sessionToRevoke.token === currentToken) {
       return NextResponse.json(
         { error: "Cannot revoke current session" },
         { status: 400 }
       );
     }
-
-    await prisma.session.delete({
-      where: {
-        id: sessionId,
-        userId: session.user.id,
-      },
-    });
+
+    await prisma.session.delete({
+      where: { id: sessionId },
+    });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @app/api/settings/sessions/[sessionId]/route.ts around lines 21-31, Check
that prisma.session.findUnique returns a session and that it belongs to the
authenticated user before proceeding: modify the findUnique call (symbol:
prisma.session.findUnique) to include userId: session.user.id in the where
clause, then if sessionToRevoke is null return NextResponse.json({ error:
"Session not found" }, { status: 404 }); only after that compare
sessionToRevoke.token with currentToken and proceed with deletion; ensure you
reference sessionToRevoke and currentToken exactly as in the diff.


await prisma.session.delete({
where: {
id: sessionId,
userId: session.user.id,
},
});

return NextResponse.json({ message: "Session revoked successfully" });
} catch (error) {
console.error("Session revoke error:", error);
return NextResponse.json(
{ error: "Failed to revoke session" },
{ status: 500 },
{ status: 500 }
);
}
}
24 changes: 19 additions & 5 deletions app/api/settings/sessions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
import { requireAuth } from "@/lib/auth-utils";
import { prisma } from "@/lib/prisma";
import { UAParser } from "ua-parser-js";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

// Helper function to parse user agent and get device info
function parseUserAgent(userAgent: string) {
Expand Down Expand Up @@ -59,10 +61,15 @@ function getTimeAgo(date: Date) {

export async function GET(request: NextRequest) {
try {
const session = await requireAuth();
const session = await auth.api.getSession({ headers: await headers() });

// Get current session token from cookie
const currentToken = request.cookies.get("session")?.value;
if (!session) {
return NextResponse.json(
{ error: "Unauthorized", success: false },
{ status: 401 }
);
}
const currentToken = session.session.token;

// Fetch all sessions for the user
const sessions = await prisma.session.findMany({
Expand Down Expand Up @@ -96,8 +103,15 @@ export async function GET(request: NextRequest) {

export async function DELETE(request: NextRequest) {
try {
const session = await requireAuth();
const currentToken = request.cookies.get("session")?.value;
const session = await auth.api.getSession({ headers: await headers() });

if (!session) {
return NextResponse.json(
{ error: "Unauthorized", success: false },
{ status: 401 }
);
}
const currentToken = session.session.token;

const { searchParams } = new URL(request.url);
const sessionId = searchParams.get("sessionId");
Expand Down
Loading