From 4bf9d9098fde993431e02996828d8aa8d93239a6 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Mon, 31 Aug 2026 00:59:20 +0700 Subject: [PATCH] fix production prerender without auth config --- app/admin/layout.tsx | 5 +++++ components/page-progress.tsx | 37 ++++++++++++++++++++++++++---------- lib/auth/server.ts | 11 +++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 app/admin/layout.tsx diff --git a/app/admin/layout.tsx b/app/admin/layout.tsx new file mode 100644 index 0000000..602f5f5 --- /dev/null +++ b/app/admin/layout.tsx @@ -0,0 +1,5 @@ +import { Suspense } from "react"; + +export default function AdminLayout({ children }: LayoutProps<"/admin">) { + return {children}; +} diff --git a/components/page-progress.tsx b/components/page-progress.tsx index aea5dc3..a420e3f 100644 --- a/components/page-progress.tsx +++ b/components/page-progress.tsx @@ -1,15 +1,14 @@ "use client" -import { Suspense, useEffect, useRef, useState } from "react" -import { usePathname, useSearchParams } from "next/navigation" +import { useEffect, useRef, useState } from "react" import { Progress } from "@/components/ui/progress" function PageProgressContent() { - const pathname = usePathname() - const searchParams = useSearchParams() const [progress, setProgress] = useState(0) const [isVisible, setIsVisible] = useState(false) + const [navigationKey, setNavigationKey] = useState(0) const intervalRef = useRef | null>(null) + const navigationUrlRef = useRef(null) // Start progress on click useEffect(() => { @@ -26,6 +25,7 @@ function PageProgressContent() { // Only show progress for actual page transitions if (isInternal && !isSamePage && target.getAttribute("target") !== "_blank") { + navigationUrlRef.current = window.location.href setIsVisible(true) setProgress(15) @@ -48,6 +48,26 @@ function PageProgressContent() { return () => document.removeEventListener("click", handleClick) }, []) + // Detect completion without reading Next.js URL hooks during prerendering. + useEffect(() => { + if (!isVisible || !navigationUrlRef.current) return + const initialUrl = navigationUrlRef.current + const checkInterval = window.setInterval(() => { + if (window.location.href !== initialUrl) { + window.clearInterval(checkInterval) + setNavigationKey((key) => key + 1) + } + }, 50) + const fallback = window.setTimeout(() => { + window.clearInterval(checkInterval) + setNavigationKey((key) => key + 1) + }, 10_000) + return () => { + window.clearInterval(checkInterval) + window.clearTimeout(fallback) + } + }, [isVisible]) + // Finish progress on route change useEffect(() => { if (intervalRef.current) { @@ -60,6 +80,7 @@ function PageProgressContent() { const timeout = setTimeout(() => { setIsVisible(false) setTimeout(() => setProgress(0), 300) // Reset after hiding + navigationUrlRef.current = null }, 400) return () => { clearTimeout(finishTimeout) @@ -67,7 +88,7 @@ function PageProgressContent() { } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pathname, searchParams]) + }, [navigationKey]) if (!isVisible && progress === 0) return null @@ -82,9 +103,5 @@ function PageProgressContent() { } export function PageProgress() { - return ( - - - - ) + return } diff --git a/lib/auth/server.ts b/lib/auth/server.ts index 0199b12..6848bd6 100644 --- a/lib/auth/server.ts +++ b/lib/auth/server.ts @@ -26,6 +26,12 @@ function required(name: string): string { return value; } +function hasAuthConfiguration(): boolean { + return ["DATABASE_URL", "BETTER_AUTH_URL", "BETTER_AUTH_SECRET", "ADMIN_EMAIL"].every( + (name) => Boolean(process.env[name]), + ); +} + function createAuth() { const adminEmail = required("ADMIN_EMAIL"); @@ -109,6 +115,11 @@ export async function getAdminSession(): Promise { }; } + // Public pages can be prerendered without the runtime auth secret. In that + // case the header simply omits the admin link; auth routes still fail loudly + // through getAuth() when authentication is actually used. + if (!hasAuthConfiguration()) return null; + const session = await getAuth().api.getSession({ headers: await headers() }); if ( !session?.session ||