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.

161 lines
4.0 KiB

  1. const _ = require('lodash')
  2. const autoload = require('auto-load')
  3. const path = require('path')
  4. const Promise = require('bluebird')
  5. const Knex = require('knex')
  6. const Objection = require('objection')
  7. const migrationSource = require('../db/migrator-source')
  8. const migrateFromBeta = require('../db/beta')
  9. /* global WIKI */
  10. /**
  11. * ORM DB module
  12. */
  13. module.exports = {
  14. Objection,
  15. knex: null,
  16. /**
  17. * Initialize DB
  18. *
  19. * @return {Object} DB instance
  20. */
  21. init() {
  22. let self = this
  23. let dbClient = null
  24. let dbConfig = (!_.isEmpty(process.env.DATABASE_URL)) ? process.env.DATABASE_URL : {
  25. host: WIKI.config.db.host,
  26. user: WIKI.config.db.user,
  27. password: WIKI.config.db.pass,
  28. database: WIKI.config.db.db,
  29. port: WIKI.config.db.port
  30. }
  31. const dbUseSSL = (WIKI.config.db.ssl === true || WIKI.config.db.ssl === 'true' || WIKI.config.db.ssl === 1 || WIKI.config.db.ssl === '1')
  32. switch (WIKI.config.db.type) {
  33. case 'postgres':
  34. dbClient = 'pg'
  35. if (dbUseSSL && _.isPlainObject(dbConfig)) {
  36. dbConfig.ssl = true
  37. }
  38. break
  39. case 'mariadb':
  40. case 'mysql':
  41. dbClient = 'mysql2'
  42. if (dbUseSSL && _.isPlainObject(dbConfig)) {
  43. dbConfig.ssl = true
  44. }
  45. // Fix mysql boolean handling...
  46. dbConfig.typeCast = (field, next) => {
  47. if (field.type === 'TINY' && field.length === 1) {
  48. let value = field.string()
  49. return value ? (value === '1') : null
  50. }
  51. return next()
  52. }
  53. break
  54. case 'mssql':
  55. dbClient = 'mssql'
  56. if (_.isPlainObject(dbConfig)) {
  57. dbConfig.appName = 'Wiki.js'
  58. if (dbUseSSL) {
  59. dbConfig.encrypt = true
  60. }
  61. }
  62. break
  63. case 'sqlite':
  64. dbClient = 'sqlite3'
  65. dbConfig = { filename: WIKI.config.db.storage }
  66. break
  67. default:
  68. WIKI.logger.error('Invalid DB Type')
  69. process.exit(1)
  70. }
  71. this.knex = Knex({
  72. client: dbClient,
  73. useNullAsDefault: true,
  74. asyncStackTraces: WIKI.IS_DEBUG,
  75. connection: dbConfig,
  76. pool: {
  77. ...WIKI.config.pool,
  78. async afterCreate(conn, done) {
  79. // -> Set Connection App Name
  80. switch (WIKI.config.db.type) {
  81. case 'postgres':
  82. await conn.query(`set application_name = 'Wiki.js'`)
  83. done()
  84. break
  85. default:
  86. done()
  87. break
  88. }
  89. }
  90. },
  91. debug: WIKI.IS_DEBUG
  92. })
  93. Objection.Model.knex(this.knex)
  94. // Load DB Models
  95. const models = autoload(path.join(WIKI.SERVERPATH, 'models'))
  96. // Set init tasks
  97. let conAttempts = 0
  98. let initTasks = {
  99. // -> Attempt initial connection
  100. async connect () {
  101. try {
  102. WIKI.logger.info('Connecting to database...')
  103. await self.knex.raw('SELECT 1 + 1;')
  104. WIKI.logger.info('Database Connection Successful [ OK ]')
  105. } catch (err) {
  106. if (conAttempts < 10) {
  107. WIKI.logger.error(`Database Connection Error: ${err.code} ${err.address}:${err.port}`)
  108. WIKI.logger.warn(`Will retry in 3 seconds... [Attempt ${++conAttempts} of 10]`)
  109. await new Promise(resolve => setTimeout(resolve, 3000))
  110. await initTasks.connect()
  111. } else {
  112. throw err
  113. }
  114. }
  115. },
  116. // -> Migrate DB Schemas
  117. async syncSchemas () {
  118. return self.knex.migrate.latest({
  119. tableName: 'migrations',
  120. migrationSource
  121. })
  122. },
  123. // -> Migrate DB Schemas from beta
  124. async migrateFromBeta () {
  125. return migrateFromBeta.migrate(self.knex)
  126. }
  127. }
  128. let initTasksQueue = (WIKI.IS_MASTER) ? [
  129. initTasks.connect,
  130. initTasks.migrateFromBeta,
  131. initTasks.syncSchemas
  132. ] : [
  133. () => { return Promise.resolve() }
  134. ]
  135. // Perform init tasks
  136. this.onReady = Promise.each(initTasksQueue, t => t()).return(true)
  137. return {
  138. ...this,
  139. ...models
  140. }
  141. }
  142. }