fix(frontend): read AUTH_MODE at request time so Clerk toggle works

The frontend is documented as a single image whose AUTH_MODE env var
selects local vs Clerk auth per deployment (docs/clerk.md). But the root
layout and /login read AUTH_MODE during render with no request-time
signal, so Next 16 statically prerendered them at build time in the
build host's mode (local). The deployed image then served the local
login form forever, ignoring AUTH_MODE=clerk at runtime — ClerkProvider
and <SignIn/> never appeared.

Add `await connection()` (the v16 way to read env at request time) in
the layout's IdentityProvider and in /login so both render per-request
and honor the runtime AUTH_MODE.

Also add a root .dockerignore: the repo had none, so every image build
shipped the 67GB Rust target/ dir as build context.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-15 01:29:03 -07:00
co-authored by Claude Opus 4.8
parent 34b8d2f903
commit 6823147334
3 changed files with 34 additions and 1 deletions
+23
View File
@@ -0,0 +1,23 @@
# Build artifacts and dependencies — never belong in an image build context.
# Without this, the 67GB Rust target/ dir is sent as context on every build.
# Rust
target/
**/target/
# Node — reinstalled (npm ci) / regenerated (next build) inside the image
node_modules/
**/node_modules/
.next/
**/.next/
# VCS & editor
.git/
.gitignore
.vscode/
.idea/
# Local artifacts
*.tar
*.log
*.bak
+7 -1
View File
@@ -1,3 +1,4 @@
import { connection } from "next/server";
import type { Metadata } from "next"; import type { Metadata } from "next";
import localFont from "next/font/local"; import localFont from "next/font/local";
import { NuqsAdapter } from "nuqs/adapters/next/app"; import { NuqsAdapter } from "nuqs/adapters/next/app";
@@ -6,8 +7,13 @@ import { RegisterServiceWorker } from "@/components/shell/RegisterServiceWorker"
import { authMode, clerkPublishableKey } from "@/lib/auth/mode"; import { authMode, clerkPublishableKey } from "@/lib/auth/mode";
/** Wraps the tree in Clerk's provider only when the deployment uses /** Wraps the tree in Clerk's provider only when the deployment uses
* Clerk; the air-gapped/local build never loads the SDK. */ * Clerk; the air-gapped/local build never loads the SDK. The AUTH_MODE
* switch is a runtime env var (same image, per-deployment toggle), so we
* call connection() to opt this out of build-time prerendering — otherwise
* next build bakes in the build host's mode and the toggle never flips
* (see docs/clerk.md). */
async function IdentityProvider({ children }: { children: React.ReactNode }) { async function IdentityProvider({ children }: { children: React.ReactNode }) {
await connection();
if (authMode() !== "clerk") { if (authMode() !== "clerk") {
return <>{children}</>; return <>{children}</>;
} }
+4
View File
@@ -1,8 +1,12 @@
import { connection } from "next/server";
import { AuthShell } from "@/components/auth/AuthShell"; import { AuthShell } from "@/components/auth/AuthShell";
import { LoginForm } from "@/components/auth/LoginForm"; import { LoginForm } from "@/components/auth/LoginForm";
import { authMode } from "@/lib/auth/mode"; import { authMode } from "@/lib/auth/mode";
export default async function LoginPage() { export default async function LoginPage() {
// Read AUTH_MODE at request time, not build time (see IdentityProvider).
await connection();
if (authMode() === "clerk") { if (authMode() === "clerk") {
const { SignIn } = await import("@clerk/nextjs"); const { SignIn } = await import("@clerk/nextjs");
return ( return (