diff --git a/docker-compose.yml b/docker-compose.yml index a6adf34..a53a2c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: NEXT_TELEMETRY_DISABLED: "1" NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-https://mohfarawati.de} NEXT_PUBLIC_ADMIN_URL: ${NEXT_PUBLIC_ADMIN_URL:-https://root.mohfarawati.de} + SITE_RUNTIME_ORIGIN: ${SITE_RUNTIME_ORIGIN:-http://127.0.0.1:3000} DATABASE_URL: postgresql://postgres:postgres@db:5432/moh_sass?schema=public ADMIN_PASSWORD: ${ADMIN_PASSWORD:-change-me} ADMIN_AUTH_SECRET: ${ADMIN_AUTH_SECRET:-change-me-long-secret} diff --git a/middleware.ts b/middleware.ts index 9af1e6b..444b0ee 100644 --- a/middleware.ts +++ b/middleware.ts @@ -28,6 +28,20 @@ type SiteRuntimeState = { maintenanceEnabled: boolean; }; +function getSiteRuntimeStateOrigin(request: NextRequest): string { + const configuredOrigin = process.env.SITE_RUNTIME_ORIGIN?.trim(); + + if (configuredOrigin) { + return configuredOrigin; + } + + if (process.env.NODE_ENV === "production") { + return "http://127.0.0.1:3000"; + } + + return request.nextUrl.origin; +} + function getPathLocale(pathname: string, fallbackLocale: (typeof appLocales)[number]) { const locale = pathname.split("/")[1]; @@ -40,8 +54,8 @@ function isComingSoonPath(pathname: string) { async function getSiteRuntimeState(request: NextRequest): Promise { try { - const runtimeStateUrl = new URL("/api/site/default-locale", request.url); - const response = await fetch(new URL("/api/site/default-locale", request.url), { + const runtimeStateUrl = new URL("/api/site/default-locale", getSiteRuntimeStateOrigin(request)); + const response = await fetch(runtimeStateUrl, { headers: { "x-middleware-request": "1", }, @@ -74,8 +88,8 @@ async function getSiteRuntimeState(request: NextRequest): Promise { beforeEach(() => { vi.resetModules(); + process.env.NODE_ENV = "production"; + delete process.env.SITE_RUNTIME_ORIGIN; createMiddlewareMock.mockReset(); intlHandlerMock.mockReset(); intlHandlerMock.mockReturnValue(NextResponse.next()); @@ -66,6 +68,10 @@ describe("middleware locale runtime config", () => { await middleware(request as never); + expect(fetch).toHaveBeenCalledWith( + new URL("http://127.0.0.1:3000/api/site/default-locale"), + expect.any(Object), + ); expect(createMiddlewareMock).toHaveBeenCalledTimes(1); expect(createMiddlewareMock).toHaveBeenCalledWith( expect.objectContaining({ @@ -115,4 +121,29 @@ describe("middleware locale runtime config", () => { expect(response.headers.get("location")).toBe("https://example.com/coming-soon"); expect(intlHandlerMock).not.toHaveBeenCalled(); }); + + it("prefers the configured internal runtime origin when provided", async () => { + process.env.SITE_RUNTIME_ORIGIN = "http://app:3000"; + + vi.stubGlobal( + "fetch", + vi.fn(async () => ({ + ok: true, + json: async () => ({ + defaultLocale: "en", + maintenanceEnabled: false, + }), + })), + ); + + const { default: middleware } = await import("../middleware"); + const request = createMockRequest("https://example.com/"); + + await middleware(request as never); + + expect(fetch).toHaveBeenCalledWith( + new URL("http://app:3000/api/site/default-locale"), + expect.any(Object), + ); + }); });