fix: Fixed api url on web
Chart Release / chart (push) Successful in 10s
Server Deploy / deploy (push) Successful in 7m37s

This commit is contained in:
2026-08-25 13:53:59 +00:00
parent 3e4ccc9720
commit 7a0a1953f6
14 changed files with 109 additions and 83 deletions
+3 -4
View File
@@ -30,10 +30,9 @@ 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
# No control-plane address here on purpose: this app proxies nothing. /api,
# /auth, /install, /update and /public are routed to the server by the reverse
# proxy in front of both.
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
+17 -3
View File
@@ -12,8 +12,21 @@ type FetchResult =
| { kind: "not-found" }
| { kind: "unavailable" };
async function fetchSnapshot(host: string, forwardedFor: string, pageId: string): Promise<FetchResult> {
const base = process.env.API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8080";
/*
* The control plane this call is made to is the visitor's own host.
*
* This app proxies nothing and holds no address for the server (see
* next.config.ts): /public is routed to it by the reverse proxy, exactly as
* /api and /auth are for the browser. So the SSR fetch goes back through that
* proxy at <slug>.vantage.<tld>, which is a per-tenant address by
* construction — and X-Forwarded-Host below is still what selects the tenant,
* because the hop from this process cannot set Host.
*/
function apiBase(proto: string, host: string): string {
return `${proto}://${host}`;
}
async function fetchSnapshot(base: string, host: string, forwardedFor: string, pageId: string): Promise<FetchResult> {
// The instance is resolved server-side from the visitor's host, so it has
// to be forwarded explicitly — this is a server-to-server call and its own
@@ -95,7 +108,8 @@ export default async function PublicStatusPage({
.filter((v): v is string => !!v)
.join(", ");
const result = await fetchSnapshot(host, forwardedFor, pageId);
const proto = h.get("x-forwarded-proto")?.split(",")[0].trim() || "https";
const result = await fetchSnapshot(apiBase(proto, host), host, forwardedFor, pageId);
if (result.kind === "not-found") notFound();
return (
+16 -40
View File
@@ -1,11 +1,21 @@
import type { NextConfig } from "next";
// 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";
/*
* This app proxies nothing.
*
* /api, /auth, /install, /update and /public are the Go server's, and routing
* them there is the reverse proxy's job — the same proxy that already
* terminates TLS in front of this process. Next used to rewrite them itself
* from an API_URL naming the control plane, which meant every deployment had
* two possible request paths for the same URL and one environment variable
* that silently broke a whole prefix when it named the wrong host: pointed at
* the marketing site, /public/status/... 404'd as a page that does not exist,
* indistinguishable from a status page that does not exist.
*
* Browser calls are same-origin and relative (see lib/api.ts), and the public
* status page's server-side fetch is made against the visitor's own host, so
* nothing in this app needs to know the control plane's address any more.
*/
const nextConfig: NextConfig = {
output: "standalone",
async redirects() {
@@ -22,40 +32,6 @@ const nextConfig: NextConfig = {
},
];
},
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${apiUrl}/api/:path*`,
},
{
source: "/auth/:path*",
destination: `${apiUrl}/auth/:path*`,
},
{
source: "/install",
destination: `${apiUrl}/install`,
},
{
source: "/install.ps1",
destination: `${apiUrl}/install.ps1`,
},
{
source: "/update",
destination: `${apiUrl}/update`,
},
{
source: "/update.ps1",
destination: `${apiUrl}/update.ps1`,
},
{
// The public status page refreshes itself in the browser, so
// the public prefix has to be proxied the same way /api is.
source: "/public/:path*",
destination: `${apiUrl}/public/:path*`,
},
];
},
};
export default nextConfig;