From caaad8adf4666936f1ebd91be4270e9b7c7a4a8e Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sat, 25 Apr 2026 22:25:31 +0200 Subject: [PATCH] fix(web): SSR calls use cluster-internal backend URL to bypass nginx timeout All serverFetch calls were going to https://api.marktvogt.de (public gateway), creating a second nginx hop for every SSR operation. Slow LLM calls (merge-plan, research-plan) hit the 60s proxy_read_timeout. - Add PRIVATE_API_BASE_URL=http://marktvogt-backend to web Helm config - serverFetch now builds SERVER_API_BASE from PRIVATE_API_BASE_URL at runtime (falls back to PUBLIC_API_BASE_URL when not set) - apiFetch accepts optional baseURL param; client-side calls unchanged --- web/deploy/helm/values.yaml | 2 ++ web/src/lib/api/client.server.ts | 7 +++++++ web/src/lib/api/client.ts | 7 ++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/deploy/helm/values.yaml b/web/deploy/helm/values.yaml index 24a1d15..9c6da43 100644 --- a/web/deploy/helm/values.yaml +++ b/web/deploy/helm/values.yaml @@ -64,6 +64,8 @@ config: HOST: "0.0.0.0" # Cloudflare Turnstile — read at runtime via $env/dynamic/public PUBLIC_TURNSTILE_SITE_KEY: "0x4AAAAAACjLCV-78Ql1oTPz" + # Cluster-internal backend URL — SSR calls bypass the public gateway entirely + PRIVATE_API_BASE_URL: "http://marktvogt-backend" nodeSelector: {} tolerations: [] diff --git a/web/src/lib/api/client.server.ts b/web/src/lib/api/client.server.ts index 539e9f7..e399f7b 100644 --- a/web/src/lib/api/client.server.ts +++ b/web/src/lib/api/client.server.ts @@ -1,8 +1,14 @@ import type { Cookies } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; +import { PUBLIC_API_BASE_URL } from '$env/static/public'; import { apiFetch } from './client.js'; import type { ApiResponse, AuthData } from './types.js'; import { setAuthCookies } from '$lib/auth/cookies.js'; +// Cluster-internal URL bypasses the public gateway (no nginx hop, no 60s timeout). +// Falls back to PUBLIC_API_BASE_URL if PRIVATE_API_BASE_URL is not set. +const SERVER_API_BASE = `${env.PRIVATE_API_BASE_URL ?? PUBLIC_API_BASE_URL}/api/v1`; + export async function serverFetch( path: string, cookies: Cookies, @@ -17,6 +23,7 @@ export async function serverFetch( return apiFetch(path, { ...init, + baseURL: SERVER_API_BASE, headers: { ...headers, ...init?.headers diff --git a/web/src/lib/api/client.ts b/web/src/lib/api/client.ts index 8eb097b..deaa290 100644 --- a/web/src/lib/api/client.ts +++ b/web/src/lib/api/client.ts @@ -16,12 +16,13 @@ export class ApiClientError extends Error { export async function apiFetch( path: string, - init?: RequestInit & { fetch?: typeof globalThis.fetch } + init?: RequestInit & { fetch?: typeof globalThis.fetch; baseURL?: string } ): Promise> { const fetchFn = init?.fetch ?? globalThis.fetch; - const { fetch: _, ...restInit } = init ?? {}; + const { fetch: _, baseURL, ...restInit } = init ?? {}; + const base = baseURL ?? API_BASE; - const res = await fetchFn(`${API_BASE}${path}`, { + const res = await fetchFn(`${base}${path}`, { headers: { 'Content-Type': 'application/json', ...restInit.headers