Files
kandev/apps/web/lib/api/domains/system-api.ts
T
2026-07-18 19:08:44 +01:00

310 lines
9.7 KiB
TypeScript

import { fetchJson, type ApiRequestOptions } from "../client";
import { getBackendConfig } from "@/lib/config";
import type {
SystemInfo,
DiskUsageResponse,
DatabaseStats,
SnapshotInfo,
LogFileInfo,
LogTailResponse,
UpdatesResponse,
JobAcceptResponse,
RestartCapability,
RestartResponse,
StorageAdoptionResponse,
StorageMaintenanceRun,
StorageMaintenanceSettings,
StorageOverviewResponse,
StorageQuarantineEntry,
StorageSettingsResponse,
} from "@/lib/types/system";
const SYSTEM_BASE = "/api/v1/system";
// --- Info ---------------------------------------------------------------
export function fetchSystemInfo(options?: ApiRequestOptions): Promise<SystemInfo> {
return fetchJson<SystemInfo>(`${SYSTEM_BASE}/info`, options);
}
// --- Disk usage ---------------------------------------------------------
export function fetchDiskUsage(options?: ApiRequestOptions): Promise<DiskUsageResponse> {
return fetchJson<DiskUsageResponse>(`${SYSTEM_BASE}/disk-usage`, options);
}
export function refreshDiskUsage(options?: ApiRequestOptions): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/disk-usage/refresh`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
export function openDataFolder(options?: ApiRequestOptions): Promise<{ path: string }> {
return fetchJson<{ path: string }>(`${SYSTEM_BASE}/disk-usage/open`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
// --- Database -----------------------------------------------------------
export function fetchDatabaseStats(options?: ApiRequestOptions): Promise<DatabaseStats> {
return fetchJson<DatabaseStats>(`${SYSTEM_BASE}/database`, options);
}
export function vacuumDatabase(options?: ApiRequestOptions): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/database/vacuum`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
export function optimizeDatabase(options?: ApiRequestOptions): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/database/optimize`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
export function resetDatabase(
confirm: string,
options?: ApiRequestOptions,
): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/database/reset`, {
...options,
init: { method: "POST", body: JSON.stringify({ confirm }), ...(options?.init ?? {}) },
});
}
// --- Backups ------------------------------------------------------------
export async function fetchBackups(options?: ApiRequestOptions): Promise<SnapshotInfo[]> {
const res = await fetchJson<{ snapshots: SnapshotInfo[] }>(`${SYSTEM_BASE}/backups`, options);
return res.snapshots ?? [];
}
export function createBackup(options?: ApiRequestOptions): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/backups`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
export function restoreBackup(
name: string,
confirm: string,
options?: ApiRequestOptions,
): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(
`${SYSTEM_BASE}/backups/${encodeURIComponent(name)}/restore`,
{
...options,
init: { method: "POST", body: JSON.stringify({ confirm }), ...(options?.init ?? {}) },
},
);
}
export function deleteBackup(name: string, options?: ApiRequestOptions): Promise<void> {
return fetchJson<void>(`${SYSTEM_BASE}/backups/${encodeURIComponent(name)}`, {
...options,
init: { method: "DELETE", ...(options?.init ?? {}) },
});
}
/**
* Builds an absolute URL for downloading a snapshot. Snapshot downloads stream
* binary data, so they cannot reuse fetchJson — they are typically wired into
* <a href={...} download> or window.open().
*/
export function buildBackupDownloadUrl(name: string, baseUrl?: string): string {
const root = baseUrl ?? getBackendConfig().apiBaseUrl;
return `${root}${SYSTEM_BASE}/backups/${encodeURIComponent(name)}/download`;
}
// --- Logs ---------------------------------------------------------------
export async function fetchLogFiles(options?: ApiRequestOptions): Promise<LogFileInfo[]> {
const res = await fetchJson<{ files: LogFileInfo[] }>(`${SYSTEM_BASE}/logs`, options);
return res.files ?? [];
}
export function fetchLogTail(n = 1000, options?: ApiRequestOptions): Promise<LogTailResponse> {
return fetchJson<LogTailResponse>(`${SYSTEM_BASE}/logs/tail?n=${encodeURIComponent(String(n))}`, {
cache: "no-store",
...options,
});
}
export function buildLogDownloadUrl(name: string, baseUrl?: string): string {
const root = baseUrl ?? getBackendConfig().apiBaseUrl;
return `${root}${SYSTEM_BASE}/logs/${encodeURIComponent(name)}/download`;
}
// --- Jobs ---------------------------------------------------------------
export function fetchSystemJob(
jobId: string,
options?: ApiRequestOptions,
): Promise<import("@/lib/types/system").SystemJob> {
return fetchJson<import("@/lib/types/system").SystemJob>(
`${SYSTEM_BASE}/jobs/${encodeURIComponent(jobId)}`,
{ cache: "no-store", ...options },
);
}
// --- Updates ------------------------------------------------------------
export function fetchUpdates(options?: ApiRequestOptions): Promise<UpdatesResponse> {
return fetchJson<UpdatesResponse>(`${SYSTEM_BASE}/updates`, options);
}
export function checkUpdates(options?: ApiRequestOptions): Promise<UpdatesResponse> {
return fetchJson<UpdatesResponse>(`${SYSTEM_BASE}/updates/check`, {
...options,
init: { method: "POST", ...(options?.init ?? {}) },
});
}
export function applyUpdate(
confirm = "UPDATE",
options?: ApiRequestOptions,
): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/updates/apply`, {
...options,
// Spread caller init first so the required method/body can't be overridden.
init: { ...(options?.init ?? {}), method: "POST", body: JSON.stringify({ confirm }) },
});
}
// --- Restart ------------------------------------------------------------
export function fetchRestartCapability(options?: ApiRequestOptions): Promise<RestartCapability> {
return fetchJson<RestartCapability>(`${SYSTEM_BASE}/restart-capability`, {
...options,
cache: "no-store",
});
}
export function requestRestart(options?: ApiRequestOptions): Promise<RestartResponse> {
return fetchJson<RestartResponse>(`${SYSTEM_BASE}/restart`, {
...options,
init: { ...(options?.init ?? {}), method: "POST" },
});
}
// --- Storage maintenance ------------------------------------------------
export function fetchStorageOverview(
options?: ApiRequestOptions,
): Promise<StorageOverviewResponse> {
return fetchJson<StorageOverviewResponse>(`${SYSTEM_BASE}/storage`, {
...options,
cache: "no-store",
});
}
export function saveStorageSettings(
settings: StorageMaintenanceSettings,
dedicatedDockerConfirmation?: "DEDICATED",
options?: ApiRequestOptions,
): Promise<StorageSettingsResponse> {
return fetchJson<StorageSettingsResponse>(`${SYSTEM_BASE}/storage/settings`, {
...options,
init: {
...(options?.init ?? {}),
method: "PATCH",
body: JSON.stringify({
settings,
confirmations: dedicatedDockerConfirmation
? { dedicated_docker: dedicatedDockerConfirmation }
: {},
}),
},
});
}
export function adoptStorageGoCache(
path: string,
options?: ApiRequestOptions,
): Promise<StorageAdoptionResponse> {
return fetchJson<StorageAdoptionResponse>(`${SYSTEM_BASE}/storage/go-cache/adopt`, {
...options,
init: {
...(options?.init ?? {}),
method: "POST",
body: JSON.stringify({ path, confirm: "ADOPT" }),
},
});
}
export function analyzeStorage(options?: ApiRequestOptions): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/storage/analyze`, {
...options,
init: { ...(options?.init ?? {}), method: "POST" },
});
}
export function runStorageMaintenance(
resources?: string[],
options?: ApiRequestOptions,
): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(`${SYSTEM_BASE}/storage/run`, {
...options,
init: {
...(options?.init ?? {}),
method: "POST",
body: JSON.stringify(resources?.length ? { resources } : {}),
},
});
}
export async function fetchStorageRuns(
limit = 20,
options?: ApiRequestOptions,
): Promise<StorageMaintenanceRun[]> {
const response = await fetchJson<{ runs: StorageMaintenanceRun[] }>(
`${SYSTEM_BASE}/storage/runs?limit=${encodeURIComponent(String(limit))}`,
{ ...options, cache: "no-store" },
);
return response.runs ?? [];
}
export async function fetchStorageQuarantine(
options?: ApiRequestOptions,
): Promise<StorageQuarantineEntry[]> {
const response = await fetchJson<{ entries: StorageQuarantineEntry[] }>(
`${SYSTEM_BASE}/storage/quarantine`,
{ ...options, cache: "no-store" },
);
return response.entries ?? [];
}
export async function restoreStorageQuarantine(
id: string,
options?: ApiRequestOptions,
): Promise<StorageQuarantineEntry> {
const response = await fetchJson<{ entry: StorageQuarantineEntry }>(
`${SYSTEM_BASE}/storage/quarantine/${encodeURIComponent(id)}/restore`,
{ ...options, init: { ...(options?.init ?? {}), method: "POST" } },
);
return response.entry;
}
export function deleteStorageQuarantine(
id: string,
options?: ApiRequestOptions,
): Promise<JobAcceptResponse> {
return fetchJson<JobAcceptResponse>(
`${SYSTEM_BASE}/storage/quarantine/${encodeURIComponent(id)}`,
{
...options,
init: {
...(options?.init ?? {}),
method: "DELETE",
body: JSON.stringify({ confirm: "DELETE" }),
},
},
);
}