Wire Vite allowed hosts to env for Docker frontend

This commit is contained in:
2026-03-02 15:37:39 -03:00
parent b86223f943
commit 0acce2e260
5 changed files with 86 additions and 7 deletions

View File

@@ -40,6 +40,7 @@ PROVIDER_BASE_URL_ALLOWLIST=[]
PUBLIC_BASE_URL=http://localhost:8000
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000"]
VITE_API_BASE=
VITE_ALLOWED_HOSTS=
# Optional frontend build network and npm fetch tuning:
DOCKER_BUILD_NETWORK=default
@@ -61,3 +62,4 @@ NPM_FETCH_TIMEOUT=300000
# PUBLIC_BASE_URL=https://api.example.com
# CORS_ORIGINS=["https://app.example.com"]
# VITE_API_BASE=https://api.example.com/api/v1
# VITE_ALLOWED_HOSTS=app.example.com

View File

@@ -8,6 +8,6 @@ This directory contains technical documentation for DMS.
- `architecture-overview.md` - backend, frontend, and infrastructure architecture
- `api-contract.md` - API endpoint contract grouped by route module, including session auth, login throttle responses, role and ownership scope, upload limits, and settings or processing-log security constraints
- `data-model-reference.md` - database entity definitions and lifecycle states
- `operations-and-configuration.md` - runtime operations, hardened compose defaults, DEV and LIVE security values, and persisted settings configuration behavior
- `operations-and-configuration.md` - runtime operations, hardened compose defaults, DEV and LIVE security values, persisted settings configuration behavior, and frontend Vite host allowlist controls
- `frontend-design-foundation.md` - frontend visual system, tokens, UI implementation rules, authenticated media delivery under session auth, processing-log timeline behavior, and settings helper-copy guidance
- `../.env.example` - repository-level environment template with local defaults and production override guidance

View File

@@ -87,6 +87,7 @@ Use `.env.example` as baseline. The table below documents user-managed settings
| `HOST_BIND_IP` | `127.0.0.1` or local LAN bind if needed | `127.0.0.1` (publish behind proxy only) |
| `PUBLIC_BASE_URL` | `http://localhost:8000` | `https://api.example.com` |
| `VITE_API_BASE` | empty for host-derived `http://<frontend-host>:8000/api/v1`, or explicit local URL | `https://api.example.com/api/v1` |
| `VITE_ALLOWED_HOSTS` | optional comma-separated hostnames, for example `localhost,docs.lan` | optional comma-separated public frontend hostnames, for example `app.example.com` |
| `CORS_ORIGINS` | `["http://localhost:5173","http://localhost:3000"]` | exact frontend origins only, for example `["https://app.example.com"]` |
| `REDIS_URL` | `redis://:<password>@redis:6379/0` in isolated local network | `rediss://:<password>@redis.internal:6379/0` |
| `REDIS_SECURITY_MODE` | `compat` or `auto` | `strict` |
@@ -138,6 +139,9 @@ Recommended LIVE pattern:
## Frontend Runtime
- Frontend no longer consumes `VITE_API_TOKEN`.
- Vite dev server host allowlist uses the union of:
- hostnames extracted from `CORS_ORIGINS`
- optional explicit hostnames from `VITE_ALLOWED_HOSTS`
- Session authentication is cookie-based; browser reloads and new tabs can reuse an active session until it expires or is revoked.
- Protected media and file download flows still use authenticated fetch plus blob/object URL handling.

View File

@@ -152,6 +152,8 @@ services:
NPM_FETCH_TIMEOUT: ${NPM_FETCH_TIMEOUT:-300000}
environment:
VITE_API_BASE: ${VITE_API_BASE:-}
CORS_ORIGINS: '${CORS_ORIGINS:-["http://localhost:5173","http://localhost:3000"]}'
VITE_ALLOWED_HOSTS: ${VITE_ALLOWED_HOSTS:-}
# ports:
# - "${HOST_BIND_IP:-127.0.0.1}:5173:5173"
volumes:

View File

@@ -1,14 +1,85 @@
/**
* Vite configuration for the DMS frontend application.
*/
import { defineConfig } from 'vite';
import { defineConfig, loadEnv } from 'vite';
/**
* Parses a comma-separated environment value into normalized entries.
*
* @param rawValue Raw comma-separated value.
* @returns List of non-empty normalized entries.
*/
function parseCsvList(rawValue: string | undefined): string[] {
if (!rawValue) {
return [];
}
return rawValue
.split(',')
.map((entry) => entry.trim())
.filter((entry) => entry.length > 0);
}
/**
* Extracts hostnames from CORS origin values.
*
* @param rawValue JSON array string or comma-separated origin list.
* @returns Hostnames parsed from valid origins.
*/
function parseCorsOriginHosts(rawValue: string | undefined): string[] {
if (!rawValue) {
return [];
}
let origins: string[] = [];
try {
const parsedOrigins = JSON.parse(rawValue);
if (Array.isArray(parsedOrigins)) {
origins = parsedOrigins.filter((entry): entry is string => typeof entry === 'string');
} else if (typeof parsedOrigins === 'string') {
origins = [parsedOrigins];
}
} catch {
origins = parseCsvList(rawValue);
}
return origins.flatMap((origin) => {
try {
const parsedUrl = new URL(origin);
return parsedUrl.hostname ? [parsedUrl.hostname] : [];
} catch {
return [];
}
});
}
/**
* Builds the Vite allowed host list from environment-driven inputs.
*
* @param env Environment variable key-value map.
* @returns De-duplicated hostnames, or undefined to keep Vite defaults.
*/
function buildAllowedHosts(env: Record<string, string>): string[] | undefined {
const explicitHosts = parseCsvList(env.VITE_ALLOWED_HOSTS);
const corsOriginHosts = parseCorsOriginHosts(env.CORS_ORIGINS);
const mergedHosts = Array.from(new Set([...explicitHosts, ...corsOriginHosts]));
return mergedHosts.length > 0 ? mergedHosts : undefined;
}
/**
* Exports frontend build and dev-server settings.
*/
export default defineConfig({
server: {
host: '0.0.0.0',
port: 5173,
},
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const allowedHosts = buildAllowedHosts(env);
return {
server: {
host: '0.0.0.0',
port: 5173,
...(allowedHosts ? { allowedHosts } : {}),
},
};
});