CI & Build / Python lint (push) Successful in 9s
CI & Build / Plugin hooks (push) Successful in 22s
CI & Build / TypeScript typecheck (push) Successful in 1m0s
CI & Build / integration (push) Successful in 1m55s
CI & Build / Python tests (push) Successful in 2m20s
CI & Build / Build & push image (push) Successful in 1m7s
Components, stores, api clients, router, types and shared CSS now state what each piece is and why, without the milestone that introduced it. diff.ts names its consumers instead of the copy history; stores/notes and tasks state their current scope instead of the cleanup that set it; theme.css states the token practice instead of the sweep that established it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
import { apiGet, apiPost, apiPut } from "@/api/client";
|
||
|
||
/**
|
||
* Scheduled backups — `/api/admin/backups`. One dated,
|
||
* uncompressed tar per run, delivered to a folder or an S3 bucket.
|
||
*/
|
||
export interface BackupConfig {
|
||
enabled: boolean;
|
||
/** UTC hour the daily backup runs at, 0–23. */
|
||
hour: number;
|
||
destination: "folder" | "s3";
|
||
folder_path: string;
|
||
s3_endpoint: string;
|
||
s3_bucket: string;
|
||
s3_access_key: string;
|
||
s3_region: string;
|
||
s3_verify_tls: boolean;
|
||
}
|
||
|
||
export interface BackupRun {
|
||
started_at: string;
|
||
finished_at: string;
|
||
trigger: "scheduled" | "manual";
|
||
status: "ok" | "failed";
|
||
destination?: string;
|
||
file?: string;
|
||
size_bytes?: number;
|
||
where?: string;
|
||
error?: string;
|
||
duration_s: number;
|
||
}
|
||
|
||
export interface BackupsOverview {
|
||
config: BackupConfig;
|
||
/** Never the secret itself: whether one is kept, and whether this
|
||
* instance can still open it. */
|
||
secret: { set: boolean; readable: boolean };
|
||
/** What stops the saved config from sending a backup, in words. */
|
||
problems: string[];
|
||
running: boolean;
|
||
/** Newest first, at most 30. */
|
||
runs: BackupRun[];
|
||
last_run: BackupRun | null;
|
||
last_success: BackupRun | null;
|
||
overdue: boolean;
|
||
}
|
||
|
||
/** What the form sends: config fields, plus the secret when one was typed. */
|
||
export type BackupChanges = Partial<BackupConfig> & {
|
||
s3_secret?: string;
|
||
clear_s3_secret?: boolean;
|
||
};
|
||
|
||
export function getBackups(): Promise<BackupsOverview> {
|
||
return apiGet<BackupsOverview>("/api/admin/backups");
|
||
}
|
||
|
||
export function saveBackups(changes: BackupChanges): Promise<BackupsOverview> {
|
||
return apiPut<BackupsOverview>("/api/admin/backups", changes);
|
||
}
|
||
|
||
/** Ask the destination whether it would take a backup, with the form's
|
||
* unsaved values. The server gives the destination 45s; this waits a little
|
||
* longer so its own answer, not the client's deadline, is what arrives. */
|
||
export function checkBackupDestination(
|
||
changes: BackupChanges,
|
||
): Promise<{ ok: boolean; message: string }> {
|
||
return apiPost<{ ok: boolean; message: string }>("/api/admin/backups/check", changes, { timeoutMs: 60_000 });
|
||
}
|
||
|
||
export function runBackupNow(): Promise<{ status: string }> {
|
||
return apiPost<{ status: string }>("/api/admin/backups/run", {});
|
||
}
|