Checkpoint: Ajout de la prochaine date d'exécution dynamique dans les Paramètres (calcul frontend selon mode/heure/jour, aligné sur le cron réel backend). Ajout de la règle de rétention configurable (Illimité/3/6/12/18/24 mois) avec purge automatique au démarrage et après chaque import. 0 erreur TypeScript, 21 tests passés.

This commit is contained in:
Manus
2026-07-05 07:51:02 -04:00
parent f0be15d432
commit 55f3d5a5d4
6 changed files with 215 additions and 6 deletions

View File

@@ -12,7 +12,7 @@ import { runFullImport } from "../importer";
import uploadRoutes from "../uploadRoutes";
import scheduledRoutes from "../scheduledRoutes";
import { ensureAdminExists } from "../localAuth";
import { getSetting } from "../db";
import { getSetting, purgeOldArticles } from "../db";
import { runRssFetch } from "../rssEngine";
function isPortAvailable(port: number): Promise<boolean> {
@@ -91,6 +91,19 @@ export async function scheduleDailyImport() {
} catch (e) {
console.error("[Cron] Erreur lors de la lecture RSS:", e);
}
// 3. Purge des articles selon la règle de rétention
try {
const retentionStr = await getSetting("retention_months");
const retentionMonths = retentionStr ? parseInt(retentionStr, 10) : 0;
if (retentionMonths > 0) {
const purged = await purgeOldArticles(retentionMonths);
if (purged.veille > 0 || purged.aap > 0) {
console.log(`[Cron] Purge rétention (${retentionMonths} mois) — Veille: -${purged.veille} | AAP: -${purged.aap}`);
}
}
} catch (e) {
console.error("[Cron] Erreur lors de la purge de rétention:", e);
}
});
}
@@ -136,6 +149,15 @@ async function startServer() {
try {
await ensureAdminExists();
await scheduleDailyImport();
// Purge de rétention au démarrage
const retentionStr = await getSetting("retention_months");
const retentionMonths = retentionStr ? parseInt(retentionStr, 10) : 0;
if (retentionMonths > 0) {
const purged = await purgeOldArticles(retentionMonths);
if (purged.veille > 0 || purged.aap > 0) {
console.log(`[Init] Purge rétention (${retentionMonths} mois) — Veille: -${purged.veille} | AAP: -${purged.aap}`);
}
}
} catch (e) {
console.error("[Init] Erreur d'initialisation:", e);
}

View File

@@ -444,6 +444,19 @@ export async function saveRssSettings(data: Partial<Omit<InsertRssSettings, "id"
}
// ─── Purge ───────────────────────────────────────────────────────────────────
export async function purgeOldArticles(retentionMonths: number): Promise<{ veille: number; aap: number }> {
const db = await getDb();
if (!db) throw new Error("Database not available");
const cutoff = new Date();
cutoff.setMonth(cutoff.getMonth() - retentionMonths);
const veilleResult = await db.delete(veilleItems).where(lte(veilleItems.importedAt, cutoff));
const aapResult = await db.delete(aapItems).where(lte(aapItems.importedAt, cutoff));
return {
veille: (veilleResult as any).affectedRows ?? 0,
aap: (aapResult as any).affectedRows ?? 0,
};
}
export async function purgeVeilleItems(): Promise<number> {
const db = await getDb();
if (!db) throw new Error("Database not available");

View File

@@ -376,6 +376,7 @@ export const appRouter = router({
fetch_interval_minutes: z.string().optional(),
fetch_day_of_week: z.string().optional(),
fetch_day_of_month: z.string().optional(),
retention_months: z.string().optional(),
})
)
.mutation(async ({ input }) => {