feat(scheduler): automatisations planifiées (cron) — tâche 5

- table schedules (migration 0007) + service scheduler (croner) : CRUD,
  runSchedule avec scope (all/liste), pool de concurrence et verrou par machine,
  mapping actions → refresh/metrics/docker_scan ; reloadSchedules au boot
- worker = reloadSchedules (remplace le refresh 30 min en dur)
- routes /api/schedules (CRUD + :id/run) ; cron invalide rejeté (validation croner)
- UI Paramètres : onglet « Automatisations » (liste, activer/lancer/supprimer, création)

tsc 0 · 113 tests · build OK · boot OK (migration 0007, CRUD vérifié).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 19:25:44 +02:00
parent bdbe7af55c
commit ff9cfaa9e1
11 changed files with 2796 additions and 18 deletions
+4 -17
View File
@@ -1,24 +1,11 @@
// server/jobs/worker.ts
import { Cron } from "croner";
import { listMachines } from "../services/machines.js";
import { refreshMachine } from "../services/refresh.js";
import { reloadSchedules, stopSchedules } from "../services/scheduler.js";
let job: Cron | null = null;
/** Rafraîchit toutes les machines toutes les 30 minutes (tâche de fond). */
/** Démarre le planificateur : enregistre les automatisations actives (cron) depuis la BDD. */
export function startWorker(): void {
job = new Cron("*/30 * * * *", async () => {
for (const m of listMachines()) {
try {
await refreshMachine(m.id);
} catch (err) {
console.error(`[worker] refresh échoué pour ${m.id}:`, (err as Error).message);
}
}
});
reloadSchedules();
}
export function stopWorker(): void {
job?.stop();
job = null;
stopSchedules();
}