Fix LAN API access and dev proxy routing
This commit is contained in:
@@ -116,9 +116,9 @@ cd frontend && npm run preview
|
|||||||
Main runtime variables are defined in `docker-compose.yml`:
|
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`
|
- 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://<current-hostname>: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:
|
Application settings saved from the UI persist at:
|
||||||
|
|
||||||
|
|||||||
@@ -116,10 +116,9 @@ Frontend runtime API target:
|
|||||||
- `VITE_API_BASE` in `docker-compose.yml` frontend service (optional override)
|
- `VITE_API_BASE` in `docker-compose.yml` frontend service (optional override)
|
||||||
- `VITE_API_TOKEN` in `docker-compose.yml` frontend service (optional compatibility fallback only)
|
- `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:
|
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).
|
||||||
- `http://<current-frontend-hostname>:8000/api/v1`
|
|
||||||
|
|
||||||
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 API authentication behavior:
|
||||||
- `frontend/src/lib/api.ts` resolves bearer tokens at request time in this order:
|
- `frontend/src/lib/api.ts` resolves bearer tokens at request time in this order:
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ services:
|
|||||||
context: ./frontend
|
context: ./frontend
|
||||||
environment:
|
environment:
|
||||||
VITE_API_BASE: ${VITE_API_BASE:-}
|
VITE_API_BASE: ${VITE_API_BASE:-}
|
||||||
|
VITE_DEV_PROXY_TARGET: ${VITE_DEV_PROXY_TARGET:-http://api:8000}
|
||||||
VITE_API_TOKEN: ${VITE_API_TOKEN:-}
|
VITE_API_TOKEN: ${VITE_API_TOKEN:-}
|
||||||
ports:
|
ports:
|
||||||
- "${HOST_BIND_IP:-127.0.0.1}:5173:5173"
|
- "${HOST_BIND_IP:-127.0.0.1}:5173:5173"
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ async function runApiTests(): Promise<void> {
|
|||||||
assert(await thumbnail.text() === 'preview-bytes', 'Thumbnail blob bytes mismatch');
|
assert(await thumbnail.text() === 'preview-bytes', 'Thumbnail blob bytes mismatch');
|
||||||
assert(await preview.text() === 'preview-bytes', 'Preview blob bytes mismatch');
|
assert(await preview.text() === 'preview-bytes', 'Preview blob bytes mismatch');
|
||||||
assert(
|
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]}`,
|
`Unexpected thumbnail URL ${requestUrls[0]}`,
|
||||||
);
|
);
|
||||||
assert(
|
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]}`,
|
`Unexpected preview URL ${requestUrls[1]}`,
|
||||||
);
|
);
|
||||||
assert(requestAuthHeaders[0] === null, `Expected no auth header for thumbnail request, got "${requestAuthHeaders[0]}"`);
|
assert(requestAuthHeaders[0] === null, `Expected no auth header for thumbnail request, got "${requestAuthHeaders[0]}"`);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type {
|
|||||||
} from '../types';
|
} 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 {
|
function resolveApiBase(): string {
|
||||||
const envValue = import.meta.env?.VITE_API_BASE;
|
const envValue = import.meta.env?.VITE_API_BASE;
|
||||||
@@ -25,11 +25,7 @@ function resolveApiBase(): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.location?.hostname) {
|
return '/api/v1';
|
||||||
return `http://${window.location.hostname}:8000/api/v1`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'http://localhost:8000/api/v1';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const API_BASE = resolveApiBase();
|
const API_BASE = resolveApiBase();
|
||||||
|
|||||||
@@ -10,5 +10,11 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api/v1': {
|
||||||
|
target: process.env.VITE_DEV_PROXY_TARGET ?? 'http://localhost:8000',
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user