import config from 'models/config' import type MigrationObj from 'models/Migrations' export enum ConnectionStatus { DISCONNECTED, MIGRATING, READY } export interface ClientStatic { get(): Promise } export default abstract class Client { public status: ConnectionStatus = ConnectionStatus.DISCONNECTED /** * -1: unknown * 0: migrating * 1: migrated */ public migrationStatus = -1 /** * get the current migration version * * -1 nothing/error * 0+ current migration */ public abstract getVersion(): Promise public abstract setVersion(version: number): Promise public abstract execute(query: string, params?: Array | object, ...options: Array): Promise>> public abstract connect(): Promise /** * Migrate the database to the latest version */ public async migrateToLatest() { const migrations = this.getMigrations() const latest = migrations[migrations.length - 1] if (!latest) { return } return await this.migrateTo(latest.date) } public getMigrations(): ReadonlyArray { return config.migrations as ReadonlyArray } /** * migrate to a specific date in time * @param newVersion the date to try to migrate to */ public async migrateTo(newVersion: number) { if (this.migrationStatus === 0) { return } this.migrationStatus = 0 let currentVersion = await this.getVersion() ?? -1 const migrations = this.getMigrations() // same version, don't to anything if (newVersion === currentVersion) { this.migrationStatus = 1 return } console.log('\x1b[35mCurrent DB version', currentVersion, '\x1b[0m') // run up migrations if (currentVersion < newVersion) { console.log('\x1b[35m', 'Migrating up to', newVersion, '\x1b[0m') const migrationsToRun = migrations.filter((it) => it.date > currentVersion && it.date <= newVersion) for (const migration of migrationsToRun) { console.log('\x1b[35m', 'Migrating from', currentVersion, 'to', migration.date, '\x1b[0m') await migration.up(this) await this.setVersion(migration.date) currentVersion = migration.date } } else if (currentVersion > newVersion) { // run down migrations console.log('\x1b[35m', 'Migrating down to', newVersion, '\x1b[0m') const migrationsToRun = migrations.filter((it) => it.date < currentVersion && it.date >= newVersion) .toReversed() for (const migration of migrationsToRun) { console.log('\x1b[35m', 'Migrating from', currentVersion, 'to', migration.date, '\x1b[0m') await migration.down?.(this) await this.setVersion(migration.date) currentVersion = migration.date } } console.log('\x1b[32mDone migrating\x1b[0m') this.migrationStatus = 1 } // public getStatus(): Promise // public abstract isMigrated(): Promise /** * indicate if the client is ready for new requests (not if migrations are done or not) */ public abstract isReady(): Promise /** * wait until every migrations are done or fail */ public async waitForMigrations(): Promise { if (this.migrationStatus === -1) { await this.migrateToLatest() } while (!await this.isMigrated()) { console.log('waiting...') await new Promise((res) => setTimeout(res, 100)) } } public isMigrating(): boolean { return this.migrationStatus === 0 } public async isMigrated(): Promise { return this.migrationStatus === 1 // if (this.migrationStatus < 1) { // return false // } else if (this.migrationStatus === 1) { // return // } // const migrations = this.getMigrations() // const last = migrations[migrations.length - 1] // if (!last) { // return true // } // return last.date === await this.getVersion() } }