diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx
index fb98185..5faf03a 100644
--- a/src/app/(app)/layout.tsx
+++ b/src/app/(app)/layout.tsx
@@ -1,7 +1,23 @@
+import { redirect } from "next/navigation";
+import { auth } from "@/lib/auth";
+import { prisma } from "@/lib/prisma";
import { Sidebar } from "@/components/layout/sidebar";
import { Header } from "@/components/layout/header";
-export default function AppLayout({ children }: { children: React.ReactNode }) {
+export default async function AppLayout({ children }: { children: React.ReactNode }) {
+ // Guard against a stale JWT session whose user no longer exists in the
+ // database (e.g. after a DB reset). The signed cookie still passes edge
+ // middleware, but every downstream query keyed on session.user.id would fail.
+ // Send such sessions to /logout, which clears the cookie and returns to login.
+ const session = await auth();
+ if (session?.user?.id) {
+ const user = await prisma.user.findUnique({
+ where: { id: session.user.id },
+ select: { id: true },
+ });
+ if (!user) redirect("/logout");
+ }
+
return (
diff --git a/src/app/logout/route.ts b/src/app/logout/route.ts
new file mode 100644
index 0000000..66b4f7d
--- /dev/null
+++ b/src/app/logout/route.ts
@@ -0,0 +1,9 @@
+import { signOut } from "@/lib/auth";
+
+// Server-side sign-out that clears the JWT session cookie and redirects to the
+// login page. Used to recover from a stale session whose user no longer exists
+// in the database (e.g. after a DB reset), which a client-only signOut can't
+// reach because the app crashes before rendering the user menu.
+export async function GET() {
+ await signOut({ redirectTo: "/login" });
+}
diff --git a/src/data/settings.queries.ts b/src/data/settings.queries.ts
index d353462..246fe41 100644
--- a/src/data/settings.queries.ts
+++ b/src/data/settings.queries.ts
@@ -1,20 +1,37 @@
+import { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
+const DEFAULT_SETTINGS = {
+ lowStockThreshold: 20,
+ currency: "EUR",
+ theme: "dark",
+ units: "metric",
+} as const;
+
export async function getUserSettings(userId: string) {
let settings = await prisma.userSettings.findUnique({
where: { userId },
});
if (!settings) {
- settings = await prisma.userSettings.create({
- data: {
- userId,
- lowStockThreshold: 20,
- currency: "EUR",
- theme: "dark",
- units: "metric",
- },
- });
+ try {
+ settings = await prisma.userSettings.create({
+ data: { userId, ...DEFAULT_SETTINGS },
+ });
+ } catch (err) {
+ // The session's user may no longer exist (e.g. a stale JWT cookie after a
+ // database reset). Creating settings then hits a foreign-key violation
+ // (P2003). Don't crash the Server Component render — return unsaved
+ // defaults. The (app) layout guard redirects such stale sessions to
+ // sign-out, so this fallback is only ever momentarily visible.
+ if (
+ err instanceof Prisma.PrismaClientKnownRequestError &&
+ err.code === "P2003"
+ ) {
+ return { id: "", userId, ...DEFAULT_SETTINGS };
+ }
+ throw err;
+ }
}
return settings;