Fix: убран process.exit(1) — приложение не падает, показывает сообщение об ошибке. Версия 0.1.14

This commit is contained in:
2026-07-08 21:20:43 +04:00
parent 771473c97a
commit 1b902c5540
3 changed files with 32 additions and 26 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ipwhitelist", "name": "ipwhitelist",
"version": "0.1.13", "version": "0.1.14",
"description": "IP WhiteList microservice for cloud provider", "description": "IP WhiteList microservice for cloud provider",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
+30 -24
View File
@@ -61,23 +61,16 @@ async function start() {
const { pool } = require('./src/db'); const { pool } = require('./src/db');
app.use(createSessionMiddleware(pool)); app.use(createSessionMiddleware(pool));
// ── Fail-fast в production ──────────────────────────────────────────────── // ── Fail-fast в production (без exit — сообщение в лог) ────────────────────
// Если секреты не заданы — приложение в проде немедленно падает. const DEFAULT_SESSION = 'dev-session-secret-change-me';
// Дефолтные значения — только для локальной разработки. let fatalError = null;
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
const DEFAULT_SESSION = 'dev-session-secret-change-me'; if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION)
if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION) { fatalError = 'SESSION_SECRET не задан или равен дефолту в production';
console.error('FATAL: SESSION_SECRET не задан или равен дефолту в production!'); else if (!process.env.DB_PASS)
process.exit(1); fatalError = 'DB_PASS не задан в production';
} else if (!process.env.CSRF_SECRET || process.env.CSRF_SECRET === 'dev-csrf-secret-change-in-prod')
if (!process.env.DB_PASS) { fatalError = 'CSRF_SECRET не задан или равен дефолту в production';
console.error('FATAL: DB_PASS не задан в production!');
process.exit(1);
}
if (!process.env.CSRF_SECRET || process.env.CSRF_SECRET === 'dev-csrf-secret-change-in-prod') {
console.error('FATAL: CSRF_SECRET не задан или равен дефолту в production!');
process.exit(1);
}
} }
// k8s liveness + readiness probes // k8s liveness + readiness probes
@@ -91,6 +84,15 @@ async function start() {
} }
}); });
// Если фатальная ошибка конфигурации — показываем на всех страницах сообщение
if (fatalError) {
console.error(fatalError);
app.use((req, res) => {
res.status(503).type('html').send(errorPage(fatalError));
});
return app;
}
// JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме) // JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме)
if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler); if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);
@@ -136,20 +138,24 @@ async function start() {
if (require.main === module) { if (require.main === module) {
start() start()
.then(a => { .then(a => {
const server = a.listen(PORT, () => console.log('Server on port ' + PORT)); a.listen(PORT, () => console.log('Server on port ' + PORT));
// Graceful shutdown по SIGTERM (k8s) // Graceful shutdown по SIGTERM (k8s)
process.on('SIGTERM', () => { process.on('SIGTERM', () => {
console.log('SIGTERM — shutting down...'); console.log('SIGTERM — shutting down...');
server.close(() => { const { pool } = require('./src/db');
console.log('HTTP closed'); pool.end().then(() => { console.log('DB pool closed'); process.exit(0); });
const { pool } = require('./src/db');
pool.end().then(() => { console.log('DB pool closed'); process.exit(0); });
});
setTimeout(() => { console.log('Forced exit'); process.exit(0); }, 10000); setTimeout(() => { console.log('Forced exit'); process.exit(0); }, 10000);
}); });
return server;
}) })
.catch(e => { console.error('Startup error:', e); process.exit(1); }); .catch(e => {
console.error('Startup error:', e.message);
// Не падаем — запускаем HTTP с сообщением об ошибке
const errorApp = require('express')();
errorApp.use((req, res) => {
res.status(503).type('html').send('<html><head><meta charset="utf-8"><title>Авария</title></head><body style="font-family:sans-serif;padding:40px;text-align:center"><h1>⚙️ Сервис временно недоступен</h1><p>Причина: ' + (e.message || 'неизвестна') + '</p><p>Попробуйте обновить страницу через минуту</p></body></html>');
});
errorApp.listen(PORT, () => console.log('Server on port ' + PORT + ' (ERROR MODE)'));
});
} }
module.exports = { start }; module.exports = { start };
+1 -1
View File
@@ -8,7 +8,7 @@
// ═══════════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════════
module.exports = { module.exports = {
version: '0.1.13', version: '0.1.14',
// ── Среда ───────────────────────────────────────────────────────────────── // ── Среда ─────────────────────────────────────────────────────────────────
// APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API) // APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API)