LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
/* ── Environment configuration & startup validation ───────────────────────
|
||||
Require this module BEFORE anything else that reads process.env.
|
||||
Fails fast with a clear message if required vars are missing or weak.
|
||||
──────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
require('dotenv').config({ path: require('path').join(__dirname, '../.env') });
|
||||
|
||||
const path = require('path');
|
||||
const errors = [];
|
||||
|
||||
function _require(key, { minLen, notValue } = {}) {
|
||||
const val = process.env[key];
|
||||
if (!val) { errors.push(`${key} is required`); return undefined; }
|
||||
if (minLen && val.length < minLen) errors.push(`${key} must be at least ${minLen} chars (got ${val.length})`);
|
||||
if (notValue && val === notValue) errors.push(`${key} must not be the default placeholder value`);
|
||||
return val;
|
||||
}
|
||||
|
||||
function _optional(key, defaultVal = undefined) {
|
||||
return process.env[key] || defaultVal;
|
||||
}
|
||||
|
||||
/* ── Required vars ────────────────────────────────────────────────────── */
|
||||
const JWT_SECRET = _require('JWT_SECRET', {
|
||||
minLen: 32,
|
||||
notValue: 'change_this_to_a_long_random_string',
|
||||
});
|
||||
|
||||
/* ── Optional vars with defaults ─────────────────────────────────────── */
|
||||
const NODE_ENV = _optional('NODE_ENV', 'development');
|
||||
if (!['development', 'production', 'test'].includes(NODE_ENV)) {
|
||||
errors.push(`NODE_ENV must be development | production | test (got: "${NODE_ENV}")`);
|
||||
}
|
||||
|
||||
const PORT_RAW = _optional('PORT', '3000');
|
||||
const PORT = Number(PORT_RAW);
|
||||
if (!Number.isInteger(PORT) || PORT < 1 || PORT > 65535) {
|
||||
errors.push(`PORT must be an integer between 1 and 65535 (got: "${PORT_RAW}")`);
|
||||
}
|
||||
|
||||
/* ── Fail fast ───────────────────────────────────────────────────────── */
|
||||
if (errors.length) {
|
||||
process.stderr.write('\n[config] FATAL: invalid environment configuration:\n');
|
||||
errors.forEach(e => process.stderr.write(` ✗ ${e}\n`));
|
||||
process.stderr.write('\nFix these issues in your .env file and restart.\n\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
module.exports = Object.freeze({
|
||||
/* env */
|
||||
JWT_SECRET,
|
||||
PORT,
|
||||
NODE_ENV,
|
||||
isProd: NODE_ENV === 'production',
|
||||
LOG_LEVEL: _optional('LOG_LEVEL', NODE_ENV === 'production' ? 'info' : 'debug'),
|
||||
CLIENT_ORIGIN: _optional('CLIENT_ORIGIN'),
|
||||
/* paths */
|
||||
DB_PATH: _optional('DB_PATH', path.join(__dirname, '../data/learnspace.db')),
|
||||
UPLOADS_DIR: _optional('UPLOADS_DIR', path.join(__dirname, '../uploads')),
|
||||
/* constants */
|
||||
BCRYPT_ROUNDS: 12,
|
||||
MAX_FILE_SIZE: 50 * 1024 * 1024,
|
||||
});
|
||||
Reference in New Issue
Block a user