You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
63 lines
1.8 KiB
63 lines
1.8 KiB
type SqlValue = string | number | null | Uint8Array;
|
|
|
|
export interface QueryExecResult {
|
|
columns: Array<string>;
|
|
values: Array<Array<SqlValue>>;
|
|
}
|
|
|
|
interface Migration {
|
|
name: string;
|
|
sql: string;
|
|
}
|
|
|
|
export class MigrationService {
|
|
private static instance: MigrationService;
|
|
private migrations: Migration[] = [];
|
|
|
|
private constructor() {}
|
|
|
|
static getInstance(): MigrationService {
|
|
if (!MigrationService.instance) {
|
|
MigrationService.instance = new MigrationService();
|
|
}
|
|
return MigrationService.instance;
|
|
}
|
|
|
|
async registerMigration(migration: Migration): Promise<void> {
|
|
this.migrations.push(migration);
|
|
}
|
|
|
|
async runMigrations(
|
|
sqlExec: (sql: string, params?: any[]) => Promise<Array<QueryExecResult>>
|
|
): Promise<void> {
|
|
// Create migrations table if it doesn't exist
|
|
await sqlExec(`
|
|
CREATE TABLE IF NOT EXISTS migrations (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
|
|
// Get list of executed migrations
|
|
const result = await sqlExec('SELECT name FROM migrations');
|
|
const singleResult = result[0];
|
|
const executedMigrations = new Set(singleResult.values.map(row => row[0]));
|
|
|
|
// Run pending migrations in order
|
|
for (const migration of this.migrations) {
|
|
if (!executedMigrations.has(migration.name)) {
|
|
try {
|
|
await sqlExec(migration.sql);
|
|
await sqlExec('INSERT INTO migrations (name) VALUES (?)', [migration.name]);
|
|
console.log(`Migration ${migration.name} executed successfully`);
|
|
} catch (error) {
|
|
console.error(`Error executing migration ${migration.name}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default MigrationService.getInstance();
|