Шаги 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
57 lines
1.9 KiB
TypeScript
57 lines
1.9 KiB
TypeScript
import { withSuperAdmin } from "../db";
|
|
import { gpsStorage } from "./storage";
|
|
import { notifyOffline } from "./geozone.service";
|
|
import { eventBus } from "../routes/shared";
|
|
import { logger } from "../utils/logger";
|
|
|
|
/**
|
|
* Воркер офлайн-детекта GPS-объектов.
|
|
* Раз в минуту ищет активные онлайн-объекты, от которых не было точек
|
|
* более 15 минут, переводит их в offline и уведомляет подписчиков (on_offline).
|
|
* Обратный переход в online происходит в processPosition при новой точке.
|
|
* Работает независимо от Traccar (PWA-трекинг тоже покрывается).
|
|
*/
|
|
|
|
const log = logger("GPS Worker");
|
|
|
|
const TICK_MS = 60_000;
|
|
const OFFLINE_THRESHOLD_MS = 15 * 60_000;
|
|
|
|
let isRunning = false;
|
|
|
|
async function tick(): Promise<void> {
|
|
const cutoff = new Date(Date.now() - OFFLINE_THRESHOLD_MS);
|
|
const staleAssets = await withSuperAdmin(() => gpsStorage.getStaleOnlineAssets(cutoff));
|
|
|
|
for (const asset of staleAssets) {
|
|
try {
|
|
await withSuperAdmin(async () => {
|
|
await gpsStorage.setAssetOnline(asset.id, false);
|
|
await notifyOffline(asset);
|
|
});
|
|
|
|
// SSE — чтобы открытые карты сразу «погасили» маркер
|
|
eventBus.publishEvent({
|
|
type: "gps_asset_offline",
|
|
data: { assetId: asset.id },
|
|
organizationId: asset.organizationId,
|
|
});
|
|
|
|
log.info(`Объект ${asset.id} ('${asset.name}', org ${asset.organizationId}) переведён в offline`);
|
|
} catch (err) {
|
|
log.error(`Ошибка обработки объекта ${asset.id}:`, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function startGpsWorker(): void {
|
|
if (isRunning) return;
|
|
isRunning = true;
|
|
|
|
setInterval(() => {
|
|
tick().catch((err) => log.error("Tick error:", err));
|
|
}, TICK_MS);
|
|
|
|
log.info("started");
|
|
}
|