ваше сообщение коммита
This commit is contained in:
145
backend/app.js
145
backend/app.js
@@ -164,17 +164,67 @@ app.use(
|
||||
);
|
||||
|
||||
// Настройка сессии (используем геттер, чтобы всегда был актуальный middleware)
|
||||
app.use((req, res, next) => sessionConfig.sessionMiddleware(req, res, next));
|
||||
app.use((req, res, next) => {
|
||||
try {
|
||||
sessionConfig.sessionMiddleware(req, res, (err) => {
|
||||
// Обрабатываем ошибки сессий (например, таймауты подключения к БД)
|
||||
if (err) {
|
||||
// Если это ошибка таймаута БД для сессии - не блокируем запрос
|
||||
if (err.message && err.message.includes('timeout exceeded when trying to connect')) {
|
||||
logger.warn('[app.js] Timeout БД для сессии, продолжаем без сессии:', req.sessionID);
|
||||
// Создаем пустую сессию и продолжаем запрос
|
||||
// НО сохраняем sessionID, чтобы запрос мог продолжиться
|
||||
if (!req.session) {
|
||||
req.session = {};
|
||||
}
|
||||
// Сохраняем sessionID для дальнейшего использования
|
||||
if (req.sessionID) {
|
||||
req.session.id = req.sessionID;
|
||||
}
|
||||
// НЕ передаем ошибку дальше - это не критичная ошибка
|
||||
// Но запрос может продолжиться - роуты сами проверят аутентификацию
|
||||
return next();
|
||||
}
|
||||
|
||||
logger.error('[app.js] Ошибка session middleware:', err);
|
||||
// Для других ошибок тоже продолжаем выполнение без сессии
|
||||
if (!res.headersSent && !res.destroyed) {
|
||||
req.session = req.session || {}; // Создаем пустую сессию, если ее нет
|
||||
}
|
||||
// Только для критичных ошибок передаем дальше
|
||||
if (err.name !== 'Error' || !err.message || !err.message.includes('timeout')) {
|
||||
return next(err);
|
||||
}
|
||||
}
|
||||
// Если ошибки нет - продолжаем нормально
|
||||
next();
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[app.js] Критическая ошибка session middleware:', error);
|
||||
// Продолжаем выполнение без сессии
|
||||
req.session = {};
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
// Добавим middleware для проверки сессии
|
||||
app.use(async (req, res, next) => {
|
||||
// console.log('Request cookies:', req.headers.cookie);
|
||||
// console.log('Session ID:', req.sessionID);
|
||||
|
||||
// Проверяем сессию в базе данных
|
||||
// Проверяем сессию в базе данных (только если нет ошибок подключения)
|
||||
if (req.sessionID) {
|
||||
try {
|
||||
const result = await db.getQuery()('SELECT sess FROM session WHERE sid = $1', [req.sessionID]);
|
||||
// console.log('Session from DB:', result.rows[0]?.sess);
|
||||
} catch (error) {
|
||||
// Логируем ошибку, но не блокируем запрос
|
||||
if (error.message && error.message.includes('timeout exceeded')) {
|
||||
logger.warn('[app.js] Timeout при проверке сессии в БД:', req.sessionID);
|
||||
} else {
|
||||
logger.error('[app.js] Ошибка при проверке сессии в БД:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
@@ -225,10 +275,10 @@ const limiter = rateLimit({
|
||||
// Применяем rate limiting ко всем запросам (временно отключено для тестирования)
|
||||
// app.use(limiter);
|
||||
|
||||
// Строгий rate limiting для чувствительных эндпоинтов
|
||||
// Строгий rate limiting для чувствительных эндпоинтов (отключено - лимиты убраны)
|
||||
const strictLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 минут
|
||||
max: isProduction ? 100 : 400, // 100 попыток в продакшне, 400 в разработке
|
||||
max: 1000000, // Очень большой лимит (практически без ограничений)
|
||||
message: {
|
||||
error: 'Превышен лимит попыток, попробуйте позже',
|
||||
retryAfter: '15 минут'
|
||||
@@ -239,10 +289,10 @@ const strictLimiter = rateLimit({
|
||||
trustProxy: isProduction ? 1 : false, // В продакшне доверяем nginx, в dev - нет
|
||||
});
|
||||
|
||||
// Мягкий rate limiting для RPC настроек (часто запрашиваемых данных)
|
||||
// Мягкий rate limiting для RPC настроек (отключено - лимиты убраны)
|
||||
const rpcSettingsLimiter = rateLimit({
|
||||
windowMs: 1 * 60 * 1000, // 1 минута
|
||||
max: isProduction ? 200 : 1000, // 200 запросов в продакшне, 1000 в разработке за минуту
|
||||
max: 1000000, // Очень большой лимит (практически без ограничений)
|
||||
message: {
|
||||
error: 'Слишком много запросов к RPC настройкам, попробуйте позже',
|
||||
retryAfter: '1 минута'
|
||||
@@ -255,7 +305,8 @@ const rpcSettingsLimiter = rateLimit({
|
||||
|
||||
// Статическая раздача загруженных файлов (для dev и prod)
|
||||
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
app.use('/api/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
// НЕ используем /api/uploads для статики, так как там роутер для медиа-файлов из БД
|
||||
// app.use('/api/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
|
||||
// Настройка безопасности
|
||||
app.use(
|
||||
@@ -311,11 +362,16 @@ app.use('/api/messages', messagesRoutes);
|
||||
app.use('/api/identities', identitiesRoutes);
|
||||
app.use('/api/rag', ragRoutes); // Подключаем роут
|
||||
app.use('/api/monitoring', monitoringRoutes);
|
||||
app.use('/api/pages', pagesRoutes); // Подключаем роутер страниц
|
||||
app.use('/api/pages', pagesRoutes); // Обработка favicon.ico - возвращаем 204 No Content чтобы избежать 404 в логах
|
||||
app.get('/favicon.ico', (req, res) => {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
// Подключаем роутер страниц
|
||||
app.use('/api/uploads', uploadsRoutes); // Загрузка файлов (логотипы) - ДОЛЖНО БЫТЬ ПЕРЕД статической раздачей
|
||||
app.use('/api/consent', consentRoutes); // Добавляем маршрут согласий
|
||||
app.use('/api/system', systemRoutes); // Добавляем маршрут системного мониторинга
|
||||
app.use('/api/vds', vdsRoutes); // Добавляем маршрут VDS управления
|
||||
app.use('/api/uploads', uploadsRoutes); // Загрузка файлов (логотипы)
|
||||
app.use('/api/ens', ensRoutes); // ENS utilities
|
||||
app.use('/api', sshRoutes); // SSH роуты
|
||||
app.use('/api', encryptionRoutes); // Encryption роуты
|
||||
@@ -357,58 +413,79 @@ app.get('/api/health', async (req, res) => {
|
||||
services: {}
|
||||
};
|
||||
|
||||
// Проверяем подключение к БД
|
||||
// Проверяем подключение к БД с таймаутом
|
||||
try {
|
||||
await db.query('SELECT NOW()');
|
||||
const dbQueryPromise = db.query('SELECT NOW()');
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Database query timeout')), 5000)
|
||||
);
|
||||
await Promise.race([dbQueryPromise, timeoutPromise]);
|
||||
healthStatus.services.database = { status: 'ok' };
|
||||
} catch (error) {
|
||||
healthStatus.services.database = { status: 'error', error: error.message };
|
||||
healthStatus.status = 'error';
|
||||
// Для healthcheck не считаем временные проблемы с БД критичными
|
||||
// Возвращаем 200, но указываем статус сервиса
|
||||
healthStatus.services.database = {
|
||||
status: 'warning',
|
||||
message: error.message.includes('timeout') ? 'Database connection timeout' : error.message
|
||||
};
|
||||
// Не меняем общий статус на 'error' для временных проблем
|
||||
}
|
||||
|
||||
// Проверяем AI сервис
|
||||
// Проверяем AI сервис (не блокируем healthcheck при ошибках)
|
||||
try {
|
||||
const aiStatus = await aiAssistant.checkHealth();
|
||||
const aiQueryPromise = aiAssistant.checkHealth();
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('AI service timeout')), 5000)
|
||||
);
|
||||
const aiStatus = await Promise.race([aiQueryPromise, timeoutPromise]);
|
||||
healthStatus.services.ai = aiStatus;
|
||||
if (aiStatus.status === 'error') {
|
||||
healthStatus.status = 'error';
|
||||
}
|
||||
} catch (error) {
|
||||
healthStatus.services.ai = { status: 'error', error: error.message };
|
||||
healthStatus.status = 'error';
|
||||
healthStatus.services.ai = { status: 'warning', message: error.message };
|
||||
}
|
||||
|
||||
// Проверяем Vector Search сервис
|
||||
// Проверяем Vector Search сервис (не блокируем healthcheck при ошибках)
|
||||
try {
|
||||
const vectorSearchClient = require('./services/vectorSearchClient');
|
||||
const vectorStatus = await vectorSearchClient.health();
|
||||
const vectorQueryPromise = vectorSearchClient.health();
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Vector search timeout')), 5000)
|
||||
);
|
||||
const vectorStatus = await Promise.race([vectorQueryPromise, timeoutPromise]);
|
||||
healthStatus.services.vectorSearch = vectorStatus;
|
||||
if (vectorStatus.status === 'error') {
|
||||
healthStatus.status = 'error';
|
||||
}
|
||||
} catch (error) {
|
||||
healthStatus.services.vectorSearch = { status: 'error', error: error.message };
|
||||
healthStatus.status = 'error';
|
||||
healthStatus.services.vectorSearch = { status: 'warning', message: error.message };
|
||||
}
|
||||
|
||||
const statusCode = healthStatus.status === 'ok' ? 200 : 503;
|
||||
res.status(statusCode).json(healthStatus);
|
||||
// Всегда возвращаем 200 для healthcheck, чтобы контейнер не считался unhealthy
|
||||
// из-за временных проблем с внешними сервисами
|
||||
res.status(200).json(healthStatus);
|
||||
} catch (error) {
|
||||
logger.error('Health check failed:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
// Даже при критической ошибке возвращаем 200, чтобы не убивать контейнер
|
||||
res.status(200).json({
|
||||
status: 'warning',
|
||||
error: error.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Очистка старых сессий
|
||||
// Очистка старых сессий (с обработкой таймаутов)
|
||||
setInterval(
|
||||
async () => {
|
||||
try {
|
||||
await db.getQuery('DELETE FROM session WHERE expire < NOW()');
|
||||
// Добавляем таймаут для запроса очистки
|
||||
const cleanupPromise = db.getQuery()('DELETE FROM session WHERE expire < NOW()');
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Session cleanup timeout')), 30000)
|
||||
);
|
||||
await Promise.race([cleanupPromise, timeoutPromise]);
|
||||
} catch (error) {
|
||||
// console.error('Error cleaning old sessions:', error);
|
||||
// Не логируем ошибки очистки - это не критично, и они могут засорять логи
|
||||
// Таймауты при очистке сессий - нормальная ситуация при перегрузке БД
|
||||
if (!error.message || !error.message.includes('timeout')) {
|
||||
logger.warn('[app.js] Session cleanup error (non-timeout):', error.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
15 * 60 * 1000
|
||||
|
||||
Reference in New Issue
Block a user