From f587e1e4c7df67e86c00c11b2a247007d0edbd82 Mon Sep 17 00:00:00 2001 From: hzhang Date: Sun, 24 May 2026 19:17:05 +0100 Subject: [PATCH] fix(frontend): drop localStorage HF_BACKEND_BASE_URL; env-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit api.ts + useAuthConfig kept the old wizard-era localStorage path for the backend URL, which is why fresh browsers saw blank pages on MonitorPage and any other api-using page after the v0.4.0 wizard removal — without the wizard pre-seeding localStorage, getApiBase() returned undefined and axios called same-origin (frontend nginx, 404). App.tsx getApiBase() partially worked because I had only refactored that one path; api.ts (used by everything else) still had its own duplicate getApiBase that I missed. This commit removes the localStorage path entirely from all 3 read sites (api.ts / App.tsx / useAuthConfig.ts) — single source of truth is now the build-time env baked in by the Dockerfile ARG. - src/services/api.ts: const API_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL - src/App.tsx: const API_BASE = ... (replaces getApiBase()) - src/hooks/useAuthConfig.ts: const BACKEND_BASE = ... - src/test/setup.ts: stub import.meta.env instead of localStorage key Co-Authored-By: Claude Opus 4.7 (1M context) --- src/App.tsx | 19 ++++++------------- src/hooks/useAuthConfig.ts | 8 ++++---- src/services/api.ts | 10 +++++----- src/test/setup.ts | 8 +++++++- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 70e4717..964d63c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,17 +23,10 @@ import OidcCallbackPage from '@/pages/OidcCallbackPage' import OidcSettingsPage from '@/pages/OidcSettingsPage' import axios from 'axios' -// Backend URL is baked in at build time via VITE_HF_BACKEND_BASE_URL (the -// docker-compose hf-frontend service passes it as a build ARG). Falling -// back to a same-origin call only makes sense in dev with the Vite proxy. -// localStorage override is kept as an escape hatch for one-off pointing -// (e.g. dev pointing the prod build at a local backend). -const getApiBase = (): string => { - const ls = localStorage.getItem('HF_BACKEND_BASE_URL') - if (ls) return ls - const baked = import.meta.env.VITE_HF_BACKEND_BASE_URL - return baked || '' -} +// Backend URL is baked in at build time via VITE_HF_BACKEND_BASE_URL +// (docker build --build-arg). Empty string → same-origin call (only +// works in dev with the Vite proxy). +const API_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL || '' type AppState = 'checking' | 'no-admin' | 'ready' @@ -48,7 +41,7 @@ export default function App() { const checkInitialized = async () => { try { - const res = await axios.get(`${getApiBase()}/config/status`, { timeout: 5000 }) + const res = await axios.get(`${API_BASE}/config/status`, { timeout: 5000 }) const cfg = res.data || {} if (cfg.initialized === true) { setAppState('ready') @@ -57,7 +50,7 @@ export default function App() { setAppState('no-admin') } catch (err) { const msg = err instanceof Error ? err.message : String(err) - setErrorMessage(`Backend unreachable at ${getApiBase() || ''} — ${msg}`) + setErrorMessage(`Backend unreachable at ${API_BASE || ''} — ${msg}`) setAppState('no-admin') } } diff --git a/src/hooks/useAuthConfig.ts b/src/hooks/useAuthConfig.ts index af0f0d2..da23940 100644 --- a/src/hooks/useAuthConfig.ts +++ b/src/hooks/useAuthConfig.ts @@ -45,14 +45,14 @@ async function load(): Promise { } /** Absolute backend URL for full-page OIDC redirects. */ +const BACKEND_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL || '' + export function oidcLoginHref(cfg: AuthConfig): string { - const base = localStorage.getItem('HF_BACKEND_BASE_URL') ?? '' - return `${base}${cfg.oidcLoginUrl}` + return `${BACKEND_BASE}${cfg.oidcLoginUrl}` } export function oidcLinkHref(): string { - const base = localStorage.getItem('HF_BACKEND_BASE_URL') ?? '' - return `${base}/auth/oidc/link` + return `${BACKEND_BASE}/auth/oidc/link` } export function useAuthConfig() { diff --git a/src/services/api.ts b/src/services/api.ts index 477bd30..3e4a57f 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,15 +1,15 @@ import axios from 'axios' -const getApiBase = (): string | undefined => { - return localStorage.getItem('HF_BACKEND_BASE_URL') ?? undefined -} +// Backend URL is the build-time VITE_HF_BACKEND_BASE_URL baked into the +// bundle by docker build --build-arg. Empty → axios uses same-origin +// (only works in dev with the Vite proxy). +const API_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL || undefined const api = axios.create({ - baseURL: getApiBase(), + baseURL: API_BASE, }) api.interceptors.request.use((config) => { - config.baseURL = getApiBase() const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` diff --git a/src/test/setup.ts b/src/test/setup.ts index ce144cc..0ad9280 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -1,11 +1,17 @@ import '@testing-library/jest-dom' import { vi } from 'vitest' +// VITE_HF_BACKEND_BASE_URL is the build-time backend URL; in tests we +// stub it via import.meta.env (api.ts + useAuthConfig + App read it). +;(import.meta as any).env = { + ...(import.meta as any).env, + VITE_HF_BACKEND_BASE_URL: 'http://localhost:8000', +} + // Mock localStorage const localStorageMock = { getItem: vi.fn((key: string) => { if (key === 'token') return 'mock-token' - if (key === 'HF_BACKEND_BASE_URL') return 'http://localhost:8000' return null }), setItem: vi.fn(),