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.

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