107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
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)` };
|
|
},
|
|
"reset": async function (migrator: Migrator) {
|
|
if (env.NODE_ENV !== "development") throw new Error("Development only command");
|
|
|
|
if (typeof handlers["rollback"] !== "function") throw new Error("Missing rollback handler");
|
|
await handlers["rollback"](migrator);
|
|
|
|
if (typeof handlers["latest"] !== "function") throw new Error("Missing latest handler");
|
|
await handlers["latest"](migrator);
|
|
|
|
return { message: "database fully reset" };
|
|
}
|
|
};
|
|
|
|
// 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()
|
|
}
|
|
})();
|