From 3d280396ae1c4e9878da84e5e57b20e1a3f9f39e Mon Sep 17 00:00:00 2001 From: Beda Schmid Date: Sun, 1 Mar 2026 14:08:48 -0300 Subject: [PATCH] Fix LAN API access and dev proxy routing --- README.md | 4 ++-- doc/operations-and-configuration.md | 5 ++--- docker-compose.yml | 1 + frontend/src/lib/api.test.ts | 4 ++-- frontend/src/lib/api.ts | 8 ++------ frontend/vite.config.ts | 6 ++++++ 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2611b66..4a82c96 100644 --- a/README.md +++ b/README.md @@ -116,9 +116,9 @@ cd frontend && npm run preview Main runtime variables are defined in `docker-compose.yml`: - API and worker: `DATABASE_URL`, `REDIS_URL`, `REDIS_SECURITY_MODE`, `REDIS_TLS_MODE`, `STORAGE_ROOT`, `PUBLIC_BASE_URL`, `CORS_ORIGINS`, `ALLOW_DEVELOPMENT_ANONYMOUS_USER_ACCESS`, `TYPESENSE_*`, `APP_SETTINGS_ENCRYPTION_KEY` -- Frontend: optional `VITE_API_BASE`, optional `VITE_API_TOKEN` compatibility fallback +- Frontend: optional `VITE_API_BASE`, optional `VITE_API_TOKEN` compatibility fallback, optional `VITE_DEV_PROXY_TARGET` -When `VITE_API_BASE` is unset, the frontend defaults to `http://:8000/api/v1`. +When `VITE_API_BASE` is unset, the frontend uses relative `/api/v1` paths via Vite proxy. Application settings saved from the UI persist at: diff --git a/doc/operations-and-configuration.md b/doc/operations-and-configuration.md index a6bcd36..cf8b9ca 100644 --- a/doc/operations-and-configuration.md +++ b/doc/operations-and-configuration.md @@ -116,10 +116,9 @@ Frontend runtime API target: - `VITE_API_BASE` in `docker-compose.yml` frontend service (optional override) - `VITE_API_TOKEN` in `docker-compose.yml` frontend service (optional compatibility fallback only) -When `VITE_API_BASE` is unset, frontend API helpers resolve the backend URL dynamically as: -- `http://:8000/api/v1` +When `VITE_API_BASE` is unset, frontend API helpers call relative `/api/v1` paths and the Vite dev server proxy forwards requests to `VITE_DEV_PROXY_TARGET` (defaults to `http://api:8000` in docker-compose). -This keeps development access working when the UI is opened through a LAN IP instead of `localhost`. +This avoids browser cross-origin/CORS failures for LAN-hosted development. Frontend API authentication behavior: - `frontend/src/lib/api.ts` resolves bearer tokens at request time in this order: diff --git a/docker-compose.yml b/docker-compose.yml index ef2424a..b362213 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,6 +120,7 @@ services: context: ./frontend environment: VITE_API_BASE: ${VITE_API_BASE:-} + VITE_DEV_PROXY_TARGET: ${VITE_DEV_PROXY_TARGET:-http://api:8000} VITE_API_TOKEN: ${VITE_API_TOKEN:-} ports: - "${HOST_BIND_IP:-127.0.0.1}:5173:5173" diff --git a/frontend/src/lib/api.test.ts b/frontend/src/lib/api.test.ts index 6d5f551..1df065e 100644 --- a/frontend/src/lib/api.test.ts +++ b/frontend/src/lib/api.test.ts @@ -97,11 +97,11 @@ async function runApiTests(): Promise { assert(await thumbnail.text() === 'preview-bytes', 'Thumbnail blob bytes mismatch'); assert(await preview.text() === 'preview-bytes', 'Preview blob bytes mismatch'); assert( - requestUrls[0] === 'http://localhost:8000/api/v1/documents/doc-1/thumbnail', + requestUrls[0] === '/api/v1/documents/doc-1/thumbnail', `Unexpected thumbnail URL ${requestUrls[0]}`, ); assert( - requestUrls[1] === 'http://localhost:8000/api/v1/documents/doc-1/preview', + requestUrls[1] === '/api/v1/documents/doc-1/preview', `Unexpected preview URL ${requestUrls[1]}`, ); assert(requestAuthHeaders[0] === null, `Expected no auth header for thumbnail request, got "${requestAuthHeaders[0]}"`); diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 7fa3ddb..5d805b0 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -14,7 +14,7 @@ import type { } from '../types'; /** - * Resolves backend base URL from environment with current-host HTTP fallback. + * Resolves backend base URL from environment with same-origin proxy fallback. */ function resolveApiBase(): string { const envValue = import.meta.env?.VITE_API_BASE; @@ -25,11 +25,7 @@ function resolveApiBase(): string { } } - if (typeof window !== 'undefined' && window.location?.hostname) { - return `http://${window.location.hostname}:8000/api/v1`; - } - - return 'http://localhost:8000/api/v1'; + return '/api/v1'; } const API_BASE = resolveApiBase(); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 5c6934f..820a5b5 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -10,5 +10,11 @@ export default defineConfig({ server: { host: '0.0.0.0', port: 5173, + proxy: { + '/api/v1': { + target: process.env.VITE_DEV_PROXY_TARGET ?? 'http://localhost:8000', + changeOrigin: true, + }, + }, }, });