Finish setting up basic db migrations

This commit is contained in:
Dominic Ferrando
2026-07-03 22:28:14 -04:00
parent 37ffaa282b
commit 66797e9a9d
8 changed files with 104 additions and 110 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import { CreateTableBuilder, Kysely, PostgresDialect, sql } from 'kysely';
import { type DB } from "./types";
import { type DB } from "./out/db";
import { Pool } from "pg";
import { env } from '../util';
+95
View File
@@ -0,0 +1,95 @@
import * as path from 'path'
import { promises as fs } from 'fs'
import { FileMigrationProvider, Migrator, type MigrationResultSet } from 'kysely/migration'
import { db } from './db'
import { argv } from 'bun'
import { env, log } from '../util'
type HandlerResult = {
message: string;
};
function processResults(label: string, { error, results }: MigrationResultSet): string {
let message = "";
results?.forEach((it) => {
if (it.status === 'Success') {
message = `migration ${label} succeeded: "${it.migrationName}"`;
} else if (it.status === 'Error') {
throw new Error(`migration ${label} failed: "${it.migrationName}"`);
}
});
if (error) throw error;
return message;
}
const handlers: Record<string, (migrator: Migrator) => Promise<HandlerResult>> = {
"up": async function (migrator: Migrator) {
const resultSet = await migrator.migrateUp();
const message = processResults("up", resultSet);
return { message };
},
"down": async function (migrator: Migrator) {
const resultSet = await migrator.migrateDown();
const message = processResults("down", resultSet);
return { message };
},
"latest": async function (migrator: Migrator) {
const resultSet = await migrator.migrateToLatest();
const message = processResults("to latest", resultSet);
return { message };
},
"rollback": async function (migrator: Migrator) {
let count = 0;
while (true) {
const resultSet = await migrator.migrateDown();
if (!resultSet.results?.length) break;
processResults("rollback", resultSet);
++count;
}
return { message: `rolled back ${count} migration(s)` };
}
};
// Main
(async () => {
const command = argv[2] ?? "";
if (!handlers[command]) throw new Error("Unrecognized command");
const answer = prompt(`Run "${command}" migration against ${env.DATABASE_URL}? (y/N)`);
if (answer?.trim().toLowerCase() !== "y") {
log.info("Aborted");
await db.destroy();
return;
}
const migrator = new Migrator({
db,
provider: new FileMigrationProvider({
fs,
path,
// This needs to be an absolute path.
migrationFolder: path.join(__dirname, "migrations"),
}),
});
try {
const result = await handlers[command](migrator);
log.info({ name: command, result }, "finished");
}
catch (err) {
log.error({ name: command, err }, "failed");
process.exit(1)
}
finally {
await db.destroy()
}
})();
@@ -5,6 +5,8 @@ export async function up(db: Kysely<any>): Promise<void> {
// TABLE: PRODUCT_SYNCS
await db.schema.createTable("product_syncs")
.$call(addDefaultColumns)
.addColumn("session_id", "uuid", (cb) => cb.notNull())
.addColumn("session_name", "text", (cb) => cb.notNull())
.addColumn("printful_product_id_filter", sql`integer[]`)
.addColumn("syncing_printful_product_ids", sql`integer[]`, (cb) => cb
.notNull()
-28
View File
@@ -1,28 +0,0 @@
/**
* This file was generated by kysely-codegen.
* Please do not edit it manually.
*/
import type { ColumnType } from "kysely";
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>;
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
export interface ProductSyncs {
created_at: Generated<Timestamp>;
ended_at: Timestamp | null;
has_failed: Generated<boolean>;
id: Generated<Int8>;
printful_product_id_filter: number[] | null;
started_at: Timestamp | null;
syncing_printful_product_ids: Generated<number[]>;
}
export interface DB {
product_syncs: ProductSyncs;
}