Шаги 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
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { db, withSuperAdmin } from "../db";
|
|
import { sql } from "drizzle-orm";
|
|
import { logger } from "../utils/logger";
|
|
|
|
/**
|
|
* Retention журнала ошибок API (таблица error_logs, см. shared/schema.ts).
|
|
*
|
|
* Раз в сутки удаляет записи старше RETENTION_DAYS дней. Без отдельной
|
|
* миграции: таблица и индексы уже существуют. Таймер unref'нут, чтобы
|
|
* не удерживать процесс (и не мешать тестам завершаться).
|
|
* Первый запуск — сразу при старте, дальше — каждые 24 часа.
|
|
*/
|
|
|
|
const log = logger("error-logs-retention");
|
|
|
|
const TICK_MS = 24 * 60 * 60 * 1000; // раз в сутки
|
|
const RETENTION_DAYS = 30;
|
|
|
|
let isRunning = false;
|
|
|
|
async function cleanup(): Promise<void> {
|
|
// withSuperAdmin — единая точка для фоновых задач вне контекста организации
|
|
const result = await withSuperAdmin(() =>
|
|
db.execute(sql`
|
|
DELETE FROM error_logs
|
|
WHERE created_at < now() - make_interval(days => ${RETENTION_DAYS})
|
|
`)
|
|
);
|
|
const deleted = (result as { rowCount?: number }).rowCount ?? 0;
|
|
if (deleted > 0) {
|
|
log.info(`Удалено устаревших записей error_logs: ${deleted} (старше ${RETENTION_DAYS} дней)`);
|
|
} else {
|
|
log.debug(`Устаревших записей error_logs нет (старше ${RETENTION_DAYS} дней)`);
|
|
}
|
|
}
|
|
|
|
export function startErrorLogsRetention(): void {
|
|
if (isRunning) return;
|
|
isRunning = true;
|
|
|
|
const timer = setInterval(() => {
|
|
cleanup().catch((err) => log.error("Cleanup error:", err));
|
|
}, TICK_MS);
|
|
timer.unref?.();
|
|
|
|
// Первый прогон сразу после старта (в фоне, не блокируя listen)
|
|
cleanup().catch((err) => log.error("Initial cleanup error:", err));
|
|
|
|
log.info(`started (retention ${RETENTION_DAYS} дней, тик раз в сутки)`);
|
|
}
|