From 5be9ddb2e56f1a818a2a0884015eb7c9729a299e Mon Sep 17 00:00:00 2001 From: mrhid6 Date: Tue, 28 Jul 2026 16:18:50 +0100 Subject: [PATCH] fix: Fixed baked api url --- .gitea/workflows/server-deploy.yml | 1 - claude.md | 4 ++-- deploy/docker-compose.yml | 2 ++ docsite/docs/operations/ci-cd.md | 3 +-- docsite/docs/reference/environment-variables.md | 1 - web/Dockerfile | 8 +++++--- web/next.config.ts | 7 ++++++- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/server-deploy.yml b/.gitea/workflows/server-deploy.yml index e1fefcf..350291b 100644 --- a/.gitea/workflows/server-deploy.yml +++ b/.gitea/workflows/server-deploy.yml @@ -108,7 +108,6 @@ jobs: run: | IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/web:latest" docker build \ - --build-arg NEXT_PUBLIC_API_URL="${{ vars.API_URL }}" \ --build-arg NEXT_PUBLIC_HQ_URL="${{ vars.HQ_URL }}" \ -t "$IMAGE" \ -f web/Dockerfile web/ diff --git a/claude.md b/claude.md index a32e686..de66592 100644 --- a/claude.md +++ b/claude.md @@ -649,7 +649,7 @@ cd /opt/vantage && docker compose -f docker-compose.yml -f docker-compose.site.y `shared/` fans out to all three Go images because each of their Dockerfiles copies `shared/` from a root context — **if a fourth service ever imports `shared/`, add it to that list or it will ship stale**. A change to the workflow file rebuilds everything, since a build arg is baked into the image. So does anything that leaves no trustworthy base commit: a manual `workflow_dispatch`, a new branch, or a force-push whose old head is gone. -The gap this leaves: **changing a repo variable pushes no commit, so nothing rebuilds.** After editing `API_URL`, `ADMIN_API_URL`, `HQ_URL` or `ADMIN_ENV`, run the workflow manually — that is what `workflow_dispatch` is there for. Base images also stop being refreshed on a service nobody touches; a periodic manual run covers that. +The gap this leaves: **changing a repo variable pushes no commit, so nothing rebuilds.** After editing `ADMIN_API_URL`, `HQ_URL` or `ADMIN_ENV`, run the workflow manually — that is what `workflow_dispatch` is there for. Base images also stop being refreshed on a service nobody touches; a periodic manual run covers that. ### Tagging @@ -666,7 +666,7 @@ git push origin main # server + web deploy | `REGISTRY_USER` | Secret | Gitea username | | `REGISTRY_PASSWORD` | Secret | Gitea token, `write:packages` | | `DOCKER_HOST` | Variable | registry host used for image tags | -| `API_URL` | Variable | baked into the `web` image at build time | +| `API_URL` | **not** a CI variable | `web` reads it at **runtime**, from the container environment — `next.config.ts` is evaluated when `server.js` boots in standalone mode, and the rewrites it feeds are server-side, never browser-side. Default `http://localhost:8080`; compose sets `http://server:8080`. `NEXT_PUBLIC_API_URL` is still honoured as a fallback for existing deployments. | | `SITE_API_URL` | Variable | **browser-reachable** sitesvc URL, baked into the `site` image. Required — if empty, both forms report "not connected" and submit nowhere. Must also be in sitesvc's `SITE_ORIGIN`. | | `SITE_CONTACT_EMAIL` | Variable | optional; address shown when a form is misconfigured | | `ADMIN_API_URL` | Variable | **browser-reachable** admin URL, baked into **both** the `adminsite` and `site` images — `site/start` posts account signups straight to admin. Same footgun as `SITE_API_URL`: wrong here and every request fails at runtime with the not-connected panel. | diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 26e726f..264d345 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -60,6 +60,8 @@ services: restart: unless-stopped ports: - 3000:3000 + environment: + API_URL: ${API_URL:-http://server:8080} depends_on: - server volumes: diff --git a/docsite/docs/operations/ci-cd.md b/docsite/docs/operations/ci-cd.md index bb88e2d..21f7bbf 100644 --- a/docsite/docs/operations/ci-cd.md +++ b/docsite/docs/operations/ci-cd.md @@ -59,7 +59,7 @@ argument is baked into each image. ### The gap: repository variables :::danger Editing a repository variable pushes no commit, so nothing rebuilds -Values like `API_URL`, `ADMIN_API_URL`, `HQ_URL`, `ADMIN_ENV`, `PADDLE_ENV`, +Values like `ADMIN_API_URL`, `HQ_URL`, `ADMIN_ENV`, `PADDLE_ENV`, `PADDLE_CLIENT_TOKEN`, `DOCS_URL` and `DOCS_BASE_URL` are baked into images at build time. After editing one, run the workflow manually — that is what `workflow_dispatch` is for. @@ -80,7 +80,6 @@ newer base layers. A periodic manual run covers it. | `PADDLE_API_KEY` | Secret | Read by admin at runtime | | `PADDLE_WEBHOOK_SECRET` | Secret | Webhook signature verification | | `GITEA_HOST` / `DOCKER_HOST` | Variable | Hosts used in tags and URLs | -| `API_URL` | Variable | Baked into `web` | | `HQ_URL` | Variable | Baked into `web`; empty on self-hosted | | `SITE_API_URL` / `SITE_CONTACT_EMAIL` | Variable | Baked into `site` | | `ADMIN_API_URL` | Variable | Baked into `adminsite` **and** `site` | diff --git a/docsite/docs/reference/environment-variables.md b/docsite/docs/reference/environment-variables.md index b6390dd..9ac32a2 100644 --- a/docsite/docs/reference/environment-variables.md +++ b/docsite/docs/reference/environment-variables.md @@ -99,7 +99,6 @@ pushes no commit, nothing rebuilds on its own. See [CI/CD](../operations/ci-cd.m | Name | Baked into | | --- | --- | -| `API_URL` | `web` | | `HQ_URL` | `web` | | `SITE_API_URL`, `SITE_CONTACT_EMAIL` | `site` | | `ADMIN_API_URL` | `adminsite` **and** `site` | diff --git a/web/Dockerfile b/web/Dockerfile index 1fa8358..2afbfb4 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -14,9 +14,6 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . -ARG NEXT_PUBLIC_API_URL=http://localhost:8080 -ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL - # Empty default on purpose: a self-hosted install has no HQ portal, and the # "Managed in Vantage HQ" label falls back to plain text rather than linking # somewhere that does not serve them. @@ -33,6 +30,11 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 +# Control-plane URL the Next server proxies /api, /auth and /install to. Runtime, +# not build time: next.config.js is evaluated when server.js boots in standalone +# mode, so this is overridable per deployment without a rebuild. +ENV API_URL=http://localhost:8080 + RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs diff --git a/web/next.config.ts b/web/next.config.ts index ab79cc8..23cbd01 100644 --- a/web/next.config.ts +++ b/web/next.config.ts @@ -1,6 +1,11 @@ import type { NextConfig } from "next"; -const apiUrl = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8080"; +// Read at runtime, not baked in. The rewrites below run in the Next server +// process, never in the browser, so this never needed the NEXT_PUBLIC_ prefix +// that pins a value into the image at build time. NEXT_PUBLIC_API_URL is still +// honoured so an existing deployment passing it keeps working. +const apiUrl = + process.env.API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8080"; const nextConfig: NextConfig = { output: "standalone",