d72c60638a2c58a23290a1308446d3abb7bc5e6c
[oweals/peertube.git] / server / initializers / migrator.ts
1 import { waterfall, eachSeries } from 'async'
2 import * as fs from 'fs'
3 import * as path from 'path'
4 import * as Sequelize from 'sequelize'
5
6 import { database as db } from './database'
7 import { LAST_MIGRATION_VERSION } from './constants'
8 import { logger } from '../helpers'
9 import { ApplicationInstance } from '../models'
10
11 function migrate (finalCallback: (err: Error) => void) {
12   waterfall([
13
14     function checkApplicationTableExists (callback) {
15       db.sequelize.getQueryInterface().showAllTables().asCallback(function (err, tables) {
16         if (err) return callback(err)
17
18         // No tables, we don't need to migrate anything
19         // The installer will do that
20         if (tables.length === 0) return finalCallback(null)
21
22         return callback(null)
23       })
24     },
25
26     function loadMigrationVersion (callback) {
27       db.Application.loadMigrationVersion(callback)
28     },
29
30     function createMigrationRowIfNotExists (actualVersion, callback) {
31       if (actualVersion === null) {
32         db.Application.create({
33           migrationVersion: 0
34         }, function (err) {
35           return callback(err, 0)
36         })
37       }
38
39       return callback(null, actualVersion)
40     },
41
42     function abortMigrationIfNotNeeded (actualVersion, callback) {
43       // No need migrations
44       if (actualVersion >= LAST_MIGRATION_VERSION) return finalCallback(null)
45
46       return callback(null, actualVersion)
47     },
48
49     function getMigrations (actualVersion, callback) {
50       // If there are a new migration scripts
51       logger.info('Begin migrations.')
52
53       getMigrationScripts(function (err, migrationScripts) {
54         return callback(err, actualVersion, migrationScripts)
55       })
56     },
57
58     function doMigrations (actualVersion, migrationScripts, callback) {
59       eachSeries(migrationScripts, function (entity: any, callbackEach) {
60         executeMigration(actualVersion, entity, callbackEach)
61       }, function (err) {
62         if (err) return callback(err)
63
64         logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
65         return callback(null)
66       })
67     }
68   ], finalCallback)
69 }
70
71 // ---------------------------------------------------------------------------
72
73 export {
74   migrate
75 }
76
77 // ---------------------------------------------------------------------------
78
79 type GetMigrationScriptsCallback = (err: Error, filesToMigrate?: { version: string, script: string }[]) => void
80 function getMigrationScripts (callback: GetMigrationScriptsCallback) {
81   fs.readdir(path.join(__dirname, 'migrations'), function (err, files) {
82     if (err) return callback(err)
83
84     const filesToMigrate = []
85
86     files.forEach(function (file) {
87       // Filename is something like 'version-blabla.js'
88       const version = file.split('-')[0]
89       filesToMigrate.push({
90         version,
91         script: file
92       })
93     })
94
95     return callback(err, filesToMigrate)
96   })
97 }
98
99 function executeMigration (actualVersion: number, entity: { version: string, script: string }, callback: (err: Error) => void) {
100   const versionScript = parseInt(entity.version, 10)
101
102   // Do not execute old migration scripts
103   if (versionScript <= actualVersion) return callback(null)
104
105   // Load the migration module and run it
106   const migrationScriptName = entity.script
107   logger.info('Executing %s migration script.', migrationScriptName)
108
109   const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
110
111   db.sequelize.transaction().asCallback(function (err, t) {
112     if (err) return callback(err)
113
114     const options = {
115       transaction: t,
116       queryInterface: db.sequelize.getQueryInterface(),
117       sequelize: db.sequelize,
118       Sequelize: Sequelize
119     }
120     migrationScript.up(options, function (err) {
121       if (err) {
122         t.rollback()
123         return callback(err)
124       }
125
126       // Update the new migration version
127       db.Application.updateMigrationVersion(versionScript, t, function (err) {
128         if (err) {
129           t.rollback()
130           return callback(err)
131         }
132
133         t.commit().asCallback(callback)
134       })
135     })
136   })
137 }