feat(p10): archivage automatique des sessions terminées (rétention configurable)
- 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)
This commit is contained in:
@@ -24,6 +24,14 @@ import {
|
||||
readClaudeBinPath,
|
||||
readClaudeHome,
|
||||
} from '../core/claude-settings.js';
|
||||
import {
|
||||
PURGE_DAYS_KEY,
|
||||
RETENTION_DAYS_KEY,
|
||||
normalizePurgeDays,
|
||||
normalizeRetentionDays,
|
||||
readPurgeDays,
|
||||
readRetentionDays,
|
||||
} from '../core/retention-settings.js';
|
||||
|
||||
// Réglages modifiables via l'API (allow-list stricte). Les secrets ne figurent JAMAIS ici.
|
||||
export function registerSettingsRoutes(
|
||||
@@ -51,6 +59,8 @@ export function registerSettingsRoutes(
|
||||
scanIntervalMin: readScanIntervalMin(db),
|
||||
claudeBinPath: readClaudeBinPath(db),
|
||||
claudeHome: readClaudeHome(db),
|
||||
retentionDays: readRetentionDays(db),
|
||||
purgeDays: readPurgeDays(db),
|
||||
},
|
||||
server: serverInfo(),
|
||||
});
|
||||
@@ -87,6 +97,20 @@ export function registerSettingsRoutes(
|
||||
}
|
||||
setSetting(db, CLAUDE_HOME_KEY, value);
|
||||
}
|
||||
if ('retentionDays' in body) {
|
||||
const value = normalizeRetentionDays(body.retentionDays);
|
||||
if (value === null) {
|
||||
return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'retentionDays must be 0 (never) or an integer between 1 and 3650' } });
|
||||
}
|
||||
setSetting(db, RETENTION_DAYS_KEY, String(value));
|
||||
}
|
||||
if ('purgeDays' in body) {
|
||||
const value = normalizePurgeDays(body.purgeDays);
|
||||
if (value === null) {
|
||||
return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'purgeDays must be 0 (disabled) or an integer between 1 and 3650' } });
|
||||
}
|
||||
setSetting(db, PURGE_DAYS_KEY, String(value));
|
||||
}
|
||||
recordAudit(db, {
|
||||
actor: req.authContext?.tokenId ?? 'unknown',
|
||||
action: 'settings.update',
|
||||
|
||||
Reference in New Issue
Block a user