feat(admin): manual cleanup of stale tus upload sessions + visibility

Adds an admin-driven sweep on top of the existing 24h auto-expire so
operators can see and reap abandoned `.tus/` sessions without waiting.

- storageJanitor: extract `walkTusDir(predicate)` helper, add
  `getStaleTusInfo` + `cleanupStaleTusSessions(thresholdMs, dryRun)`;
  refactor `cleanupTusStragglers` to delegate while preserving its
  janitor-tick `{ removed }` contract.
- StorageStats gains `staleTusSessions` + `staleTusSize` (fixed 1h
  display threshold).
- New `POST /api/admin/storage/cleanup-tus` route with
  `maxAgeHours` validation (positive finite number, default 1) and
  `dryRun` support; admin-gated.
- StoragePanel: 6th overview card "Stale Uploads" + new cleanup
  subsection mirroring the media-cleanup pattern (preview-then-clean
  with shared result panel styling).
- Tests: 8 new janitor tests covering empty dir, threshold filtering,
  dry-run vs live, oldest-mtime tracking, subdir skipping, and the
  override path on the existing straggler sweep. New
  `routes/admin.test.ts` covers auth/admin gates, validation (zero,
  negative, NaN), default `maxAgeHours`, dry-run vs live unlink.
- Docs: `uploads.md` §Janitor expanded to the full lifecycle (cancel
  DELETE, discard DELETE, auto-expire, straggler sweep, admin route);
  `admin.md` Storage Management updated with the new endpoint and
  StorageStats fields.
This commit is contained in:
Jannis Braun
2026-05-02 18:44:19 +02:00
parent 4e5a440176
commit 2f0940c30b
9 changed files with 635 additions and 27 deletions
+4
View File
@@ -820,6 +820,10 @@ export interface StorageStats {
unlinkedSize: number;
danglingAttachments: number;
danglingSize: number;
/** Count of `.tus/` entries (payloads + sidecars) with mtime older than 1h. */
staleTusSessions: number;
/** Total size in bytes of those stale `.tus/` entries. */
staleTusSize: number;
breakdown: StorageBreakdown[];
}