Spaces:
Sleeping
Sleeping
File size: 2,314 Bytes
b01d539 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | import { DatabaseSync } from "node:sqlite";
import { lstat } from "node:fs/promises";
import type { DatabaseBootstrapMode } from "../config/environment.js";
import { prepareLocalDatabasePath } from "./database-path.js";
export interface DatabaseBootstrapResult {
source: "existing" | "restored" | "fresh";
}
export async function bootstrapLocalDatabase(input: {
databasePath: string;
objectStorageRoot: string;
mode: DatabaseBootstrapMode;
restore: () => Promise<void>;
}): Promise<DatabaseBootstrapResult> {
const before = await prepareLocalDatabasePath(input);
if (before.existed) {
await assertNoRollbackJournal(before.databasePath);
verifyDatabaseIntegrity(before.databasePath);
return { source: "existing" };
}
await input.restore();
const after = await prepareLocalDatabasePath(input);
if (after.existed) {
await assertNoRollbackJournal(after.databasePath);
verifyDatabaseIntegrity(after.databasePath);
return { source: "restored" };
}
if (input.mode === "fresh-if-replica-missing") {
return { source: "fresh" };
}
throw new Error("A database replica is required but none was restored");
}
async function assertNoRollbackJournal(databasePath: string): Promise<void> {
try {
await lstat(`${databasePath}-journal`);
throw new Error("Unexpected SQLite rollback journal is present");
} catch (error) {
if (
error instanceof Error &&
"code" in error &&
(error as NodeJS.ErrnoException).code === "ENOENT"
) {
return;
}
throw error;
}
}
function verifyDatabaseIntegrity(databasePath: string): void {
let database: DatabaseSync | undefined;
try {
database = new DatabaseSync(databasePath, { readOnly: true });
const quickCheck = database.prepare("PRAGMA quick_check").get() as
| Record<string, string>
| undefined;
if (quickCheck === undefined || Object.values(quickCheck)[0] !== "ok") {
throw new Error("SQLite quick integrity check failed");
}
if (database.prepare("PRAGMA foreign_key_check").all().length !== 0) {
throw new Error("SQLite foreign key integrity check failed");
}
} catch (error) {
throw new Error("Local SQLite database integrity validation failed", {
cause: error,
});
} finally {
database?.close();
}
}
|