db.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. WIKI.logger.error(`Database Connection Error: ${err.code} ${err.address}:${err.port}`)
  127. WIKI.logger.warn(`Will retry in 3 seconds... [Attempt ${++conAttempts} of 10]`)
  128. await new Promise(resolve => setTimeout(resolve, 3000))
  129. await initTasks.connect()
  130. } else {
  131. throw err
  132. }
  133. }
  134. },
  135. // -> Migrate DB Schemas
  136. async syncSchemas () {
  137. return self.knex.migrate.latest({
  138. tableName: 'migrations',
  139. migrationSource
  140. })
  141. },
  142. // -> Migrate DB Schemas from beta
  143. async migrateFromBeta () {
  144. return migrateFromBeta.migrate(self.knex)
  145. }
  146. }
  147. let initTasksQueue = (WIKI.IS_MASTER) ? [
  148. initTasks.connect,
  149. initTasks.migrateFromBeta,
  150. initTasks.syncSchemas
  151. ] : [
  152. () => { return Promise.resolve() }
  153. ]
  154. // Perform init tasks
  155. this.onReady = Promise.each(initTasksQueue, t => t()).return(true)
  156. return {
  157. ...this,
  158. ...models
  159. }
  160. }
  161. }