From e263bd93f864d983e56da572e2d21dc90ad2595f Mon Sep 17 00:00:00 2001 From: Zaid Marzguioui Date: Tue, 31 Mar 2026 22:43:44 +0200 Subject: [PATCH 1/4] fix(docker): restore runtime NEXT_PUBLIC vars, add DEFAULT_LOCALE build arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Restore NEXT_PUBLIC_SUPABASE_URL + NEXT_PUBLIC_SUPABASE_PUBLIC_KEY at runtime (server code reads from process.env — needs these for SSR) - Use external URL (API_EXTERNAL_URL) for both build and runtime - Add NEXT_PUBLIC_DEFAULT_LOCALE=de as Dockerfile build arg so next-intl compiles with the correct default locale (was falling back to 'en') - CACHE_BUST=3 to force full rebuild --- Dockerfile | 4 +++- docker-compose.yml | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index e20016dc0..0e87096d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app # --- Install + Build in one stage --- FROM base AS builder -ARG CACHE_BUST=2 +ARG CACHE_BUST=3 COPY . . RUN pnpm install --no-frozen-lockfile ENV NEXT_TELEMETRY_DISABLED=1 @@ -14,9 +14,11 @@ ENV NEXT_TELEMETRY_DISABLED=1 ARG NEXT_PUBLIC_SITE_URL=https://myeasycms.de ARG NEXT_PUBLIC_SUPABASE_URL=http://localhost:8000 ARG NEXT_PUBLIC_SUPABASE_PUBLIC_KEY +ARG NEXT_PUBLIC_DEFAULT_LOCALE=de ENV NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL} ENV NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=${NEXT_PUBLIC_SUPABASE_PUBLIC_KEY} +ENV NEXT_PUBLIC_DEFAULT_LOCALE=${NEXT_PUBLIC_DEFAULT_LOCALE} RUN pnpm --filter web build # --- Run --- diff --git a/docker-compose.yml b/docker-compose.yml index 8d5cebab6..acd7a63ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -321,13 +321,14 @@ services: environment: NODE_ENV: production NEXT_PUBLIC_SITE_URL: ${SITE_URL:-http://localhost:3000} - # NEXT_PUBLIC_ vars are baked at build time — runtime values only apply - # to middleware/API routes. Don't override with Docker-internal URLs. + # Must match the build-time value — server code reads from process.env + NEXT_PUBLIC_SUPABASE_URL: ${API_EXTERNAL_URL:-http://localhost:8000} + NEXT_PUBLIC_SUPABASE_PUBLIC_KEY: ${SUPABASE_ANON_KEY} + NEXT_PUBLIC_DEFAULT_LOCALE: de SUPABASE_SECRET_KEY: ${SUPABASE_SERVICE_ROLE_KEY} SUPABASE_DB_WEBHOOK_SECRET: ${DB_WEBHOOK_SECRET:-webhooksecret} EMAIL_SENDER: ${EMAIL_SENDER:-noreply@myeasycms.de} NEXT_PUBLIC_PRODUCT_NAME: MyEasyCMS - NEXT_PUBLIC_DEFAULT_LOCALE: de NEXT_PUBLIC_ENABLE_THEME_TOGGLE: "true" NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS: "true" NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION: "true" From 4450776826dcc2ac83164e1dbdf84a48e561b0ce Mon Sep 17 00:00:00 2001 From: Zaid Marzguioui Date: Tue, 31 Mar 2026 22:47:55 +0200 Subject: [PATCH 2/4] fix(i18n): add next-intl middleware for locale routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The middleware was deleted in the Next.js 16 upgrade but is still required by next-intl to handle locale detection and URL rewriting. Without it, /auth/sign-in can't resolve to [locale=de]/auth/sign-in → 404. Uses createMiddleware from next-intl/middleware with the shared routing config. --- Dockerfile | 2 +- apps/web/middleware.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 apps/web/middleware.ts diff --git a/Dockerfile b/Dockerfile index 0e87096d2..7eb92fcc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app # --- Install + Build in one stage --- FROM base AS builder -ARG CACHE_BUST=3 +ARG CACHE_BUST=4 COPY . . RUN pnpm install --no-frozen-lockfile ENV NEXT_TELEMETRY_DISABLED=1 diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts new file mode 100644 index 000000000..1e1fff1de --- /dev/null +++ b/apps/web/middleware.ts @@ -0,0 +1,15 @@ +import createMiddleware from 'next-intl/middleware'; + +import { routing } from '@kit/i18n/routing'; + +export default createMiddleware(routing); + +export const config = { + matcher: [ + // Match all pathnames except: + // - API routes (/api/...) + // - Next.js internals (/_next/...) + // - Static files with extensions + '/((?!api|_next|.*\\..*).*)', + ], +}; From d27fab750589b102aa21ada66b482148aeb6519e Mon Sep 17 00:00:00 2001 From: Zaid Marzguioui Date: Tue, 31 Mar 2026 22:49:35 +0200 Subject: [PATCH 3/4] fix: remove conflicting middleware.ts, proxy.ts already handles locale routing Next.js 16 uses proxy.ts instead of middleware.ts. The existing proxy.ts already includes next-intl locale routing + auth guards. CACHE_BUST=5. --- Dockerfile | 2 +- apps/web/middleware.ts | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 apps/web/middleware.ts diff --git a/Dockerfile b/Dockerfile index 7eb92fcc9..a8997ef83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app # --- Install + Build in one stage --- FROM base AS builder -ARG CACHE_BUST=4 +ARG CACHE_BUST=5 COPY . . RUN pnpm install --no-frozen-lockfile ENV NEXT_TELEMETRY_DISABLED=1 diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts deleted file mode 100644 index 1e1fff1de..000000000 --- a/apps/web/middleware.ts +++ /dev/null @@ -1,15 +0,0 @@ -import createMiddleware from 'next-intl/middleware'; - -import { routing } from '@kit/i18n/routing'; - -export default createMiddleware(routing); - -export const config = { - matcher: [ - // Match all pathnames except: - // - API routes (/api/...) - // - Next.js internals (/_next/...) - // - Static files with extensions - '/((?!api|_next|.*\\..*).*)', - ], -}; From c9f810169824c16d335149a9b499b2818326f40a Mon Sep 17 00:00:00 2001 From: Zaid Marzguioui Date: Tue, 31 Mar 2026 22:55:15 +0200 Subject: [PATCH 4/4] fix(docker): add SUPABASE_INTERNAL_URL for server-side Supabase access Server-side code (proxy.ts, SSR, API routes) now uses SUPABASE_INTERNAL_URL (http://supabase-kong:8000) instead of the external domain. This avoids hairpin NAT / DNS resolution issues where Docker containers can't reach their own external domain through the reverse proxy. Browser-side JS still uses the external URL (baked at build time). --- Dockerfile | 2 +- docker-compose.yml | 4 +++- packages/supabase/src/get-supabase-client-keys.ts | 12 +++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index a8997ef83..ea505715b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app # --- Install + Build in one stage --- FROM base AS builder -ARG CACHE_BUST=5 +ARG CACHE_BUST=6 COPY . . RUN pnpm install --no-frozen-lockfile ENV NEXT_TELEMETRY_DISABLED=1 diff --git a/docker-compose.yml b/docker-compose.yml index acd7a63ca..d22bbcf07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -321,10 +321,12 @@ services: environment: NODE_ENV: production NEXT_PUBLIC_SITE_URL: ${SITE_URL:-http://localhost:3000} - # Must match the build-time value — server code reads from process.env + # Browser-side: external domain (baked at build time, re-stated here for SSR) NEXT_PUBLIC_SUPABASE_URL: ${API_EXTERNAL_URL:-http://localhost:8000} NEXT_PUBLIC_SUPABASE_PUBLIC_KEY: ${SUPABASE_ANON_KEY} NEXT_PUBLIC_DEFAULT_LOCALE: de + # Server-side: Docker-internal URL (avoids hairpin NAT / DNS issues) + SUPABASE_INTERNAL_URL: http://supabase-kong:8000 SUPABASE_SECRET_KEY: ${SUPABASE_SERVICE_ROLE_KEY} SUPABASE_DB_WEBHOOK_SECRET: ${DB_WEBHOOK_SECRET:-webhooksecret} EMAIL_SENDER: ${EMAIL_SENDER:-noreply@myeasycms.de} diff --git a/packages/supabase/src/get-supabase-client-keys.ts b/packages/supabase/src/get-supabase-client-keys.ts index 1f3a3eee9..3afab122e 100644 --- a/packages/supabase/src/get-supabase-client-keys.ts +++ b/packages/supabase/src/get-supabase-client-keys.ts @@ -2,8 +2,18 @@ import * as z from 'zod'; /** * Returns and validates the Supabase client keys from the environment. + * + * On the server, prefers SUPABASE_INTERNAL_URL (Docker-internal) + * over NEXT_PUBLIC_SUPABASE_URL (external domain) to avoid + * hairpin NAT / DNS issues in containerized deployments. */ export function getSupabaseClientKeys() { + const isServer = typeof window === 'undefined'; + + const url = isServer + ? (process.env.SUPABASE_INTERNAL_URL || process.env.NEXT_PUBLIC_SUPABASE_URL) + : process.env.NEXT_PUBLIC_SUPABASE_URL; + return z .object({ url: z.string({ @@ -14,7 +24,7 @@ export function getSupabaseClientKeys() { }), }) .parse({ - url: process.env.NEXT_PUBLIC_SUPABASE_URL, + url, publicKey: process.env.NEXT_PUBLIC_SUPABASE_PUBLIC_KEY, }); }