78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import { sortMigrations } from "../../planner/migration-planner/migration-planner.js";
|
|
import type {
|
|
ExtendedMigrationStatus,
|
|
MigrationHealthCheck,
|
|
MigrationHistoryEntry,
|
|
MigrationSummary,
|
|
StatusConfig,
|
|
} from "../../types/migration.types.js";
|
|
|
|
/**
|
|
* Provides a read-only dashboard over the migration store: per-migration
|
|
* status, aggregate summaries, health checks, and history per migration id.
|
|
*/
|
|
export class MigrationStatusDashboard {
|
|
protected readonly config: StatusConfig;
|
|
|
|
constructor(config: StatusConfig) {
|
|
this.config = config;
|
|
}
|
|
|
|
async getAll(): Promise<ExtendedMigrationStatus[]> {
|
|
const { store, migrations } = this.config;
|
|
const applied = await store.getApplied();
|
|
const appliedMap = new Map(applied.map((r) => [r.id, r]));
|
|
return sortMigrations(migrations).map((m): ExtendedMigrationStatus => {
|
|
const record = appliedMap.get(m.id);
|
|
const status = record
|
|
? record.direction === "down"
|
|
? ("rolled-back" as const)
|
|
: ("applied" as const)
|
|
: ("pending" as const);
|
|
return {
|
|
id: m.id,
|
|
name: m.name,
|
|
status,
|
|
...(record !== undefined && { appliedAt: record.appliedAt }),
|
|
...(record?.checksum !== undefined && {
|
|
checksum: record.checksum,
|
|
}),
|
|
};
|
|
});
|
|
}
|
|
|
|
async getSummary(): Promise<MigrationSummary> {
|
|
const all = await this.getAll();
|
|
return {
|
|
total: all.length,
|
|
applied: all.filter((s) => s.status === "applied").length,
|
|
pending: all.filter((s) => s.status === "pending").length,
|
|
failed: all.filter((s) => s.status === "failed").length,
|
|
};
|
|
}
|
|
|
|
async healthCheck(): Promise<MigrationHealthCheck> {
|
|
const summary = await this.getSummary();
|
|
return {
|
|
healthy: summary.pending === 0 && summary.failed === 0,
|
|
pendingCount: summary.pending,
|
|
failedCount: summary.failed,
|
|
};
|
|
}
|
|
|
|
async getHistory(id: string): Promise<MigrationHistoryEntry[]> {
|
|
const { store } = this.config;
|
|
const applied = await store.getApplied();
|
|
return applied
|
|
.filter((r) => r.id === id)
|
|
.map(
|
|
(r): MigrationHistoryEntry => ({
|
|
id: r.id,
|
|
direction: r.direction,
|
|
appliedAt: r.appliedAt,
|
|
...(r.checksum !== undefined && { checksum: r.checksum }),
|
|
})
|
|
);
|
|
}
|
|
}
|