2026-01-05 19:46:33 -08:00
|
|
|
// Configuration for MacroPad Relay Server
|
|
|
|
|
|
|
|
|
|
import dotenv from 'dotenv';
|
|
|
|
|
import path from 'path';
|
|
|
|
|
|
|
|
|
|
dotenv.config();
|
|
|
|
|
|
2026-07-17 10:15:23 -07:00
|
|
|
function parseTrustProxy(): boolean | number | string {
|
|
|
|
|
const v = process.env.TRUST_PROXY ?? '1';
|
|
|
|
|
if (v === 'false') return false;
|
|
|
|
|
if (v === 'true') return true;
|
|
|
|
|
const n = Number(v);
|
|
|
|
|
return Number.isNaN(n) ? v : n;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 19:46:33 -08:00
|
|
|
export const config = {
|
|
|
|
|
port: parseInt(process.env.PORT || '3000', 10),
|
|
|
|
|
host: process.env.HOST || '0.0.0.0',
|
|
|
|
|
|
|
|
|
|
// Data storage
|
|
|
|
|
dataDir: process.env.DATA_DIR || path.join(__dirname, '..', 'data'),
|
|
|
|
|
|
|
|
|
|
// Session settings
|
2026-07-17 10:15:23 -07:00
|
|
|
sessionIdLength: parseInt(process.env.SESSION_ID_LENGTH || '12', 10),
|
|
|
|
|
|
|
|
|
|
// Maximum number of sessions that may exist at once
|
|
|
|
|
maxSessions: parseInt(process.env.MAX_SESSIONS || '1000', 10),
|
|
|
|
|
|
|
|
|
|
// Minimum password length enforced when creating a new session
|
|
|
|
|
minPasswordLength: parseInt(process.env.MIN_PASSWORD_LENGTH || '6', 10),
|
|
|
|
|
|
|
|
|
|
// Sessions unused for this many days are pruned
|
|
|
|
|
sessionTtlDays: parseInt(process.env.SESSION_TTL_DAYS || '90', 10),
|
2026-01-05 19:46:33 -08:00
|
|
|
|
|
|
|
|
// Security
|
|
|
|
|
bcryptRounds: parseInt(process.env.BCRYPT_ROUNDS || '10', 10),
|
|
|
|
|
|
2026-07-17 10:15:23 -07:00
|
|
|
// Rate limiting (HTTP)
|
2026-01-05 19:46:33 -08:00
|
|
|
rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000', 10), // 15 minutes
|
2026-07-17 10:15:23 -07:00
|
|
|
rateLimitMax: parseInt(process.env.RATE_LIMIT_MAX || '300', 10),
|
|
|
|
|
|
|
|
|
|
// WebSocket upgrade rate limiting (per IP)
|
|
|
|
|
wsUpgradeWindowMs: parseInt(process.env.WS_UPGRADE_WINDOW_MS || '60000', 10), // 1 minute
|
|
|
|
|
wsUpgradeMax: parseInt(process.env.WS_UPGRADE_MAX || '60', 10), // max upgrades per IP per window
|
|
|
|
|
|
|
|
|
|
// Auth brute-force protection
|
|
|
|
|
authMaxSocketFailures: parseInt(process.env.AUTH_MAX_SOCKET_FAILURES || '5', 10),
|
|
|
|
|
authLockoutMaxAttempts: parseInt(process.env.AUTH_LOCKOUT_MAX_ATTEMPTS || '10', 10),
|
|
|
|
|
authLockoutWindowMs: parseInt(process.env.AUTH_LOCKOUT_WINDOW_MS || '900000', 10), // 15 minutes
|
|
|
|
|
authLockoutDurationMs: parseInt(process.env.AUTH_LOCKOUT_DURATION_MS || '900000', 10), // 15 minutes
|
2026-01-05 19:46:33 -08:00
|
|
|
|
|
|
|
|
// WebSocket
|
|
|
|
|
pingInterval: parseInt(process.env.PING_INTERVAL || '30000', 10), // 30 seconds
|
|
|
|
|
requestTimeout: parseInt(process.env.REQUEST_TIMEOUT || '30000', 10), // 30 seconds
|
|
|
|
|
|
2026-07-17 10:15:23 -07:00
|
|
|
// Origin / CORS allowlist (comma-separated). Empty = same-host only.
|
|
|
|
|
allowedOrigins: (process.env.ALLOWED_ORIGINS || '')
|
|
|
|
|
.split(',')
|
|
|
|
|
.map((o) => o.trim())
|
|
|
|
|
.filter((o) => o.length > 0),
|
|
|
|
|
|
|
|
|
|
// Trust the X-Forwarded-* headers from a reverse proxy
|
|
|
|
|
trustProxy: parseTrustProxy(),
|
|
|
|
|
|
2026-01-05 19:46:33 -08:00
|
|
|
// Logging
|
|
|
|
|
logLevel: process.env.LOG_LEVEL || 'info',
|
|
|
|
|
|
|
|
|
|
// Environment
|
|
|
|
|
nodeEnv: process.env.NODE_ENV || 'development',
|
|
|
|
|
isDevelopment: process.env.NODE_ENV !== 'production',
|
|
|
|
|
};
|