From 1b902c554065176ad69d3fb4ce2bf06f4895857d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 8 Jul 2026 21:20:43 +0400 Subject: [PATCH] =?UTF-8?q?Fix:=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=20process?= =?UTF-8?q?.exit(1)=20=E2=80=94=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B5=20=D0=BF=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D0=B5=D1=82,=20=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D0=B5=D1=82=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=20=D0=BE=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BA=D0=B5.=20=D0=92=D0=B5=D1=80=D1=81=D0=B8=D1=8F=200.1.14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- server.js | 54 +++++++++++++++++++++++------------------- v2/src/config/index.js | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 358049e..ae76068 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipwhitelist", - "version": "0.1.13", + "version": "0.1.14", "description": "IP WhiteList microservice for cloud provider", "main": "server.js", "scripts": { diff --git a/server.js b/server.js index a4b1d2b..21f6f35 100644 --- a/server.js +++ b/server.js @@ -61,23 +61,16 @@ async function start() { const { pool } = require('./src/db'); 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') { - const DEFAULT_SESSION = 'dev-session-secret-change-me'; - if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION) { - console.error('FATAL: SESSION_SECRET не задан или равен дефолту в production!'); - process.exit(1); - } - if (!process.env.DB_PASS) { - 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); - } + if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION) + fatalError = 'SESSION_SECRET не задан или равен дефолту в production'; + else if (!process.env.DB_PASS) + fatalError = 'DB_PASS не задан в production'; + else if (!process.env.CSRF_SECRET || process.env.CSRF_SECRET === 'dev-csrf-secret-change-in-prod') + fatalError = 'CSRF_SECRET не задан или равен дефолту в production'; } // 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-режиме) if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler); @@ -136,20 +138,24 @@ async function start() { if (require.main === module) { start() .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) process.on('SIGTERM', () => { console.log('SIGTERM — shutting down...'); - server.close(() => { - console.log('HTTP closed'); - const { pool } = require('./src/db'); - 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); }); - 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('Авария

⚙️ Сервис временно недоступен

Причина: ' + (e.message || 'неизвестна') + '

Попробуйте обновить страницу через минуту

'); + }); + errorApp.listen(PORT, () => console.log('Server on port ' + PORT + ' (ERROR MODE)')); + }); } module.exports = { start }; diff --git a/v2/src/config/index.js b/v2/src/config/index.js index 8da03aa..428beee 100644 --- a/v2/src/config/index.js +++ b/v2/src/config/index.js @@ -8,7 +8,7 @@ // ═══════════════════════════════════════════════════════════════════════════════ module.exports = { - version: '0.1.13', + version: '0.1.14', // ── Среда ───────────────────────────────────────────────────────────────── // APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API)