From c225c9aef7a96fb7740e47dd877acd2660257927 Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Fri, 4 Sep 2026 17:56:43 +0200 Subject: [PATCH] fix(admin): mount Clerk client SDK on admin panel to stop UNAUTHORIZED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The admin panel (src/app/p/[secret]/layout.tsx) never wrapped its tree in ClerkThemeProvider, unlike (app)/layout.tsx — a SpeedUp-Plan P0 optimization that deliberately kept Clerk's client bundle off routes without a . Consequence: no client-side session-token refresh ran on admin pages. The initial page load stayed authenticated (Clerk's server-side handshake reissues the token on full document navigations), but subsequent tRPC XHRs — e.g. legal.publish — started failing with UNAUTHORIZED/401 a few minutes in, since that handshake only fires for navigations, not fetch calls. Confirmed live: a fresh page reload always restored a working session, matching this exactly. Mounting ClerkThemeProvider in the admin layout re-adds background token refresh. Also gives the legal-publish error handler an actionable message for UNAUTHORIZED specifically (distinct from other failures), since a residual case can still occur after a real 7-day session expiry. --- src/app/p/[secret]/layout.tsx | 59 +++++++++++++++++++------------ src/app/p/[secret]/legal/page.tsx | 13 ++++++- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/app/p/[secret]/layout.tsx b/src/app/p/[secret]/layout.tsx index 586009f..480e9fe 100644 --- a/src/app/p/[secret]/layout.tsx +++ b/src/app/p/[secret]/layout.tsx @@ -5,6 +5,7 @@ import { createTRPCCaller } from "@/trpc/server"; import { timingSafeStringEqual } from "@/lib/timing-safe-compare"; import { AdminMobileNav } from "@/components/admin/AdminMobileNav"; import { AdminSidebarNav } from "@/components/admin/AdminSidebarNav"; +import { ClerkThemeProvider } from "@/components/providers/ClerkThemeProvider"; import { LayoutDashboard, Users, @@ -80,31 +81,43 @@ export default async function AdminLayout({ children, params }: AdminLayoutProps })); return ( - // Admin stays permanently dark regardless of the visitor site's theme - // toggle — a moderator's ops tool, not a marketing surface — but now - // pinned to PawFeed's own dark palette (the `dark` class resolves the - // real design tokens) instead of a generic, disconnected zinc scaffold. -
- {/* Mobile topbar + drawer — hidden md: and up */} - + // ClerkThemeProvider mounts Clerk's client SDK, which keeps the short-lived + // __session cookie refreshed in the background. Without it (as before), + // this route tree never renewed the token client-side — the initial page + // load kept working (Clerk's server-side "handshake" reissues the token on + // full document navigations) but subsequent tRPC XHRs (e.g. legal.publish) + // started failing with UNAUTHORIZED/401 a few minutes into a session, since + // that handshake only runs for navigations, not fetch calls. This does + // re-add the ~240 KiB Clerk client bundle that (app)/layout.tsx's + // SpeedUp-Plan P0 deliberately kept off non-admin routes — acceptable here + // since the admin panel is an internal ops tool, not perf-critical. + + {/* Admin stays permanently dark regardless of the visitor site's theme + toggle — a moderator's ops tool, not a marketing surface — but now + pinned to PawFeed's own dark palette (the `dark` class resolves the + real design tokens) instead of a generic, disconnected zinc scaffold. */} +
+ {/* Mobile topbar + drawer — hidden md: and up */} + - {/* Sidebar — hidden below md:, permanent from md: up */} - - {/* Content */} -
{children}
-
+ {/* Content */} +
{children}
+
+ ); } diff --git a/src/app/p/[secret]/legal/page.tsx b/src/app/p/[secret]/legal/page.tsx index c0aa1a6..964e641 100644 --- a/src/app/p/[secret]/legal/page.tsx +++ b/src/app/p/[secret]/legal/page.tsx @@ -45,7 +45,18 @@ export default function AdminLegalPage() { toast.success("Veröffentlicht — Owner sehen die Meldung beim nächsten Laden der App"); setChangeSummary(""); }, - onError: () => toast.error("Veröffentlichung fehlgeschlagen"), + // UNAUTHORIZED means the Clerk session cookie went stale (see + // ClerkThemeProvider in layout.tsx for the background-refresh fix) — + // distinct from every other failure, so surface it with an actionable + // message instead of the generic one. The typed text is preserved + // (no setChangeSummary("") here) so a reload doesn't lose it. + onError: (error) => { + if (error.data?.code === "UNAUTHORIZED") { + toast.error("Session abgelaufen — bitte Seite neu laden und erneut versuchen"); + } else { + toast.error("Veröffentlichung fehlgeschlagen"); + } + }, }) );