db-migration.class.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import fs from 'fs';
  2. import fsp from 'fs/promises';
  3. import path from 'path';
  4. import { Database } from 'sqlite3';
  5. import { Logger } from '../../../common/util/logger.class';
  6. import { MigrationException } from '../lib/migration-exception.class';
  7. import { SQLiteController } from './sqlite-controller.base';
  8. export class DBMigration extends SQLiteController {
  9. constructor(protected db: Database) {
  10. super();
  11. }
  12. public async update() {
  13. const migrationsDir = path.resolve(__dirname, '../migrations');
  14. if (!fs.existsSync(migrationsDir)) return;
  15. const files = await fsp.readdir(migrationsDir);
  16. if (files.length) {
  17. files.sort((a, b) => Number(a.substring(0, 12)) - Number(b.substring(0, 12)));
  18. await this.createMigrationsTable();
  19. const lastID = await this.getLastID();
  20. console.log('[DEBUG] lastid', lastID);
  21. for (const file of files) {
  22. const m = /^(\d{12})_(.*)\.sql$/.exec(file);
  23. if (!m) {
  24. throw new MigrationException(`File ${file} does not match migration file pattern. Aborted processing migrations.`);
  25. }
  26. const id = Number(m[1]);
  27. if (id <= lastID) continue;
  28. const migFilepath = path.join(migrationsDir, file);
  29. const migration = await fsp.readFile(migFilepath, { encoding: 'utf-8' });
  30. Logger.info('[INFO] Applying DB migration', file);
  31. await this.beginTransaction();
  32. try {
  33. await this.exec(migration);
  34. await this.run('INSERT INTO db_migrations(id, title, migrated) VALUES(?, ?, ?);', [id, m[2], new Date().getTime()]);
  35. await this.commit();
  36. Logger.info('[INFO] DB migration', file, 'succeeded.');
  37. } catch (error) {
  38. Logger.error('[ERROR] DB migration failed at', file, '- Rolling back...');
  39. await this.rollback();
  40. throw error;
  41. }
  42. }
  43. }
  44. }
  45. private async createMigrationsTable() {
  46. await this.run(
  47. `CREATE TABLE IF NOT EXISTS db_migrations (
  48. id INTEGER PRIMARY KEY,
  49. title TEXT NOT NULL,
  50. migrated INTEGER NOT NULL
  51. );`,
  52. []
  53. );
  54. }
  55. private async getLastID() {
  56. const results = await this.stmt(`SELECT id FROM db_migrations ORDER BY id DESC LIMIT 0, 1;`, []);
  57. return Number(results.rows[0]?.['id'] ?? '0');
  58. }
  59. }