chore(ci+logs): eslint в CI, npm audit, logger с уровнями, retention error_logs
Шаги 1.6 и 1.7 плана production-готовности: - eslint flat-config (баг-ловушки, легаси warn), lint блокирующий в pr-check - npm audit --audit-level=high в CI (отчёт) - фикс реального бага: условный useRef в TaskTitleInline - server/utils/logger.ts (LOG_LEVEL/LOG_FORMAT) в 5 горячих местах - error_logs retention 30 дней (worker), redactSensitive в captureErrorLog - 0 errors lint, vitest 96/96
This commit is contained in:
@@ -8,6 +8,7 @@ import { notificationService } from "./services/notification.service";
|
||||
import { webPushService } from "./services/web-push.service";
|
||||
import { startMedScheduleWorker } from "./medschedule/worker";
|
||||
import { startAutomationScheduler } from "./workers/automation-scheduler";
|
||||
import { startErrorLogsRetention } from "./workers/error-logs-retention";
|
||||
import { startGpsWorker } from "./gps/worker";
|
||||
import { storage } from "./storage";
|
||||
import type { ReminderRecipient } from "@shared/schema";
|
||||
@@ -23,6 +24,10 @@ import { fileUploads, errorLogs } from "@shared/schema";
|
||||
import { authenticateToken, authenticateFileToken, type AuthenticatedRequest } from "./middleware/auth.middleware";
|
||||
import { EXT_TO_MIME, getFileExt } from "./utils/upload";
|
||||
import crypto from "crypto";
|
||||
import { logger } from "./utils/logger";
|
||||
|
||||
// Единый логгер для горячих мест index.ts (остальной файл исторически использует console/log из vite.ts)
|
||||
const errLog = logger("express");
|
||||
|
||||
const app = express();
|
||||
// Configure Express to trust proxy for accurate client IP identification
|
||||
@@ -303,13 +308,24 @@ app.use((req, res, next) => {
|
||||
const ERROR_LOG_EXCLUDED_PATHS = new Set(['/api/health', '/api/events']);
|
||||
const errorLogDedupe = new Map<string, number>();
|
||||
|
||||
// Чувствительные данные не должны попадать в error_logs даже случайно:
|
||||
// если тело ошибки содержит JSON с паролями/токенами — значения маскируем.
|
||||
const SENSITIVE_KEY_RE = /("(?:password|token|access_token|refresh_token|secret|api_key|apiKey|authorization)"\s*:\s*")[^"]*(")/gi;
|
||||
const BEARER_RE = /Bearer\s+[A-Za-z0-9\-._~+/=]+/gi;
|
||||
|
||||
function redactSensitive(text: string): string {
|
||||
return text
|
||||
.replace(SENSITIVE_KEY_RE, '$1***$2')
|
||||
.replace(BEARER_RE, 'Bearer ***');
|
||||
}
|
||||
|
||||
function captureErrorLog(req: Request, statusCode: number, path: string, body?: Record<string, any>) {
|
||||
try {
|
||||
if (statusCode < 400 || statusCode === 401) return;
|
||||
if (ERROR_LOG_EXCLUDED_PATHS.has(path)) return;
|
||||
|
||||
const rawMessage = (body && (body.error || body.message)) || '';
|
||||
const message = String(rawMessage).slice(0, 2000) || `HTTP ${statusCode}`;
|
||||
const message = redactSensitive(String(rawMessage).slice(0, 2000)) || `HTTP ${statusCode}`;
|
||||
|
||||
const key = `${req.method} ${path} ${statusCode} ${message.slice(0, 200)}`;
|
||||
const now = Date.now();
|
||||
@@ -1077,13 +1093,13 @@ async function runStartupDataPatches() {
|
||||
const status = err.status || err.statusCode || 500;
|
||||
|
||||
if (res.headersSent) {
|
||||
console.error('[express] Error after headers sent:', err);
|
||||
errLog.error('Error after headers sent:', err);
|
||||
return;
|
||||
}
|
||||
if (status >= 500) {
|
||||
// Клиенту — только нейтральный текст и id инцидента; детали (SQL, стек) — в серверный лог
|
||||
const requestId = crypto.randomUUID().slice(0, 8);
|
||||
console.error(`[express] Server error [${requestId}]:`, err);
|
||||
errLog.error(`Server error [${requestId}]:`, err);
|
||||
res.status(status).json({ message: `Внутренняя ошибка сервера (код ${requestId})` });
|
||||
return;
|
||||
}
|
||||
@@ -1112,6 +1128,10 @@ async function runStartupDataPatches() {
|
||||
startAutomationScheduler();
|
||||
log('Automation scheduler started');
|
||||
|
||||
// Start error_logs retention (daily cleanup of API error log entries older than 30 days)
|
||||
startErrorLogsRetention();
|
||||
log('Error logs retention started');
|
||||
|
||||
// Start GPS worker (offline detection for tracked assets)
|
||||
startGpsWorker();
|
||||
log('GPS worker started');
|
||||
|
||||
Reference in New Issue
Block a user