Files
visitor-signin/src/config.js
T

97 lines
4.0 KiB
JavaScript

import 'dotenv/config';
import path from 'node:path';
import crypto from 'node:crypto';
function bool(value, fallback) {
if (value === undefined || value === '') return fallback;
return ['1', 'true', 'yes', 'on'].includes(String(value).toLowerCase());
}
function int(value, fallback) {
const n = Number.parseInt(value, 10);
return Number.isFinite(n) ? n : fallback;
}
const dataDir = process.env.DATA_DIR || '/data';
if (!process.env.APP_SECRET) {
console.warn(
'[config] APP_SECRET is not set. A random one is being generated for this process only.\n' +
' Sessions will drop and stored visitor PINs will become unreadable on restart.\n' +
' Set APP_SECRET in your .env before going live.'
);
}
export const config = {
port: int(process.env.PORT, 3000),
siteName: process.env.SITE_NAME || 'Visitor sign in',
timezone: process.env.TZ || 'Australia/Melbourne',
dataDir,
dbPath: process.env.DB_PATH || path.join(dataDir, 'visitors.db'),
photoDir: process.env.PHOTO_DIR || path.join(dataDir, 'photos'),
appSecret: process.env.APP_SECRET || crypto.randomBytes(32).toString('hex'),
trustProxy: bool(process.env.TRUST_PROXY, false),
secureCookies: bool(process.env.SECURE_COOKIES, false),
admin: {
// Used once, to create the first account if the user table is empty.
bootstrapEmail: (process.env.ADMIN_BOOTSTRAP_EMAIL || '').trim().toLowerCase(),
bootstrapPassword: process.env.ADMIN_BOOTSTRAP_PASSWORD || process.env.ADMIN_PASSWORD || '',
// Blank allows any address. Otherwise a comma separated list, e.g. "school.vic.edu.au".
allowedDomains: (process.env.ADMIN_ALLOWED_DOMAINS || '')
.split(',')
.map((d) => d.trim().toLowerCase().replace(/^@/, ''))
.filter(Boolean),
require2fa: bool(process.env.ADMIN_REQUIRE_2FA, true),
},
printing: {
// brother_ql drives the label printer over the network. Overridable so a
// wrapper or a different binary can be swapped in.
command: process.env.PRINT_COMMAND || 'brother_ql',
timeoutMs: int(process.env.PRINT_TIMEOUT_MS, 15000),
// How long a sign in waits for the badge before falling back to the browser.
signInWaitMs: int(process.env.PRINT_SIGNIN_WAIT_MS, 9000),
},
// Admins are warned this many days before a WWCC or VIT expires.
expiryWarningDays: int(process.env.EXPIRY_WARNING_DAYS, 28),
requirePhoto: bool(process.env.REQUIRE_PHOTO, true),
photoRetentionDays: int(process.env.PHOTO_RETENTION_DAYS, 90),
// Blank disables the nightly sweep. Format "HH:MM" in local time.
autoSignOutTime: process.env.AUTO_SIGNOUT_TIME || '',
https: {
enabled: bool(process.env.HTTPS_ENABLED, false),
keyPath: process.env.HTTPS_KEY || path.join(dataDir, 'certs', 'server.key'),
certPath: process.env.HTTPS_CERT || path.join(dataDir, 'certs', 'server.crt'),
// Names and addresses staff will actually type. Baked into the certificate.
hostnames: (process.env.HTTPS_HOSTNAMES || 'visitors.local')
.split(',')
.map((h) => h.trim())
.filter(Boolean),
// A plain http listener that serves the CA certificate and redirects
// everything else to https. 0 turns it off.
redirectPort: int(process.env.HTTP_REDIRECT_PORT, 3001),
// The https port as published on the docker host, used when redirecting.
publicPort: int(process.env.HTTPS_PUBLIC_PORT, 8443),
},
sheets: {
enabled: bool(process.env.SHEETS_ENABLED, false),
spreadsheetId: process.env.SHEETS_SPREADSHEET_ID || '',
// Append-only history of every sign in and sign out.
logTab: process.env.SHEETS_LOG_TAB || process.env.SHEETS_TAB_NAME || 'Visitor log',
// Rewritten on every change: just the people currently on site.
onSiteTab: process.env.SHEETS_ONSITE_TAB || 'On site now',
// Either a path to the service account JSON, or the JSON itself base64 encoded.
credentialsPath: process.env.GOOGLE_CREDENTIALS_PATH || '',
credentialsB64: process.env.GOOGLE_CREDENTIALS_B64 || '',
retryIntervalMs: int(process.env.SHEETS_RETRY_INTERVAL_MS, 60000),
},
};
export default config;