Initial
This commit is contained in:
66
server/index.js
Normal file
66
server/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
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';
|
||||
|
||||
// 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();
|
||||
});
|
||||
Reference in New Issue
Block a user