96 lines
4.3 KiB
TypeScript
96 lines
4.3 KiB
TypeScript
import type { DatabaseSync } from "node:sqlite";
|
|
import { initialEnterpriseSchema } from "./migrations/001-initial";
|
|
import { collaborationProviderPolicySchema } from "./migrations/002-collaboration";
|
|
import { onboardingExecutionSchema } from "./migrations/003-onboarding";
|
|
import { filesPinnedAndScheduledPosts } from "./migrations/004-files-posts";
|
|
import { chatReadState } from "./migrations/005-chat-read-state";
|
|
import { appStateSchema } from "./migrations/006-app-state";
|
|
import { skillHardeningSchema } from "./migrations/007-skill-hardening";
|
|
import { providerHealthSchema } from "./migrations/008-provider-health";
|
|
import { socialGovernanceSchema } from "./migrations/009-social-governance";
|
|
import { fileHandoffSecuritySchema } from "./migrations/010-file-handoff-security";
|
|
import { boardInterventionSchema } from "./migrations/011-board-interventions";
|
|
import { complianceControlsSchema } from "./migrations/012-compliance-controls";
|
|
import { searchDocumentsSchema } from "./migrations/013-search-documents";
|
|
import { providerPolicyUsageSchema } from "./migrations/014-provider-policy-usage";
|
|
import { identityAccessSchema } from "./migrations/015-identity-access";
|
|
import { orgGovernanceSchema } from "./migrations/016-org-governance";
|
|
import { operationsControlsSchema } from "./migrations/017-operations-controls";
|
|
import { sdlcControlsSchema } from "./migrations/018-sdlc-controls";
|
|
|
|
interface Migration {
|
|
version: number;
|
|
name: string;
|
|
sql: string;
|
|
}
|
|
|
|
const MIGRATIONS: Migration[] = [
|
|
{ version: 1, name: "initial_enterprise_schema", sql: initialEnterpriseSchema },
|
|
{ version: 2, name: "collaboration_provider_policy_schema", sql: collaborationProviderPolicySchema },
|
|
{ version: 3, name: "onboarding_execution_schema", sql: onboardingExecutionSchema },
|
|
{ version: 4, name: "files_pinned_and_scheduled_posts", sql: filesPinnedAndScheduledPosts },
|
|
{ version: 5, name: "chat_read_state", sql: chatReadState },
|
|
{ version: 6, name: "app_state", sql: appStateSchema },
|
|
{ version: 7, name: "skill_hardening", sql: skillHardeningSchema },
|
|
{ version: 8, name: "provider_health", sql: providerHealthSchema },
|
|
{ version: 9, name: "social_governance", sql: socialGovernanceSchema },
|
|
{ version: 10, name: "file_handoff_security", sql: fileHandoffSecuritySchema },
|
|
{ version: 11, name: "board_interventions", sql: boardInterventionSchema },
|
|
{ version: 12, name: "compliance_controls", sql: complianceControlsSchema },
|
|
{ version: 13, name: "search_documents", sql: searchDocumentsSchema },
|
|
{ version: 14, name: "provider_policy_usage", sql: providerPolicyUsageSchema },
|
|
{ version: 15, name: "identity_access", sql: identityAccessSchema },
|
|
{ version: 16, name: "org_governance", sql: orgGovernanceSchema },
|
|
{ version: 17, name: "operations_controls", sql: operationsControlsSchema },
|
|
{ version: 18, name: "sdlc_controls", sql: sdlcControlsSchema },
|
|
];
|
|
|
|
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
|
|
|
export function installSchema(db: DatabaseSync): void {
|
|
db.exec(`
|
|
create table if not exists schema_migrations (
|
|
version integer primary key,
|
|
name text not null,
|
|
applied_at text not null
|
|
);
|
|
`);
|
|
|
|
const row = db.prepare("select coalesce(max(version), 0) as version from schema_migrations").get() as { version: number };
|
|
let currentVersion = row.version;
|
|
if (currentVersion > SCHEMA_VERSION) {
|
|
throw new Error(`Database schema ${currentVersion} is newer than supported schema ${SCHEMA_VERSION}.`);
|
|
}
|
|
backfillMigrationLedger(db, currentVersion);
|
|
|
|
for (const migration of MIGRATIONS) {
|
|
if (currentVersion >= migration.version) continue;
|
|
applyMigration(db, migration);
|
|
currentVersion = migration.version;
|
|
}
|
|
}
|
|
|
|
function applyMigration(db: DatabaseSync, migration: Migration): void {
|
|
db.exec("begin immediate");
|
|
try {
|
|
db.exec(migration.sql);
|
|
recordMigration(db, migration);
|
|
db.exec("commit");
|
|
} catch (error) {
|
|
db.exec("rollback");
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function backfillMigrationLedger(db: DatabaseSync, currentVersion: number): void {
|
|
for (const migration of MIGRATIONS) {
|
|
if (migration.version > currentVersion) return;
|
|
recordMigration(db, migration);
|
|
}
|
|
}
|
|
|
|
function recordMigration(db: DatabaseSync, migration: Migration): void {
|
|
db.prepare("insert or ignore into schema_migrations(version, name, applied_at) values (?, ?, datetime('now'))")
|
|
.run(migration.version, migration.name);
|
|
}
|