- DB: migration #10 (sessions.archived_at + index), helpers archive/unarchive/archiveExpired (soft-archive, jamais de DELETE) - core/retention-settings.ts: session_retention_days (défaut 30, 0=jamais, 1-3650) + session_purge_days (off) - core/session-archive.ts: scheduler start/stop/sweep (calqué DiscoveryService), câblé dans runDaemon - pty-manager.list({includeArchived}) + SessionSummary.archived + emitHistoricalUpdate - routes: GET /sessions?includeArchived, POST/DELETE /sessions/:id/archive, POST /sessions/archive-now - protocole additif: message WS session_archived (relayé par la gateway, abonnés 'sessions') - settings: retentionDays/purgeDays exposés et validés dans PATCH /settings - web: store sessions (showArchived + archive/unarchive), SessionsListView (toggle/badge/actions), SettingsView (slider rétention), i18n EN+FR - tests: retention-settings + session-archive (vitest) + settings-routes étendu ; acceptance-p10.mjs (sweep, event WS, resume d'une session archivée → 201, rétention=0)
49 lines
2.1 KiB
TypeScript
49 lines
2.1 KiB
TypeScript
// Réglages de rétention des sessions terminées (archivage auto), persistés dans `settings`.
|
|
// Frontière de sécurité : clés NON sensibles, n'entrent dans l'allow-list du PATCH /api/v1/settings
|
|
// que via les validateurs ci-dessous. Calqué sur scan-settings.ts.
|
|
import { getSetting } from '../db/index.js';
|
|
import type { Db } from '../db/index.js';
|
|
|
|
export const RETENTION_DAYS_KEY = 'session_retention_days';
|
|
export const PURGE_DAYS_KEY = 'session_purge_days';
|
|
|
|
/** Au-delà de N jours après la fin, une session managée est auto-archivée. 0 = jamais. */
|
|
export const DEFAULT_RETENTION_DAYS = 30;
|
|
/** Purge définitive (DELETE) après N jours. 0 = désactivée (conçue mais OFF par défaut). */
|
|
export const DEFAULT_PURGE_DAYS = 0;
|
|
export const MIN_RETENTION_DAYS = 1;
|
|
export const MAX_RETENTION_DAYS = 3650;
|
|
|
|
/** Rétention en jours (0 = jamais archiver). Défaut DEFAULT_RETENTION_DAYS. Lecture tolérante. */
|
|
export function readRetentionDays(db: Db): number {
|
|
return readDaysSetting(db, RETENTION_DAYS_KEY, DEFAULT_RETENTION_DAYS);
|
|
}
|
|
|
|
/** Purge définitive en jours (0 = désactivée). Défaut DEFAULT_PURGE_DAYS. Lecture tolérante. */
|
|
export function readPurgeDays(db: Db): number {
|
|
return readDaysSetting(db, PURGE_DAYS_KEY, DEFAULT_PURGE_DAYS);
|
|
}
|
|
|
|
function readDaysSetting(db: Db, key: string, fallback: number): number {
|
|
const raw = getSetting(db, key);
|
|
if (raw === null) return fallback;
|
|
const n = Number(raw);
|
|
return isValidDays(n) ? n : fallback;
|
|
}
|
|
|
|
/**
|
|
* Valide un nombre de jours : entier `0` (= désactivé) OU dans [MIN, MAX]. Retourne null si invalide
|
|
* (⇒ 400). Le même validateur sert pour la rétention et la purge (mêmes bornes, même sémantique de 0).
|
|
*/
|
|
export function normalizeRetentionDays(raw: unknown): number | null {
|
|
return typeof raw === 'number' && isValidDays(raw) ? raw : null;
|
|
}
|
|
|
|
/** Alias sémantique pour la purge (mêmes règles). */
|
|
export const normalizePurgeDays = normalizeRetentionDays;
|
|
|
|
function isValidDays(n: number): boolean {
|
|
if (!Number.isInteger(n)) return false;
|
|
return n === 0 || (n >= MIN_RETENTION_DAYS && n <= MAX_RETENTION_DAYS);
|
|
}
|