Files
poshmanager/server/index.js
2026-06-25 20:57:09 -05:00

71 lines
2.7 KiB
JavaScript

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import { config } from './config.js';
import { migrate, seed } from './db.js';
import { requestLogger } from './middleware/requestLogger.js';
import { apiRoutes } from './routes/index.js';
import { logger } from './services/logger.js';
import { effectiveTrustedOrigins } from './models/settingsModel.js';
import { startCatalogWorker } from './services/catalogWorker.js';
import { startPromotionWorker } from './services/promotionWorker.js';
import { startHostGroupWorker } from './services/hostGroupWorker.js';
import { startRunPlanScheduleWorker } from './services/runPlanScheduleWorker.js';
// Fail fast in production rather than ship with well-known default secrets.
// These defaults are fine for local development but are publicly known, so a
// production deployment that kept them would have forgeable sessions and
// recoverable credential ciphertext.
function assertProductionSecrets() {
if (!config.isProduction) return;
const insecure = [];
if (!process.env.JWT_SECRET || config.jwtSecret === 'dev-only-change-me') insecure.push('JWT_SECRET');
if (!process.env.CREDENTIAL_STORE_KEY || config.credentialStoreKey === 'dev-only-credential-key') {
insecure.push('CREDENTIAL_STORE_KEY');
}
if (config.defaultAdminPassword === 'change-me-now') insecure.push('DEFAULT_ADMIN_PASSWORD');
if (insecure.length) {
logger.error('Refusing to start in production with insecure default secrets', { insecure });
throw new Error(`Set non-default values for: ${insecure.join(', ')} before starting in production.`);
}
}
assertProductionSecrets();
migrate();
seed();
const app = express();
// The API always runs behind the Vite proxy (and typically a reverse proxy in
// production), so honor X-Forwarded-* to get the real client IP for rate
// limiting and logging.
app.set('trust proxy', true);
app.use(helmet({ contentSecurityPolicy: false }));
app.use(cors({
origin(origin, callback) {
// Read the effective allow-list per request so admin edits to
// `trusted_origins` in the Config screen take effect without a restart.
if (!origin || effectiveTrustedOrigins().includes(origin)) return callback(null, true);
return callback(new Error(`Origin ${origin} is not allowed by CORS`));
},
credentials: true
}));
app.use(express.json({ limit: config.jsonLimit }));
app.use(requestLogger);
app.use('/api', apiRoutes);
app.use((error, req, res, next) => {
logger.error('unhandled error', { error });
res.status(500).json({ error: 'Unexpected server error' });
});
app.listen(config.port, () => {
logger.info(`POSHManager API listening on ${config.port}`);
startCatalogWorker();
startPromotionWorker();
startHostGroupWorker();
startRunPlanScheduleWorker();
});