5477b3419f
Deploy to route.crispygoat.com / deploy (push) Failing after 4m16s
Two errors were aborting the Gitea build:
1. DYNAMIC_SERVER_USAGE on /admin/settings/square-sync (and any admin page):
getAdminUser() reads cookies() via next/headers. The admin layout tried
to prerender statically, so the first child page that hit cookies()
aborted the build. Added 'export const dynamic = "force-dynamic"' to
src/app/admin/layout.tsx so the whole admin tree opts out of static
prerender.
2. Prerender ECONNREFUSED on /indian-river-direct/stops and the sitemap:
getPublicStopsForBrand / getActiveStopsForSitemap / getBrandSettingsPublic
fetch NEXT_PUBLIC_SUPABASE_URL at build time. The Gitea runner sets the
Supabase env vars (so the existing env-var guard passes) but the URL
is unreachable, so fetch throws ECONNREFUSED and the prerender aborts.
Wrapped each fetch in try/catch returning [] / {success: false} so the
prerender completes; runtime behavior is unchanged when the fetch
succeeds.
Also added force-dynamic to the square-sync page itself as belt-and-braces
in case the layout change doesn't propagate.
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import AdminSidebar from "@/components/admin/AdminSidebar";
|
|
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
|
import { getAdminUser } from "@/lib/admin-permissions";
|
|
import { getActiveBrandId } from "@/lib/brand-scope";
|
|
import { listBrandsForAdmin } from "@/actions/brands";
|
|
import { redirect } from "next/navigation";
|
|
import "@/styles/admin-design-system.css";
|
|
import { ToastProvider } from "@/components/admin/Toast";
|
|
import { ToastContainer } from "@/components/admin/ToastContainer";
|
|
|
|
// Admin layout calls getAdminUser() which reads cookies(). Without this,
|
|
// Next.js tries to prerender the entire /admin/* tree statically and the
|
|
// first page that hits cookies() aborts the build with DYNAMIC_SERVER_USAGE.
|
|
export const dynamic = "force-dynamic";
|
|
|
|
// Toast provider wrapper component
|
|
function ToastProviderWrapper({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<ToastProvider>
|
|
{children}
|
|
<ToastContainer />
|
|
</ToastProvider>
|
|
);
|
|
}
|
|
|
|
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
|
|
let adminUser = null;
|
|
let authError: string | null = null;
|
|
|
|
// Robust auth with try-catch to prevent crashes
|
|
try {
|
|
adminUser = await getAdminUser();
|
|
} catch (error) {
|
|
console.error("Admin auth error:", error);
|
|
authError = "Failed to verify authentication. Please try again.";
|
|
}
|
|
|
|
// Auth verification failed
|
|
if (authError) {
|
|
return (
|
|
<ToastProviderWrapper>
|
|
<AdminSidebar userRole={null} />
|
|
<div className="min-h-screen lg:pl-60 admin-section" style={{ backgroundColor: "var(--admin-bg)" }}>
|
|
<AdminAccessDenied message={authError} />
|
|
</div>
|
|
</ToastProviderWrapper>
|
|
);
|
|
}
|
|
|
|
// Not authenticated
|
|
if (!adminUser) {
|
|
return (
|
|
<ToastProviderWrapper>
|
|
<AdminSidebar userRole={null} />
|
|
<div className="min-h-screen lg:pl-60 admin-section" style={{ backgroundColor: "var(--admin-bg)" }}>
|
|
<AdminAccessDenied message="Your account does not have admin access." />
|
|
</div>
|
|
</ToastProviderWrapper>
|
|
);
|
|
}
|
|
|
|
// Must change password
|
|
if (adminUser.must_change_password) {
|
|
redirect("/change-password");
|
|
}
|
|
|
|
// Resolve the active brand (URL > cookie > legacy > first of brand_ids)
|
|
const activeBrandId = await getActiveBrandId(adminUser);
|
|
|
|
// Fetch accessible brands for the sidebar BrandSelector. We do this
|
|
// unconditionally — `listBrandsForAdmin` is cheap and the sidebar
|
|
// decides whether to show the dropdown.
|
|
const brands = await listBrandsForAdmin();
|
|
|
|
return (
|
|
<ToastProviderWrapper>
|
|
<AdminSidebar
|
|
userRole={adminUser.role}
|
|
brandIds={adminUser.brand_ids}
|
|
activeBrandId={activeBrandId}
|
|
brands={brands}
|
|
/>
|
|
<div className="min-h-screen lg:pl-60 admin-section" style={{ backgroundColor: "var(--admin-bg)" }}>
|
|
{children}
|
|
</div>
|
|
</ToastProviderWrapper>
|
|
);
|
|
}
|