diff --git a/Dockerfile b/Dockerfile index a2f5f70..6efd963 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN apk add --no-cache openssl # them via build args (Coolify: mark them as build variables) or the browser # ClerkProvider / public URLs will be missing. ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY +ARG NEXT_PUBLIC_CLERK_FAPI_HOST ARG NEXT_PUBLIC_CLERK_SIGN_IN_URL ARG NEXT_PUBLIC_CLERK_SIGN_UP_URL ARG NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL diff --git a/docs/coolify.md b/docs/coolify.md index 612989a..a9f1465 100644 --- a/docs/coolify.md +++ b/docs/coolify.md @@ -108,6 +108,9 @@ DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@spomeniqr-db:5432/monuments # Clerk NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... +# Custom Clerk frontend API domain (if configured in Clerk dashboard) — the CSP +# allowlist needs it. Build variable. Omit if not using a custom domain. +NEXT_PUBLIC_CLERK_FAPI_HOST=clerk.testbed.mk NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/ @@ -148,7 +151,10 @@ NODE_ENV=production node -e "import('bcryptjs').then(b => b.default.hash('YOUR_PASSWORD', 12).then(console.log))" ``` - Mark **every `NEXT_PUBLIC_*` variable as a build variable** (checkbox) so it is - inlined into the client bundle at `next build`. Server-only vars + inlined into the client bundle at `next build` and visible to `next.config.ts`. + `NEXT_PUBLIC_CLERK_FAPI_HOST` in particular must be a build variable — the CSP + is generated at build time and will otherwise block Clerk JS if you use a + custom Clerk frontend API domain. Server-only vars (`CLERK_SECRET_KEY`, `DATABASE_URL`, `ADMIN_SESSION_SECRET`, `SUPER_ADMIN_PASSWORD_HASH`, `S3_*`) stay as normal runtime variables. - The `super` admin row is provisioned automatically on container start by @@ -344,6 +350,7 @@ npx prisma db push |----------|----------|-------------| | `DATABASE_URL` | Yes | PostgreSQL connection string (Coolify internal) | | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Yes | Clerk publishable key (`pk_live_...`) | +| `NEXT_PUBLIC_CLERK_FAPI_HOST` | No* | Custom Clerk frontend API domain (`clerk.testbed.mk`) — required when using a custom domain; build variable | | `CLERK_SECRET_KEY` | Yes | Clerk secret key (`sk_live_...`) | | `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | Yes | `/sign-in` | | `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | Yes | `/sign-up` | diff --git a/next.config.ts b/next.config.ts index dbc420a..51b9fd6 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,9 +4,12 @@ const s3Host = process.env.S3_ENDPOINT ? new URL(process.env.S3_ENDPOINT).hostname : ""; -// Derive the Clerk frontend API host from the publishable key so the -// CSP allowlist always matches the active environment. Clerk supports -// two publishable key formats: +// Derive the Clerk frontend API host so the CSP allowlist always matches the +// active environment. When a custom Clerk frontend API domain is configured in +// the Clerk dashboard (e.g. clerk.testbed.mk), it is resolved at runtime by +// clerk-js and can NOT be derived from the publishable key — so it must be +// provided explicitly via NEXT_PUBLIC_CLERK_FAPI_HOST. Otherwise Clerk +// supports two publishable key formats: // // 1) "Encoded" form (older): pk_test_$ // The base64 portion decodes to ".clerk.accounts.dev" (test) @@ -17,6 +20,16 @@ const s3Host = process.env.S3_ENDPOINT // The slug may itself contain hyphens and digits, so only the // final dash-group is captured as the suffix. function clerkFrontendApiHost(): string | null { + const custom = process.env.NEXT_PUBLIC_CLERK_FAPI_HOST?.trim(); + if (custom) { + try { + const host = new URL(custom.includes("://") ? custom : `https://${custom}`).hostname; + if (host) return host; + } catch { + // fall through to derivation from the publishable key + } + } + const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY; if (!key) return null;