export interface LogicalDumpConfig { enabled: boolean; database: string; scriptPath: string; serviceName: string; timerName: string; schedule: string; retentionDays: number; warningAfterHours: number; destination: { type: string; path: string }; pgDump: { host: string; format: "custom" }; encryption: { enabled: boolean }; } export interface LogicalDumpRuntimeFact { database: string; databaseExists: boolean; scriptPath: string; scriptExists: boolean; serviceName: string; serviceResult: string | null; serviceExitStatus: number | null; lastSuccessAt: string | null; timerName: string; timerActive: boolean; timerEnabled: boolean; nextRunAt: string | null; latestNonEmptyDump: { path: string; bytes: number; modifiedAt: string; } | null; } export interface LogicalDumpProjection extends LogicalDumpConfig { observed: LogicalDumpRuntimeFact | null; recoverability: { recoverable: boolean; latestNonEmptyDump: LogicalDumpRuntimeFact["latestNonEmptyDump"]; }; warnings: string[]; } export function projectLogicalDumps( declarations: LogicalDumpConfig[], facts: LogicalDumpRuntimeFact[] | null, observedAt = Date.now(), ): LogicalDumpProjection[] { return declarations.map((declaration) => { const observed = facts?.find((item) => item.database === declaration.database && item.timerName === declaration.timerName) ?? null; const warnings: string[] = []; if (!declaration.enabled) return projection(declaration, observed, warnings); if (observed === null) { warnings.push("backup-observation-unavailable"); return projection(declaration, observed, warnings); } if (!observed.databaseExists) warnings.push("backup-database-missing"); if (!observed.scriptExists) warnings.push("backup-script-missing"); if (!observed.timerEnabled) warnings.push("backup-timer-disabled"); if (!observed.timerActive) warnings.push("backup-timer-inactive"); if (observed.lastSuccessAt === null) warnings.push("backup-recent-success-missing"); if (observed.latestNonEmptyDump === null) { warnings.push("backup-latest-non-empty-dump-missing"); } else { const modifiedAt = Date.parse(observed.latestNonEmptyDump.modifiedAt); if (!Number.isFinite(modifiedAt) || observedAt - modifiedAt > declaration.warningAfterHours * 60 * 60 * 1000) { warnings.push("backup-latest-dump-stale"); } } if (observed.serviceResult !== null && observed.serviceResult !== "success") warnings.push("backup-last-run-failed"); if (observed.serviceExitStatus !== null && observed.serviceExitStatus !== 0) warnings.push("backup-last-run-nonzero"); return projection(declaration, observed, warnings); }); } function projection( declaration: LogicalDumpConfig, observed: LogicalDumpRuntimeFact | null, warnings: string[], ): LogicalDumpProjection { return { ...declaration, observed, recoverability: { recoverable: observed?.databaseExists === true && observed.latestNonEmptyDump !== null, latestNonEmptyDump: observed?.latestNonEmptyDump ?? null, }, warnings, }; }