paperclip/packages/db/src/client.ts

74 lines
2.2 KiB
TypeScript
Raw Normal View History

import { drizzle as drizzlePg } from "drizzle-orm/postgres-js";
import { migrate as migratePg } from "drizzle-orm/postgres-js/migrator";
import postgres from "postgres";
import * as schema from "./schema/index.js";
export function createDb(url: string) {
const sql = postgres(url);
return drizzlePg(sql, { schema });
}
export type MigrationBootstrapResult =
| { migrated: true; reason: "migrated-empty-db"; tableCount: 0 }
| { migrated: false; reason: "already-migrated"; tableCount: number }
| { migrated: false; reason: "not-empty-no-migration-journal"; tableCount: number };
export async function migratePostgresIfEmpty(url: string): Promise<MigrationBootstrapResult> {
const sql = postgres(url, { max: 1 });
try {
const journal = await sql<{ regclass: string | null }[]>`
select to_regclass('public.__drizzle_migrations') as regclass
`;
const tableCountResult = await sql<{ count: number }[]>`
select count(*)::int as count
from information_schema.tables
where table_schema = 'public'
and table_type = 'BASE TABLE'
`;
const tableCount = tableCountResult[0]?.count ?? 0;
if (journal[0]?.regclass) {
return { migrated: false, reason: "already-migrated", tableCount };
}
if (tableCount > 0) {
return { migrated: false, reason: "not-empty-no-migration-journal", tableCount };
}
const db = drizzlePg(sql);
const migrationsFolder = new URL("./migrations", import.meta.url).pathname;
await migratePg(db, { migrationsFolder });
return { migrated: true, reason: "migrated-empty-db", tableCount: 0 };
} finally {
await sql.end();
}
}
export async function ensurePostgresDatabase(
url: string,
databaseName: string,
): Promise<"created" | "exists"> {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(databaseName)) {
throw new Error(`Unsafe database name: ${databaseName}`);
}
const sql = postgres(url, { max: 1 });
try {
const existing = await sql<{ one: number }[]>`
select 1 as one from pg_database where datname = ${databaseName} limit 1
`;
if (existing.length > 0) return "exists";
await sql.unsafe(`create database "${databaseName}"`);
return "created";
} finally {
await sql.end();
}
}
export type Db = ReturnType<typeof createDb>;