fix(frontend): drop localStorage HF_BACKEND_BASE_URL; env-only
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) <noreply@anthropic.com>
This commit is contained in:
19
src/App.tsx
19
src/App.tsx
@@ -23,17 +23,10 @@ import OidcCallbackPage from '@/pages/OidcCallbackPage'
|
|||||||
import OidcSettingsPage from '@/pages/OidcSettingsPage'
|
import OidcSettingsPage from '@/pages/OidcSettingsPage'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
// Backend URL is baked in at build time via VITE_HF_BACKEND_BASE_URL (the
|
// Backend URL is baked in at build time via VITE_HF_BACKEND_BASE_URL
|
||||||
// docker-compose hf-frontend service passes it as a build ARG). Falling
|
// (docker build --build-arg). Empty string → same-origin call (only
|
||||||
// back to a same-origin call only makes sense in dev with the Vite proxy.
|
// works in dev with the Vite proxy).
|
||||||
// localStorage override is kept as an escape hatch for one-off pointing
|
const API_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL || ''
|
||||||
// (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 || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppState = 'checking' | 'no-admin' | 'ready'
|
type AppState = 'checking' | 'no-admin' | 'ready'
|
||||||
|
|
||||||
@@ -48,7 +41,7 @@ export default function App() {
|
|||||||
|
|
||||||
const checkInitialized = async () => {
|
const checkInitialized = async () => {
|
||||||
try {
|
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 || {}
|
const cfg = res.data || {}
|
||||||
if (cfg.initialized === true) {
|
if (cfg.initialized === true) {
|
||||||
setAppState('ready')
|
setAppState('ready')
|
||||||
@@ -57,7 +50,7 @@ export default function App() {
|
|||||||
setAppState('no-admin')
|
setAppState('no-admin')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : String(err)
|
const msg = err instanceof Error ? err.message : String(err)
|
||||||
setErrorMessage(`Backend unreachable at ${getApiBase() || '<same origin>'} — ${msg}`)
|
setErrorMessage(`Backend unreachable at ${API_BASE || '<same origin>'} — ${msg}`)
|
||||||
setAppState('no-admin')
|
setAppState('no-admin')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,14 +45,14 @@ async function load(): Promise<AuthConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Absolute backend URL for full-page OIDC redirects. */
|
/** 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 {
|
export function oidcLoginHref(cfg: AuthConfig): string {
|
||||||
const base = localStorage.getItem('HF_BACKEND_BASE_URL') ?? ''
|
return `${BACKEND_BASE}${cfg.oidcLoginUrl}`
|
||||||
return `${base}${cfg.oidcLoginUrl}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function oidcLinkHref(): string {
|
export function oidcLinkHref(): string {
|
||||||
const base = localStorage.getItem('HF_BACKEND_BASE_URL') ?? ''
|
return `${BACKEND_BASE}/auth/oidc/link`
|
||||||
return `${base}/auth/oidc/link`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthConfig() {
|
export function useAuthConfig() {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
const getApiBase = (): string | undefined => {
|
// Backend URL is the build-time VITE_HF_BACKEND_BASE_URL baked into the
|
||||||
return localStorage.getItem('HF_BACKEND_BASE_URL') ?? undefined
|
// 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({
|
const api = axios.create({
|
||||||
baseURL: getApiBase(),
|
baseURL: API_BASE,
|
||||||
})
|
})
|
||||||
|
|
||||||
api.interceptors.request.use((config) => {
|
api.interceptors.request.use((config) => {
|
||||||
config.baseURL = getApiBase()
|
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers.Authorization = `Bearer ${token}`
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
import { vi } from 'vitest'
|
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
|
// Mock localStorage
|
||||||
const localStorageMock = {
|
const localStorageMock = {
|
||||||
getItem: vi.fn((key: string) => {
|
getItem: vi.fn((key: string) => {
|
||||||
if (key === 'token') return 'mock-token'
|
if (key === 'token') return 'mock-token'
|
||||||
if (key === 'HF_BACKEND_BASE_URL') return 'http://localhost:8000'
|
|
||||||
return null
|
return null
|
||||||
}),
|
}),
|
||||||
setItem: vi.fn(),
|
setItem: vi.fn(),
|
||||||
|
|||||||
Reference in New Issue
Block a user