chore(config): centralize and document environment variables

- Read game paths and NODE_ENV through the config module in
  gamezipserver instead of process.env directly
- Harden SQLite cache/mmap parsing with isNaN checks so invalid
  values fall back to defaults instead of NaN
- Pass auth, rate-limit, application, and OpenTelemetry variables
  through both docker-compose files with documented defaults
- Expand env var documentation and .env examples to match
This commit is contained in:
Darkraise
2026-08-11 16:02:06 +07:00
parent b57f2701c0
commit bd2514f1f7
7 changed files with 144 additions and 54 deletions
+23 -1
View File
@@ -43,9 +43,11 @@ LOG_LEVEL=info
OTEL_ENABLED=false
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# OTEL_API_KEY=your-api-key-here
OTEL_SERVICE_NAME=flashpoint-web
OTEL_SERVICE_NAME=flashpoint-web-backend
OTEL_TRACES_ENABLED=true
OTEL_METRICS_ENABLED=true
# OTEL_METRICS_EXPORT_INTERVAL=60000
# OTEL_LOG_LEVEL=info
# ---------------------------------------------------------------------------
# Container User Permissions
@@ -78,3 +80,23 @@ SQLITE_MMAP_SIZE=268435456
# Cache pre-warming (loads common queries on startup)
ENABLE_CACHE_PREWARM=true
# ---------------------------------------------------------------------------
# Auth & Rate Limiting
# ---------------------------------------------------------------------------
# JWT_EXPIRES_IN=1h
# BCRYPT_SALT_ROUNDS=10
# RATE_LIMIT_WINDOW_MS=60000
# RATE_LIMIT_MAX_REQUESTS=100
# ---------------------------------------------------------------------------
# Application
# ---------------------------------------------------------------------------
# HOME_RECENT_HOURS=24
# ENABLE_CGI=false
# ---------------------------------------------------------------------------
# Frontend Nginx (Docker only)
# ---------------------------------------------------------------------------
# BACKEND_HOST=backend
# BACKEND_PORT=3100
+3
View File
@@ -18,6 +18,9 @@ RATE_LIMIT_MAX_REQUESTS=100
JWT_EXPIRES_IN=1h
BCRYPT_SALT_ROUNDS=10
# Game content serving
# ENABLE_CGI=false
# Misc
HOME_RECENT_HOURS=24
+9 -3
View File
@@ -66,7 +66,7 @@ const flashpointVersion = parseVersionFile();
export const config = {
nodeEnv: process.env.NODE_ENV || 'development',
port: 3100,
port: 3100, // Hardcoded — Docker EXPOSE and healthcheck depend on this value
host: '0.0.0.0',
flashpointPath,
@@ -100,8 +100,14 @@ export const config = {
enableLocalDbCopy: process.env.ENABLE_LOCAL_DB_COPY === 'true',
localDbPath: '/app/data/flashpoint.sqlite',
sqliteCacheSize: parseInt(process.env.SQLITE_CACHE_SIZE || '', 10) || -64000, // Negative = KB (-64000 = 64MB)
sqliteMmapSize: parseInt(process.env.SQLITE_MMAP_SIZE || '', 10) || 268435456, // 256MB
sqliteCacheSize: (() => {
const parsed = parseInt(process.env.SQLITE_CACHE_SIZE ?? '', 10);
return isNaN(parsed) ? -64000 : parsed; // Negative = KB (-64000 = 64MB)
})(),
sqliteMmapSize: (() => {
const parsed = parseInt(process.env.SQLITE_MMAP_SIZE ?? '', 10);
return isNaN(parsed) ? 268435456 : parsed; // 256MB
})(),
enableCachePrewarm: process.env.ENABLE_CACHE_PREWARM !== 'false',
// Auto-detected from version.txt; affects metadata sync and image path availability
+3 -5
View File
@@ -2,6 +2,7 @@ import http from 'http';
import path from 'path';
import fs from 'fs/promises';
import { logger } from '../utils/logger';
import { config } from '../config';
import { zipManager } from './zip-manager';
import { ConfigManager } from './config';
import { getMimeType } from './mimeTypes';
@@ -73,11 +74,8 @@ export class GameZipServer {
return { success: false, statusCode: 400 };
}
const flashpointPath =
process.env.FLASHPOINT_PATH ||
(process.env.NODE_ENV === 'production' ? '/data/flashpoint' : 'D:/Flashpoint');
const allowedGamesPath =
process.env.FLASHPOINT_GAMES_PATH || path.join(flashpointPath, 'Data', 'Games');
process.env.FLASHPOINT_GAMES_PATH ?? config.flashpointGamesPath;
try {
const normalizedZipPath = path.normalize(zipPath);
@@ -318,7 +316,7 @@ export class GameZipServer {
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Length', fileData.length);
res.setHeader('Cache-Control', 'public, max-age=86400');
if (process.env.NODE_ENV !== 'production') {
if (config.nodeEnv !== 'production') {
res.setHeader('X-Source', `gamezipserver:${result.mountId}`);
}
res.setHeader('Connection', 'keep-alive');
+13
View File
@@ -44,10 +44,23 @@ services:
- SQLITE_CACHE_SIZE=${SQLITE_CACHE_SIZE:--64000}
- SQLITE_MMAP_SIZE=${SQLITE_MMAP_SIZE:-268435456}
- ENABLE_CACHE_PREWARM=${ENABLE_CACHE_PREWARM:-true}
# Auth & rate limiting
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-1h}
- BCRYPT_SALT_ROUNDS=${BCRYPT_SALT_ROUNDS:-10}
- RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
- RATE_LIMIT_MAX_REQUESTS=${RATE_LIMIT_MAX_REQUESTS:-100}
# Application
- HOME_RECENT_HOURS=${HOME_RECENT_HOURS:-24}
- ENABLE_CGI=${ENABLE_CGI:-false}
# OpenTelemetry
- OTEL_ENABLED=${OTEL_ENABLED:-false}
- OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-}
- OTEL_API_KEY=${OTEL_API_KEY:-}
- OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-flashpoint-web-backend}
- OTEL_TRACES_ENABLED=${OTEL_TRACES_ENABLED:-true}
- OTEL_METRICS_ENABLED=${OTEL_METRICS_ENABLED:-true}
- OTEL_METRICS_EXPORT_INTERVAL=${OTEL_METRICS_EXPORT_INTERVAL:-60000}
- OTEL_LOG_LEVEL=${OTEL_LOG_LEVEL:-info}
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:3100/health"]
interval: 30s
+13
View File
@@ -39,10 +39,23 @@ services:
- SQLITE_CACHE_SIZE=${SQLITE_CACHE_SIZE:--64000}
- SQLITE_MMAP_SIZE=${SQLITE_MMAP_SIZE:-268435456}
- ENABLE_CACHE_PREWARM=${ENABLE_CACHE_PREWARM:-true}
# Auth & rate limiting
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-1h}
- BCRYPT_SALT_ROUNDS=${BCRYPT_SALT_ROUNDS:-10}
- RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
- RATE_LIMIT_MAX_REQUESTS=${RATE_LIMIT_MAX_REQUESTS:-100}
# Application
- HOME_RECENT_HOURS=${HOME_RECENT_HOURS:-24}
- ENABLE_CGI=${ENABLE_CGI:-false}
# OpenTelemetry
- OTEL_ENABLED=${OTEL_ENABLED:-false}
- OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-}
- OTEL_API_KEY=${OTEL_API_KEY:-}
- OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-flashpoint-web-backend}
- OTEL_TRACES_ENABLED=${OTEL_TRACES_ENABLED:-true}
- OTEL_METRICS_ENABLED=${OTEL_METRICS_ENABLED:-true}
- OTEL_METRICS_EXPORT_INTERVAL=${OTEL_METRICS_EXPORT_INTERVAL:-60000}
- OTEL_LOG_LEVEL=${OTEL_LOG_LEVEL:-info}
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:3100/health"]
interval: 30s
+80 -45
View File
@@ -4,28 +4,29 @@ Configuration reference for all Flashpoint Web services.
## Backend Variables
Location: `backend/.env`
Location: `backend/.env` (local development)
**Server:**
| Variable | Default | Description |
| ---------- | ----------- | ----------------------------------------------- |
| `NODE_ENV` | development | Environment: development, production, test |
| `PORT` | 3100 | HTTP server port |
| `HOST` | 0.0.0.0 | Bind address (use 127.0.0.1 for localhost only) |
| Variable | Default | Description |
| ---------- | ----------- | ------------------------------------------ |
| `NODE_ENV` | development | Environment: development, production, test |
**Paths (only set FLASHPOINT_PATH):**
> **Note:** Port (3100) and bind address (0.0.0.0) are hardcoded in the backend.
> In Docker, use `API_PORT` to map a different host port to the container.
| Variable | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `FLASHPOINT_PATH` | Root Flashpoint directory. All other paths auto-derived. Edition (Infinity/Ultimate) auto-detected from `version.txt`. |
**Paths:**
| Variable | Default | Description |
| ---------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `FLASHPOINT_PATH` | `D:/Flashpoint` (dev), `/data/flashpoint` (prod) | Root Flashpoint directory. All other paths auto-derived. Edition (Infinity/Ultimate) auto-detected from `version.txt`. |
| `FLASHPOINT_GAMES_PATH`| `<FLASHPOINT_PATH>/Data/Games` | Override game ZIP directory (rarely needed). |
**Authentication & Security:**
| Variable | Default | Description |
| -------------------- | --------------------- | ---------------------------------------------------- |
| `JWT_SECRET` | INSECURE-... | Secret for JWT signing (CHANGE IN PRODUCTION!) |
| `JWT_SECRET` | auto-generated (dev) | Secret for JWT signing (**required in production**) |
| `JWT_EXPIRES_IN` | 1h | Access token expiration (15m, 1h, 7d, etc.) |
| `BCRYPT_SALT_ROUNDS` | 10 | Password hash cost (higher = more secure but slower) |
| `DOMAIN` | http://localhost:5173 | CORS origin (use specific domain in production) |
@@ -45,11 +46,39 @@ openssl rand -hex 64
**Logging:**
| Variable | Default | Description |
| ----------- | ------- | ------------------------------- |
| `LOG_LEVEL` | info | Level: error, warn, info, debug |
| `LOG_FILE` | - | Optional log file path |
| Variable | Default | Description |
| ----------- | ------------------------------ | -------------------------------------------- |
| `LOG_LEVEL` | info | Level: error, warn, info, debug |
| `LOG_FILE` | `/app/logs/backend.log` (prod) | Log file path (unset in dev = stdout only) |
**Database Performance:**
| Variable | Default | Description |
| --------------------- | ----------- | ---------------------------------------------------------------- |
| `ENABLE_LOCAL_DB_COPY`| false | Copy flashpoint.sqlite locally (for network storage SMB/NFS) |
| `SQLITE_CACHE_SIZE` | -64000 | SQLite page cache size (negative = KB, -64000 = 64MB) |
| `SQLITE_MMAP_SIZE` | 268435456 | Memory-mapped I/O size in bytes (256MB) |
| `ENABLE_CACHE_PREWARM`| true | Pre-load common queries on startup |
**Game Content Serving:**
| Variable | Default | Description |
| -------------------- | ------- | ------------------------------------------------ |
| `ENABLE_CGI` | false | Enable PHP CGI execution for legacy game content |
| `HOME_RECENT_HOURS` | 24 | Hours to look back for "recently added" games |
**OpenTelemetry:**
| Variable | Default | Description |
| ------------------------------ | -------------------------- | ---------------------------------------- |
| `OTEL_ENABLED` | false | Enable distributed tracing and metrics |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | http://localhost:4318 | OTLP collector endpoint |
| `OTEL_API_KEY` | - | API key for authentication |
| `OTEL_SERVICE_NAME` | flashpoint-web-backend | Service name in traces/metrics |
| `OTEL_TRACES_ENABLED` | true | Enable trace export (when OTEL enabled) |
| `OTEL_METRICS_ENABLED` | true | Enable metrics export (when OTEL enabled)|
| `OTEL_METRICS_EXPORT_INTERVAL`| 60000 | Metrics export interval (ms) |
| `OTEL_LOG_LEVEL` | info | OTEL SDK log level |
## Frontend Variables
@@ -57,33 +86,46 @@ The frontend has no environment variables in development or production. API
calls are proxied through the backend.
- **Development**: Via Vite proxy to `http://localhost:3100`
- **Production**: Via Nginx or reverse proxy
- **Production**: Via Nginx reverse proxy (configured by `BACKEND_HOST`/`BACKEND_PORT`)
## Docker Environment Variables
Location: `.env` in project root
Location: `.env` in project root (used by docker-compose)
**Core Configuration:**
**Required:**
| Variable | Default | Description |
| ---------------------- | ------------- | ------------------------------------ |
| `FLASHPOINT_HOST_PATH` | D:/Flashpoint | Host path to Flashpoint installation |
| `NODE_ENV` | production | Environment for all services |
| `JWT_SECRET` | - | Secret for JWT signing (required) |
| Variable | Description |
| ---------------------- | --------------------------------------------------------- |
| `FLASHPOINT_HOST_PATH` | Host path to Flashpoint installation (bind-mounted as ro) |
| `JWT_SECRET` | Secret for JWT signing (generate with `openssl rand -hex 64`) |
**Port Mapping:**
| Variable | Default | Description |
| ---------- | ------- | -------------------- |
| Variable | Default | Description |
| ---------- | ------- | --------------------- |
| `WEB_PORT` | 80 | Frontend exposed port |
| `API_PORT` | 3100 | Backend exposed port |
**Other:**
**Container Settings:**
| Variable | Default | Description |
| ----------- | ---------------- | ------------- |
| `DOMAIN` | http://localhost | CORS origin |
| `LOG_LEVEL` | info | Logging level |
| Variable | Default | Description |
| ----------- | ------- | ---------------------------------------------------- |
| `PUID` | 1000 | Container user ID (match host user: `id -u`) |
| `PGID` | 1000 | Container group ID (match host user: `id -g`) |
| `TZ` | UTC | Container timezone |
| `IMAGE_TAG` | latest | Docker image tag (production compose only) |
| `DATA_PATH` | ./data | Host path for persistent data (production compose) |
| `LOGS_PATH` | ./logs | Host path for log files |
**Frontend Nginx:**
| Variable | Default | Description |
| -------------- | -------- | --------------------------------------- |
| `BACKEND_HOST` | backend | Backend hostname (Docker service name) |
| `BACKEND_PORT` | 3100 | Backend port for Nginx proxy_pass |
All backend variables listed above are also passable via docker-compose. See
`docker-compose.yml` for the full list with defaults.
## Docker Compose Setup
@@ -92,7 +134,6 @@ Location: `.env` in project root
```bash
cat > .env << EOF
FLASHPOINT_HOST_PATH=/path/to/flashpoint
NODE_ENV=production
JWT_SECRET=$(openssl rand -hex 64)
DOMAIN=https://flashpoint.example.com
WEB_PORT=80
@@ -104,17 +145,15 @@ EOF
**Start services:**
```bash
docker-compose --env-file .env up -d
docker compose up -d
```
## Environment Templates
**Development (.env.development):**
**Development (`backend/.env`):**
```bash
NODE_ENV=development
PORT=3100
HOST=0.0.0.0
FLASHPOINT_PATH=D:/Flashpoint
JWT_SECRET=development-secret-change-in-production
DOMAIN=http://localhost:5173
@@ -122,33 +161,29 @@ RATE_LIMIT_MAX_REQUESTS=1000
LOG_LEVEL=debug
```
**Production (.env.production):**
**Production (`.env` for Docker):**
```bash
NODE_ENV=production
PORT=3100
HOST=127.0.0.1
FLASHPOINT_PATH=/data/flashpoint
FLASHPOINT_HOST_PATH=/data/flashpoint
JWT_SECRET=CHANGE-THIS-TO-A-RANDOM-64-CHARACTER-STRING
DOMAIN=https://flashpoint.example.com
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100
WEB_PORT=80
API_PORT=3100
LOG_LEVEL=warn
LOG_FILE=/var/log/flashpoint-backend.log
```
## Validation and Defaults
**Required Variables:**
**Backend:**
**Backend (local dev):**
- `FLASHPOINT_PATH` (only this is required; others auto-derive)
**Docker Production:**
- `FLASHPOINT_HOST_PATH` (must point to Flashpoint installation)
- `JWT_SECRET` (must be changed from default)
- `DOMAIN` (must match frontend URL)
**Verify configuration:**