import type { AppSettings, SessionState, HistoryEntry } from "../shared/types"; export type BackupKind = "full" | "settings-only"; export interface BackupRemoteDiagnostics { allowlist: string[]; port: number; hostMode: "local" | "network"; } export interface BackupPayload { version: 2; kind: BackupKind; appVersion: string; exportedAt: string; settings: AppSettings; session?: SessionState; history?: HistoryEntry[]; remoteDiagnostics?: BackupRemoteDiagnostics; } export interface BuildBackupInput { settings: AppSettings; appVersion: string; exportedAt: string; /** Only bundled when includeDownloads is true. */ session: SessionState; history: HistoryEntry[]; remoteDiagnostics?: BackupRemoteDiagnostics; } /** * Build the backup payload. By default ("Download-Liste mitsichern" off) the * payload contains ONLY settings — no session, no history. The download list is * bundled solely when settings.backupIncludeDownloads is true. An explicit kind * marker makes the import side unambiguous and survives hand-edited files. */ export function buildBackupPayload(input: BuildBackupInput): BackupPayload { const includeDownloads = Boolean(input.settings.backupIncludeDownloads); const base: BackupPayload = { version: 2, kind: includeDownloads ? "full" : "settings-only", appVersion: input.appVersion, exportedAt: input.exportedAt, settings: input.settings }; if (includeDownloads) { base.session = input.session; base.history = input.history; } if (Boolean(input.settings.backupIncludeRemoteDiagnostics) && input.remoteDiagnostics) { base.remoteDiagnostics = input.remoteDiagnostics; } return base; } export interface RemoteDiagnosticsRestore { host?: "127.0.0.1" | "0.0.0.0"; port?: number; allowlist?: string[]; } export function resolveRemoteDiagnosticsRestore(section: unknown): RemoteDiagnosticsRestore | null { if (!section || typeof section !== "object") { return null; } const s = section as { allowlist?: unknown; port?: unknown; hostMode?: unknown }; const allowlist = Array.isArray(s.allowlist) ? s.allowlist.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim()) : undefined; const port = (typeof s.port === "number" && Number.isInteger(s.port) && s.port >= 1024 && s.port <= 65535) ? s.port : undefined; let host: "127.0.0.1" | "0.0.0.0" | undefined; if (s.hostMode === "network") { host = allowlist && allowlist.length > 0 ? "0.0.0.0" : "127.0.0.1"; } else if (s.hostMode === "local") { host = "127.0.0.1"; } if (host === undefined && port === undefined && allowlist === undefined) { return null; } return { host, port, allowlist }; } export interface ImportPlan { valid: boolean; /** Restore the download list (session + history) and relaunch. */ restoreDownloads: boolean; message: string; } /** * Decide how to apply an imported backup based on what the FILE physically * contains — NOT the local toggle. A backup without a session restores settings * only (no queue wipe, no relaunch); a full backup (with session) restores the * queue too. This way an old full backup still restores fully even if the local * toggle is currently off, and a settings-only backup never disturbs a running * queue. */ export function planBackupImport(parsed: unknown): ImportPlan { if (!parsed || typeof parsed !== "object") { return { valid: false, restoreDownloads: false, message: "Kein gültiges Backup (settings fehlen)" }; } const record = parsed as Record; if (!record.settings || typeof record.settings !== "object") { return { valid: false, restoreDownloads: false, message: "Kein gültiges Backup (settings fehlen)" }; } const hasSession = Boolean(record.session) && typeof record.session === "object"; return { valid: true, restoreDownloads: hasSession, message: hasSession ? "Backup wiederhergestellt – App startet automatisch neu…" : "Einstellungen wiederhergestellt" }; }