Files
HealthLog/src/types/backups.ts
T
Marc-André Bombeck fea1d1e0b5 chore: strip planning tags from comments and enable the rule
Output of `node scripts/strip-planning-tags.mjs --write`: 3,157 comment
lines in 1,308 files lose the work-item identifier at their head. Release
versions and issue references stay; nothing outside comment text
changes, which the script checks per file against a comment-free
printout. healthlog/no-planning-tags is enabled at error level with this
commit, so the tree it lands on is clean.
2026-10-20 09:08:40 +02:00

113 lines
4.2 KiB
TypeScript

/**
* v1.4.41 (org-audit rec #2) — DTOs for `/api/admin/backups`.
*
* Pre-v1.4.41 these interfaces lived inside the route handler at
* `src/app/api/admin/backups/route.ts` and were imported directly from
* the route module by `src/components/admin/backups-section.tsx`. That
* was a textbook layer violation (component → route handler) and the
* only one of its kind in the codebase. Hoisting the shapes here gives
* both sides a route-handler-independent shared type home; the route
* keeps owning the HTTP contract, the component keeps owning the UI.
*/
import type { BackupPassActivity } from "@/lib/jobs/backup-pass-attempts";
import type { BackupScheduleStatus } from "@/lib/jobs/backup-schedule-status";
import type { OffhostBackupFreshness } from "@/lib/jobs/offhost-backup-freshness";
export interface BackupRow {
id: string;
userId: string;
username: string;
type: string;
/**
* Size of the encrypted blob in bytes — useful for capacity planning.
* The blob itself is never returned.
*/
sizeBytes: number;
createdAt: string;
}
/** One account's newest object in the operator's off-host bucket. */
export interface OffhostAccountRow {
userId: string;
username: string;
/**
* ISO instant a run last walked this account, or null when none has. Null
* is what the whole cohort reads until the first run after the upgrade that
* adds the ledger, and the card says so rather than claiming `never`.
*/
lastAttemptAt: string | null;
/** ISO instant the newest object landed, or null when there is none. */
lastSuccessAt: string | null;
/** Size of that object in bytes, or null when there is none. */
sizeBytes: number | null;
/** Whole hours since it landed, or null when there is none. */
ageHours: number | null;
freshness: OffhostBackupFreshness;
}
/**
* The off-host picture, per account.
*
* Computed from the ledger the nightly worker writes, never from the bucket:
* the page must be able to answer "does this account have a recent copy
* off-host" on a host whose worker holds no listing grant on the bucket.
*/
export interface OffhostBackupOverview {
/** Whether this host has the variables the nightly job needs. */
configured: boolean;
/** The run in progress, and accounts a run died under. */
activity: BackupPassActivity;
/** Hours between two scheduled runs — the number behind the verdicts. */
periodHours: number;
/** One row per account. Empty when off-host backup is not configured. */
rows: OffhostAccountRow[];
/**
* Deleted or wiped accounts whose copies are still in the bucket. A count
* above zero with an error means the bucket refused the deletion (most
* often a credential without DeleteObject); the job retries nightly.
*/
pendingDeletions: {
count: number;
oldestRequestedAt: string | null;
lastFailure: string | null;
};
/**
* Whether the bucket retires old copies by itself. Read from the bucket
* (`GetBucketLifecycleConfiguration`), cached for ten minutes; `unknown`
* when the credential may not read it.
*/
lifecycle: {
state: "configured" | "missing" | "unknown";
expirationDays: number | null;
};
}
export interface BackupsList {
rows: BackupRow[];
/**
* Whether the weekly schedule is still producing copies, and how the last
* scheduled run ended. A row's own timestamp cannot answer that — a copy
* made six weeks ago and one made on Sunday look identical in a table.
*/
schedule: BackupScheduleStatus;
/**
* The weekly run in progress, and accounts a run died under: a start
* without a finish, which a crash leaves and a caught failure does not.
*/
scheduleActivity: BackupPassActivity;
/**
* The off-host leg. A weekly row in this table says the copy that lives in
* this database is current; it says nothing about whether anything reached
* the operator's bucket, and those two fail independently.
*/
offhost: OffhostBackupOverview;
/**
* Soft retention hint — the worker is configured for weekly backups
* (see `DATA_BACKUP_CRON` in `src/lib/jobs/reminder-worker.ts`), and
* the model upserts in-place per (userId, type), so each user has
* exactly one current snapshot. The frontend uses this to label the
* grid; no server-side enforcement.
*/
retentionDays: number;
}