migrator.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { readdir } from 'fs/promises'
  2. import { join } from 'path'
  3. import { QueryTypes } from 'sequelize'
  4. import { currentDir } from '@peertube/peertube-node-utils'
  5. import { logger } from '../helpers/logger.js'
  6. import { LAST_MIGRATION_VERSION } from './constants.js'
  7. import { sequelizeTypescript } from './database.js'
  8. async function migrate () {
  9. const tables = await sequelizeTypescript.getQueryInterface().showAllTables()
  10. // No tables, we don't need to migrate anything
  11. // The installer will do that
  12. if (tables.length === 0) return
  13. let actualVersion: number | null = null
  14. const query = 'SELECT "migrationVersion" FROM "application"'
  15. const options = {
  16. type: QueryTypes.SELECT as QueryTypes.SELECT
  17. }
  18. const rows = await sequelizeTypescript.query<{ migrationVersion: number }>(query, options)
  19. if (rows?.[0]?.migrationVersion) {
  20. actualVersion = rows[0].migrationVersion
  21. }
  22. if (actualVersion === null) {
  23. await sequelizeTypescript.query('INSERT INTO "application" ("migrationVersion") VALUES (0)')
  24. actualVersion = 0
  25. }
  26. // No need migrations, abort
  27. if (actualVersion >= LAST_MIGRATION_VERSION) return
  28. // If there are a new migration scripts
  29. logger.info('Begin migrations.')
  30. const migrationScripts = await getMigrationScripts()
  31. for (const migrationScript of migrationScripts) {
  32. try {
  33. await executeMigration(actualVersion, migrationScript)
  34. } catch (err) {
  35. logger.error('Cannot execute migration %s.', migrationScript.version, { err })
  36. process.exit(-1)
  37. }
  38. }
  39. logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
  40. }
  41. // ---------------------------------------------------------------------------
  42. export {
  43. migrate
  44. }
  45. // ---------------------------------------------------------------------------
  46. async function getMigrationScripts () {
  47. const files = await readdir(join(currentDir(import.meta.url), 'migrations'))
  48. const filesToMigrate: {
  49. version: string
  50. script: string
  51. }[] = []
  52. files
  53. .filter(file => file.endsWith('.js'))
  54. .forEach(file => {
  55. // Filename is something like 'version-blabla.js'
  56. const version = file.split('-')[0]
  57. filesToMigrate.push({
  58. version,
  59. script: file
  60. })
  61. })
  62. return filesToMigrate
  63. }
  64. async function executeMigration (actualVersion: number, entity: { version: string, script: string }) {
  65. const versionScript = parseInt(entity.version, 10)
  66. // Do not execute old migration scripts
  67. if (versionScript <= actualVersion) return undefined
  68. // Load the migration module and run it
  69. const migrationScriptName = entity.script
  70. logger.info('Executing %s migration script.', migrationScriptName)
  71. const migrationScript = await import(join(currentDir(import.meta.url), 'migrations', migrationScriptName))
  72. return sequelizeTypescript.transaction(async t => {
  73. const options = {
  74. transaction: t,
  75. queryInterface: sequelizeTypescript.getQueryInterface(),
  76. sequelize: sequelizeTypescript
  77. }
  78. await migrationScript.up(options)
  79. // Update the new migration version
  80. await sequelizeTypescript.query('UPDATE "application" SET "migrationVersion" = ' + versionScript, { transaction: t })
  81. })
  82. }