Compare commits
3 Commits
b86223f943
...
d50169b883
| Author | SHA1 | Date | |
|---|---|---|---|
|
d50169b883
|
|||
|
b5b74845f2
|
|||
|
0acce2e260
|
@@ -39,7 +39,10 @@ PROVIDER_BASE_URL_ALLOWLIST=[]
|
||||
|
||||
PUBLIC_BASE_URL=http://localhost:8000
|
||||
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000"]
|
||||
# Used at build time for production frontend image, and at runtime in development.
|
||||
VITE_API_BASE=
|
||||
# Development-only Vite host allowlist override.
|
||||
VITE_ALLOWED_HOSTS=
|
||||
|
||||
# Optional frontend build network and npm fetch tuning:
|
||||
DOCKER_BUILD_NETWORK=default
|
||||
@@ -61,3 +64,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
|
||||
|
||||
@@ -83,6 +83,7 @@ These create an extra non-admin account on first startup.
|
||||
|
||||
- `APP_ENV=development`: Local mode (default).
|
||||
- `APP_ENV=production`: Use when running as a real shared deployment with HTTPS and tighter security settings.
|
||||
- Frontend runtime switches to a static build served by Nginx in this mode.
|
||||
|
||||
## Daily Use Commands
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,7 +86,8 @@ Use `.env.example` as baseline. The table below documents user-managed settings
|
||||
| `APP_ENV` | `development` | `production` |
|
||||
| `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_API_BASE` | empty for host-derived `http://<frontend-host>:8000/api/v1`, or explicit local URL | `https://api.example.com/api/v1` (build-time value for production frontend image) |
|
||||
| `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,14 @@ Recommended LIVE pattern:
|
||||
## Frontend Runtime
|
||||
|
||||
- Frontend no longer consumes `VITE_API_TOKEN`.
|
||||
- Frontend image target is environment-driven:
|
||||
- `APP_ENV=development` builds the `development` target and runs Vite dev server
|
||||
- `APP_ENV=production` builds the `production` target and serves static assets through Nginx
|
||||
- Frontend Docker targets are selected from `APP_ENV`, so use `development` or `production` values.
|
||||
- Vite dev server host allowlist uses the union of:
|
||||
- hostnames extracted from `CORS_ORIGINS`
|
||||
- optional explicit hostnames from `VITE_ALLOWED_HOSTS`
|
||||
- `VITE_ALLOWED_HOSTS` only affects development mode where Vite is running.
|
||||
- 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.
|
||||
|
||||
|
||||
@@ -143,8 +143,10 @@ services:
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
target: ${APP_ENV:-development}
|
||||
network: ${DOCKER_BUILD_NETWORK:-default}
|
||||
args:
|
||||
VITE_API_BASE: ${VITE_API_BASE:-}
|
||||
NPM_REGISTRY: ${NPM_REGISTRY:-https://registry.npmjs.org/}
|
||||
NPM_FETCH_RETRIES: ${NPM_FETCH_RETRIES:-5}
|
||||
NPM_FETCH_RETRY_MINTIMEOUT: ${NPM_FETCH_RETRY_MINTIMEOUT:-20000}
|
||||
@@ -152,6 +154,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:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:22-slim
|
||||
FROM node:22-slim AS base
|
||||
|
||||
ARG NPM_REGISTRY=https://registry.npmjs.org/
|
||||
ARG NPM_FETCH_RETRIES=5
|
||||
@@ -15,8 +15,8 @@ RUN npm config set registry "${NPM_REGISTRY}" \
|
||||
&& npm config set fetch-retry-mintimeout "${NPM_FETCH_RETRY_MINTIMEOUT}" \
|
||||
&& npm config set fetch-retry-maxtimeout "${NPM_FETCH_RETRY_MAXTIMEOUT}" \
|
||||
&& npm config set fetch-timeout "${NPM_FETCH_TIMEOUT}" \
|
||||
&& NODE_OPTIONS=--dns-result-order=ipv4first npm ci --no-audit
|
||||
RUN chown -R node:node /app
|
||||
&& NODE_OPTIONS=--dns-result-order=ipv4first npm ci --no-audit \
|
||||
&& chown -R node:node /app
|
||||
|
||||
COPY --chown=node:node tsconfig.json /app/tsconfig.json
|
||||
COPY --chown=node:node tsconfig.node.json /app/tsconfig.node.json
|
||||
@@ -24,8 +24,26 @@ COPY --chown=node:node vite.config.ts /app/vite.config.ts
|
||||
COPY --chown=node:node index.html /app/index.html
|
||||
COPY --chown=node:node src /app/src
|
||||
|
||||
FROM base AS development
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
USER node
|
||||
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"]
|
||||
|
||||
FROM base AS build
|
||||
|
||||
ARG VITE_API_BASE=
|
||||
ENV VITE_API_BASE=${VITE_API_BASE}
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.27-alpine AS production
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
12
frontend/nginx.conf
Normal file
12
frontend/nginx.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 5173;
|
||||
listen [::]:5173;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
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 } : {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user