Browse Source

refactor: migrate to objection.js + knex

NGPixel 7 years ago
parent
commit
c9b643fbf0
46 changed files with 943 additions and 740 deletions
  1. 3 6
      client/components/setup.vue
  2. 3 2
      config.sample.yml
  3. 27 27
      package.json
  4. 1 52
      server/core/auth.js
  5. 10 13
      server/core/config.js
  6. 48 84
      server/core/db.js
  7. 1 5
      server/core/localization.js
  8. 75 0
      server/db/migrations/2.0.0.js
  9. 48 0
      server/db/models/groups.js
  10. 34 0
      server/db/models/locales.js
  11. 31 0
      server/db/models/settings.js
  12. 235 0
      server/db/models/users.js
  13. 11 0
      server/db/seeds/settings.js
  14. 2 2
      server/graph/resolvers/authentication.js
  15. 15 35
      server/graph/resolvers/group.js
  16. 1 6
      server/graph/resolvers/localization.js
  17. 6 3
      server/graph/resolvers/system.js
  18. 12 29
      server/graph/resolvers/user.js
  19. 2 1
      server/jobs/fetch-graph-locale.js
  20. 2 2
      server/jobs/sync-graph-locales.js
  21. 0 210
      server/models/user.js
  22. 0 0
      server/models_old/_relations.js
  23. 0 0
      server/models_old/comment.js
  24. 0 0
      server/models_old/document.js
  25. 0 0
      server/models_old/file.js
  26. 0 0
      server/models_old/folder.js
  27. 0 0
      server/models_old/group.js
  28. 0 0
      server/models_old/locale.js
  29. 0 0
      server/models_old/right.js
  30. 0 0
      server/models_old/setting.js
  31. 0 0
      server/models_old/tag.js
  32. 1 1
      server/modules/authentication/auth0.js
  33. 1 1
      server/modules/authentication/azure.js
  34. 1 1
      server/modules/authentication/discord.js
  35. 1 1
      server/modules/authentication/dropbox.js
  36. 1 1
      server/modules/authentication/facebook.js
  37. 1 1
      server/modules/authentication/github.js
  38. 1 1
      server/modules/authentication/google.js
  39. 1 1
      server/modules/authentication/ldap.js
  40. 4 6
      server/modules/authentication/local.js
  41. 1 1
      server/modules/authentication/microsoft.js
  42. 1 1
      server/modules/authentication/oauth2.js
  43. 1 1
      server/modules/authentication/slack.js
  44. 1 1
      server/modules/authentication/twitch.js
  45. 30 6
      server/setup.js
  46. 330 239
      yarn.lock

+ 3 - 6
client/components/setup.vue

@@ -220,10 +220,11 @@
                     )
                   v-flex(xs6)
                     v-text-field(
+                      ref='adminPasswordConfirm',
                       v-model='conf.adminPasswordConfirm',
                       label='Confirm Password',
                       hint='Verify your password again.',
-                      v-validate='{ required: true, confirmed: `$adminPassword` }',
+                      v-validate='{ required: true, min: 8 }',
                       data-vv-name='adminPasswordConfirm',
                       data-vv-as='Confirm Password',
                       data-vv-scope='admin',
@@ -308,10 +309,6 @@ export default {
     wikiVersion: {
       type: String,
       required: true
-    },
-    langs: {
-      type: Array,
-      required: true
     }
   },
   data() {
@@ -394,7 +391,7 @@ export default {
     async proceedToUpgrade () {
       if (this.state < 5) {
         const validationSuccess = await this.$validator.validateAll('admin')
-        if (!validationSuccess) {
+        if (!validationSuccess || this.conf.adminPassword !== this.conf.adminPasswordConfirm) {
           this.state = 4
           return
         }

+ 3 - 2
config.sample.yml

@@ -23,12 +23,13 @@ paths:
 # ---------------------------------------------------------------------
 # Supported Database Engines:
 # - postgres = PostgreSQL 9.5 or later
-# - mysql = MySQL 5.7.8 or later
+# - mysql = MySQL 8.0 / MariaDB 10.2.7 or later
+# - mssql = MS SQL Server 2012 or later
 # - sqlite = SQLite 3.9 or later
 
 db:
   type: postgres
-  # PostgreSQL and MySQL only:
+  # PostgreSQL / MySQL / MariaDB / MS SQL Server only:
   host: localhost
   port: 5432
   user: wikijs

+ 27 - 27
package.json

@@ -9,7 +9,6 @@
     "restart": "node wiki restart",
     "dev": "node wiki dev",
     "build": "webpack --profile --config dev/webpack/webpack.prod.js",
-    "build:locales": "node dev/tasks/localization",
     "watch": "webpack --config dev/webpack/webpack.dev.js",
     "test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest"
   },
@@ -43,9 +42,9 @@
     "axios": "0.18.0",
     "bcryptjs-then": "1.0.1",
     "bluebird": "3.5.1",
-    "body-parser": "1.18.2",
+    "body-parser": "1.18.3",
     "bugsnag": "2.3.1",
-    "bull": "3.4.1",
+    "bull": "3.4.2",
     "cheerio": "1.0.0-rc.2",
     "child-process-promise": "2.2.1",
     "chokidar": "2.0.3",
@@ -61,15 +60,15 @@
     "express-brute": "1.0.1",
     "express-brute-redis": "0.0.1",
     "express-session": "1.15.6",
-    "file-type": "7.7.1",
+    "file-type": "8.0.0",
     "filesize.js": "1.0.2",
-    "follow-redirects": "1.4.1",
+    "follow-redirects": "1.5.0",
     "fs-extra": "6.0.1",
     "getos": "3.1.0",
     "graphql": "0.13.2",
     "graphql-list-fields": "2.0.2",
     "graphql-tools": "3.0.1",
-    "i18next": "11.3.1",
+    "i18next": "11.3.2",
     "i18next-express-middleware": "1.1.1",
     "i18next-localstorage-cache": "1.1.1",
     "i18next-node-fs-backend": "1.0.0",
@@ -78,6 +77,7 @@
     "js-yaml": "3.11.0",
     "jsonwebtoken": "8.2.1",
     "klaw": "2.1.1",
+    "knex": "0.14.6",
     "lodash": "4.17.10",
     "markdown-it": "8.4.1",
     "markdown-it-abbr": "1.0.4",
@@ -96,12 +96,14 @@
     "mathjax-node": "2.1.0",
     "mime-types": "2.1.18",
     "moment": "2.22.1",
-    "moment-timezone": "0.5.16",
-    "mongodb": "3.0.7",
+    "moment-timezone": "0.5.17",
+    "mongodb": "3.1.0-beta4",
+    "mssql": "4.1.0",
     "multer": "1.3.0",
     "mysql2": "1.5.3",
     "node-2fa": "1.1.2",
     "oauth2orize": "1.11.0",
+    "objection": "1.1.8",
     "ora": "2.1.0",
     "passport": "0.4.0",
     "passport-auth0": "0.6.1",
@@ -117,20 +119,18 @@
     "passport-slack": "0.0.7",
     "passport-twitch": "1.0.3",
     "passport-windowslive": "1.0.2",
-    "pg": "6.4.2",
+    "pg": "7.4.3",
     "pg-hstore": "2.3.2",
-    "pg-promise": "7.5.3",
-    "pm2": "2.10.3",
+    "pm2": "2.10.4",
     "pug": "2.0.3",
     "qr-image": "3.2.0",
-    "raven": "2.6.1",
+    "raven": "2.6.2",
     "read-chunk": "2.1.0",
     "remove-markdown": "0.2.2",
-    "request": "2.85.0",
+    "request": "2.86.0",
     "request-promise": "4.2.2",
     "scim-query-filter-parser": "1.1.0",
     "semver": "5.5.0",
-    "sequelize": "4.37.7",
     "serve-favicon": "2.5.0",
     "sqlite3": "4.0.0",
     "uuid": "3.2.1",
@@ -145,7 +145,7 @@
     "apollo-client-preset": "1.0.8",
     "apollo-fetch": "0.7.0",
     "apollo-link-batch-http": "1.2.2",
-    "autoprefixer": "8.4.1",
+    "autoprefixer": "8.5.0",
     "babel-cli": "6.26.0",
     "babel-core": "6.26.3",
     "babel-eslint": "8.2.3",
@@ -169,7 +169,7 @@
     "eslint": "4.19.1",
     "eslint-config-requarks": "1.0.7",
     "eslint-config-standard": "11.0.0",
-    "eslint-plugin-import": "2.11.0",
+    "eslint-plugin-import": "2.12.0",
     "eslint-plugin-node": "6.0.1",
     "eslint-plugin-promise": "3.7.0",
     "eslint-plugin-standard": "3.1.0",
@@ -183,8 +183,8 @@
     "html-webpack-pug-plugin": "0.3.0",
     "i18next-xhr-backend": "1.5.1",
     "ignore-loader": "0.1.2",
-    "jest": "22.4.3",
-    "jest-junit": "3.7.0",
+    "jest": "22.4.4",
+    "jest-junit": "4.0.0",
     "js-cookie": "2.2.0",
     "lodash-webpack-plugin": "0.11.5",
     "mini-css-extract-plugin": "0.4.0",
@@ -196,7 +196,7 @@
     "postcss-flexibility": "2.0.0",
     "postcss-import": "11.1.0",
     "postcss-loader": "2.1.5",
-    "postcss-selector-parser": "4.0.0",
+    "postcss-selector-parser": "5.0.0-rc.3",
     "pug-lint": "2.5.0",
     "pug-loader": "2.4.0",
     "pug-plain-loader": "1.0.0",
@@ -218,23 +218,23 @@
     "vue-clipboards": "1.2.4",
     "vue-codemirror": "4.0.5",
     "vue-hot-reload-api": "2.3.0",
-    "vue-loader": "15.0.10",
+    "vue-loader": "15.1.0",
     "vue-material-design-icons": "1.4.0",
-    "vue-moment": "3.2.0",
+    "vue-moment": "4.0.0-0",
     "vue-router": "3.0.1",
     "vue-simple-breakpoints": "1.0.3",
     "vue-template-compiler": "2.5.16",
-    "vuetify": "1.0.17",
+    "vuetify": "1.0.18",
     "vuex": "3.0.1",
-    "vuex-persistedstate": "2.5.2",
-    "webpack": "4.8.2",
-    "webpack-bundle-analyzer": "2.11.2",
+    "vuex-persistedstate": "2.5.4",
+    "webpack": "4.8.3",
+    "webpack-bundle-analyzer": "2.12.0",
     "webpack-cli": "2.1.3",
     "webpack-dev-middleware": "3.1.3",
-    "webpack-hot-middleware": "2.22.1",
+    "webpack-hot-middleware": "2.22.2",
     "webpack-merge": "4.1.2",
     "whatwg-fetch": "2.0.4",
-    "write-file-webpack-plugin": "4.2.0"
+    "write-file-webpack-plugin": "4.3.2"
   },
   "browserslist": [
     "> 1%",

+ 1 - 52
server/core/auth.js

@@ -18,7 +18,7 @@ module.exports = {
     })
 
     passport.deserializeUser(function (id, done) {
-      WIKI.db.User.findById(id).then((user) => {
+      WIKI.db.users.query().findById(id).then((user) => {
         if (user) {
           done(null, user)
         } else {
@@ -58,57 +58,6 @@ module.exports = {
       WIKI.logger.info(`Authentication Provider ${strategy.title}: [ OK ]`)
     })
 
-    // Create Guest account for first-time
-
-    WIKI.db.User.findOne({
-      where: {
-        provider: 'local',
-        email: 'guest@example.com'
-      }
-    }).then((c) => {
-      if (c < 1) {
-        return WIKI.db.User.create({
-          provider: 'local',
-          email: 'guest@example.com',
-          name: 'Guest',
-          password: '',
-          role: 'guest'
-        }).then(() => {
-          WIKI.logger.info('[AUTH] Guest account created successfully!')
-          return true
-        }).catch((err) => {
-          WIKI.logger.error('[AUTH] An error occured while creating guest account:')
-          WIKI.logger.error(err)
-          return err
-        })
-      }
-    })
-
-    // .then(() => {
-    //   if (process.env.WIKI_JS_HEROKU) {
-    //     return WIKI.db.User.findOne({ provider: 'local', email: process.env.WIKI_ADMIN_EMAIL }).then((c) => {
-    //       if (c < 1) {
-    //         // Create root admin account (HEROKU ONLY)
-
-    //         return WIKI.db.User.create({
-    //           provider: 'local',
-    //           email: process.env.WIKI_ADMIN_EMAIL,
-    //           name: 'Administrator',
-    //           password: '$2a$04$MAHRw785Xe/Jd5kcKzr3D.VRZDeomFZu2lius4gGpZZ9cJw7B7Mna', // admin123 (default)
-    //           role: 'admin'
-    //         }).then(() => {
-    //           WIKI.logger.info('[AUTH] Root admin account created successfully!')
-    //           return true
-    //         }).catch((err) => {
-    //           WIKI.logger.error('[AUTH] An error occured while creating root admin account:')
-    //           WIKI.logger.error(err)
-    //           return err
-    //         })
-    //       } else { return true }
-    //     })
-    //   } else { return true }
-    // })
-
     return this
   }
 }

+ 10 - 13
server/core/config.js

@@ -59,17 +59,10 @@ module.exports = {
       subsets = WIKI.data.configNamespaces
     }
 
-    let results = await WIKI.db.Setting.findAll({
-      attributes: ['key', 'config'],
-      where: {
-        key: {
-          $in: subsets
-        }
-      }
-    })
+    let results = await WIKI.db.settings.query().select(['key', 'value']).whereIn('key', subsets)
     if (_.isArray(results) && results.length === subsets.length) {
       results.forEach(result => {
-        WIKI.config[result.key] = result.config
+        WIKI.config[result.key] = result.value
       })
       return true
     } else {
@@ -88,14 +81,18 @@ module.exports = {
       subsets = WIKI.data.configNamespaces
     }
 
+    let trx = await WIKI.db.Objection.transaction.start(WIKI.db.knex)
+
     try {
       for (let set of subsets) {
-        await WIKI.db.Setting.upsert({
-          key: set,
-          config: _.get(WIKI.config, set, {})
-        })
+        console.info(set)
+        await WIKI.db.settings.query(trx).patch({
+          value: _.get(WIKI.config, set, {})
+        }).where('key', set)
       }
+      await trx.commit()
     } catch (err) {
+      await trx.rollback(err)
       WIKI.logger.error(`Failed to save configuration to DB: ${err.message}`)
       return false
     }

+ 48 - 84
server/core/db.js

@@ -1,55 +1,18 @@
 const _ = require('lodash')
-const fs = require('fs')
+const autoload = require('auto-load')
 const path = require('path')
 const Promise = require('bluebird')
-const Sequelize = require('sequelize')
+const Knex = require('knex')
+const Objection = require('objection')
 
 /* global WIKI */
 
-const operatorsAliases = {
-  $eq: Sequelize.Op.eq,
-  $ne: Sequelize.Op.ne,
-  $gte: Sequelize.Op.gte,
-  $gt: Sequelize.Op.gt,
-  $lte: Sequelize.Op.lte,
-  $lt: Sequelize.Op.lt,
-  $not: Sequelize.Op.not,
-  $in: Sequelize.Op.in,
-  $notIn: Sequelize.Op.notIn,
-  $is: Sequelize.Op.is,
-  $like: Sequelize.Op.like,
-  $notLike: Sequelize.Op.notLike,
-  $iLike: Sequelize.Op.iLike,
-  $notILike: Sequelize.Op.notILike,
-  $regexp: Sequelize.Op.regexp,
-  $notRegexp: Sequelize.Op.notRegexp,
-  $iRegexp: Sequelize.Op.iRegexp,
-  $notIRegexp: Sequelize.Op.notIRegexp,
-  $between: Sequelize.Op.between,
-  $notBetween: Sequelize.Op.notBetween,
-  $overlap: Sequelize.Op.overlap,
-  $contains: Sequelize.Op.contains,
-  $contained: Sequelize.Op.contained,
-  $adjacent: Sequelize.Op.adjacent,
-  $strictLeft: Sequelize.Op.strictLeft,
-  $strictRight: Sequelize.Op.strictRight,
-  $noExtendRight: Sequelize.Op.noExtendRight,
-  $noExtendLeft: Sequelize.Op.noExtendLeft,
-  $and: Sequelize.Op.and,
-  $or: Sequelize.Op.or,
-  $any: Sequelize.Op.any,
-  $all: Sequelize.Op.all,
-  $values: Sequelize.Op.values,
-  $col: Sequelize.Op.col
-}
-
 /**
  * PostgreSQL DB module
  */
 module.exports = {
-  Sequelize,
-  Op: Sequelize.Op,
-
+  Objection,
+  knex: null,
   /**
    * Initialize DB
    *
@@ -57,65 +20,63 @@ module.exports = {
    */
   init() {
     let self = this
-    let dbModelsPath = path.join(WIKI.SERVERPATH, 'models')
 
-    // Define Sequelize instance
-
-    this.inst = new this.Sequelize(WIKI.config.db.db, WIKI.config.db.user, WIKI.config.db.pass, {
+    let dbClient = null
+    const dbConfig = (!_.isEmpty(process.env.WIKI_DB_CONNSTR)) ? process.env.WIKI_DB_CONNSTR : {
       host: WIKI.config.db.host,
+      user: WIKI.config.db.user,
+      password: WIKI.config.db.pass,
+      database: WIKI.config.db.db,
       port: WIKI.config.db.port,
-      dialect: WIKI.config.db.type,
-      storage: WIKI.config.db.storage,
-      pool: {
-        max: 10,
-        min: 0,
-        idle: 10000
-      },
-      logging: log => { WIKI.logger.log('debug', log) },
-      operatorsAliases
-    })
+      filename: WIKI.config.db.storage
+    }
 
-    // Attempt to connect and authenticate to DB
+    switch (WIKI.config.db.type) {
+      case 'postgres':
+        dbClient = 'pg'
+        break
+      case 'mysql':
+        dbClient = 'mysql2'
+        break
+      case 'mssql':
+        dbClient = 'mssql'
+        break
+      case 'sqlite':
+        dbClient = 'sqlite3'
+        break
+      default:
+        WIKI.logger.error('Invalid DB Type')
+        process.exit(1)
+    }
 
-    this.inst.authenticate().then(() => {
-      WIKI.logger.info(`Database (${WIKI.config.db.type}) connection: [ OK ]`)
-    }).catch(err => {
-      WIKI.logger.error(`Failed to connect to ${WIKI.config.db.type} instance.`)
-      WIKI.logger.error(err)
-      process.exit(1)
+    this.knex = Knex({
+      client: dbClient,
+      useNullAsDefault: true,
+      connection: dbConfig,
+      debug: WIKI.IS_DEBUG
     })
 
-    // Load DB Models
-
-    fs
-      .readdirSync(dbModelsPath)
-      .filter(file => {
-        return (file.indexOf('.') !== 0 && file.indexOf('_') !== 0)
-      })
-      .forEach(file => {
-        let modelName = _.upperFirst(_.camelCase(_.split(file, '.')[0]))
-        self[modelName] = self.inst.import(path.join(dbModelsPath, file))
-      })
+    Objection.Model.knex(this.knex)
 
-    // Associate DB Models
+    // Load DB Models
 
-    require(path.join(dbModelsPath, '_relations.js'))(self)
+    const models = autoload(path.join(WIKI.SERVERPATH, 'db/models'))
 
     // Set init tasks
 
     let initTasks = {
-      // -> Sync DB Schemas
-      syncSchemas() {
-        return self.inst.sync({
-          force: false,
-          logging: log => { WIKI.logger.log('debug', log) }
+      // -> Migrate DB Schemas
+      async syncSchemas() {
+        return self.knex.migrate.latest({
+          directory: path.join(WIKI.SERVERPATH, 'db/migrations'),
+          tableName: 'migrations'
         })
       },
       // -> Set Connection App Name
-      setAppName() {
+      async setAppName() {
         switch (WIKI.config.db.type) {
           case 'postgres':
-            return self.inst.query(`set application_name = 'WIKI.js'`, { raw: true })
+            return self.knex.raw(`set application_name = 'Wiki.js'`)
         }
       }
     }
@@ -131,6 +92,9 @@ module.exports = {
 
     this.onReady = Promise.each(initTasksQueue, t => t()).return(true)
 
-    return this
+    return {
+      ...this,
+      ...models
+    }
   }
 }

+ 1 - 5
server/core/localization.js

@@ -52,11 +52,7 @@ module.exports = {
     }
   },
   async loadLocale(locale, opts = { silent: false }) {
-    const res = await WIKI.db.Locale.findOne({
-      where: {
-        code: locale
-      }
-    })
+    const res = await WIKI.db.locales.query().findOne('code', locale)
     if (res) {
       if (_.isPlainObject(res.strings)) {
         _.forOwn(res.strings, (data, ns) => {

+ 75 - 0
server/db/migrations/2.0.0.js

@@ -0,0 +1,75 @@
+exports.up = knex => {
+  return knex.schema
+    // -------------------------------------
+    // GROUPS
+    // -------------------------------------
+    .createTable('groups', table => {
+      table.increments('id').primary()
+
+      table.string('name').notNullable()
+      table.string('createdAt').notNullable()
+      table.string('updatedAt').notNullable()
+    })
+    // -------------------------------------
+    // LOCALES
+    // -------------------------------------
+    .createTable('locales', table => {
+      table.increments('id').primary()
+
+      table.string('code', 2).notNullable().unique()
+      table.json('strings')
+      table.boolean('isRTL').notNullable().defaultTo(false)
+      table.string('name').notNullable()
+      table.string('nativeName').notNullable()
+      table.string('createdAt').notNullable()
+      table.string('updatedAt').notNullable()
+    })
+    // -------------------------------------
+    // SETTINGS
+    // -------------------------------------
+    .createTable('settings', table => {
+      table.increments('id').primary()
+
+      table.string('key').notNullable().unique()
+      table.json('value')
+      table.string('createdAt').notNullable()
+      table.string('updatedAt').notNullable()
+    })
+    // -------------------------------------
+    // USERS
+    // -------------------------------------
+    .createTable('users', table => {
+      table.increments('id').primary()
+
+      table.string('email').notNullable()
+      table.string('name').notNullable()
+      table.string('provider').notNullable().defaultTo('local')
+      table.string('providerId')
+      table.string('password')
+      table.boolean('tfaIsActive').notNullable().defaultTo(false)
+      table.string('tfaSecret')
+      table.enum('role', ['admin', 'guest', 'user']).notNullable().defaultTo('guest')
+      table.string('createdAt').notNullable()
+      table.string('updatedAt').notNullable()
+
+      table.unique(['provider', 'email'])
+    })
+    // -------------------------------------
+    // USER GROUPS
+    // -------------------------------------
+    .createTable('userGroups', table => {
+      table.increments('id').primary()
+
+      table.integer('userId').unsigned().references('id').inTable('users')
+      table.integer('groupId').unsigned().references('id').inTable('groups')
+    })
+}
+
+exports.down = knex => {
+  return knex.schema
+    .dropTableIfExists('userGroups')
+    .dropTableIfExists('groups')
+    .dropTableIfExists('locales')
+    .dropTableIfExists('settings')
+    .dropTableIfExists('users')
+}

+ 48 - 0
server/db/models/groups.js

@@ -0,0 +1,48 @@
+const Model = require('objection').Model
+
+/**
+ * Settings model
+ */
+module.exports = class Group extends Model {
+  static get tableName() { return 'groups' }
+
+  static get jsonSchema () {
+    return {
+      type: 'object',
+      required: ['name'],
+
+      properties: {
+        id: {type: 'integer'},
+        name: {type: 'string'},
+        createdAt: {type: 'string'},
+        updatedAt: {type: 'string'}
+      }
+    }
+  }
+
+  static get relationMappings() {
+    const User = require('./users')
+    return {
+      users: {
+        relation: Model.ManyToManyRelation,
+        modelClass: User,
+        join: {
+          from: 'groups.id',
+          through: {
+            from: 'userGroups.groupId',
+            to: 'userGroups.userId'
+          },
+          to: 'users.id'
+        }
+      }
+    }
+  }
+
+  $beforeUpdate() {
+    this.updatedAt = new Date().toISOString()
+  }
+  $beforeInsert() {
+    this.createdAt = new Date().toISOString()
+    this.updatedAt = new Date().toISOString()
+  }
+}

+ 34 - 0
server/db/models/locales.js

@@ -0,0 +1,34 @@
+const Model = require('objection').Model
+
+/**
+ * Locales model
+ */
+module.exports = class User extends Model {
+  static get tableName() { return 'locales' }
+
+  static get jsonSchema () {
+    return {
+      type: 'object',
+      required: ['code', 'name'],
+
+      properties: {
+        id: {type: 'integer'},
+        code: {type: 'string'},
+        strings: {type: 'object'},
+        isRTL: {type: 'boolean', default: false},
+        name: {type: 'string'},
+        nativeName: {type: 'string'},
+        createdAt: {type: 'string'},
+        updatedAt: {type: 'string'}
+      }
+    }
+  }
+
+  $beforeUpdate() {
+    this.updatedAt = new Date().toISOString()
+  }
+  $beforeInsert() {
+    this.createdAt = new Date().toISOString()
+    this.updatedAt = new Date().toISOString()
+  }
+}

+ 31 - 0
server/db/models/settings.js

@@ -0,0 +1,31 @@
+const Model = require('objection').Model
+
+/**
+ * Settings model
+ */
+module.exports = class User extends Model {
+  static get tableName() { return 'settings' }
+
+  static get jsonSchema () {
+    return {
+      type: 'object',
+      required: ['key', 'value'],
+
+      properties: {
+        id: {type: 'integer'},
+        key: {type: 'string'},
+        value: {type: 'object'},
+        createdAt: {type: 'string'},
+        updatedAt: {type: 'string'}
+      }
+    }
+  }
+
+  $beforeUpdate() {
+    this.updatedAt = new Date().toISOString()
+  }
+  $beforeInsert() {
+    this.createdAt = new Date().toISOString()
+    this.updatedAt = new Date().toISOString()
+  }
+}

+ 235 - 0
server/db/models/users.js

@@ -0,0 +1,235 @@
+/* global WIKI */
+
+const bcrypt = require('bcryptjs-then')
+const _ = require('lodash')
+const tfa = require('node-2fa')
+const securityHelper = require('../../helpers/security')
+const Model = require('objection').Model
+
+const bcryptRegexp = /^\$2[ayb]\$[0-9]{2}\$[A-Za-z0-9./]{53}$/
+
+/**
+ * Users model
+ */
+module.exports = class User extends Model {
+  static get tableName() { return 'users' }
+
+  static get jsonSchema () {
+    return {
+      type: 'object',
+      required: ['email', 'name', 'provider'],
+
+      properties: {
+        id: {type: 'integer'},
+        email: {type: 'string', format: 'email'},
+        name: {type: 'string', minLength: 1, maxLength: 255},
+        provider: {type: 'string', minLength: 1, maxLength: 255},
+        providerId: {type: 'number'},
+        password: {type: 'string'},
+        role: {type: 'string', enum: ['admin', 'guest', 'user']},
+        tfaIsActive: {type: 'boolean', default: false},
+        tfaSecret: {type: 'string'},
+        createdAt: {type: 'string'},
+        updatedAt: {type: 'string'}
+      }
+    }
+  }
+
+  static get relationMappings() {
+    const Group = require('./groups')
+    return {
+      groups: {
+        relation: Model.ManyToManyRelation,
+        modelClass: Group,
+        join: {
+          from: 'users.id',
+          through: {
+            from: 'userGroups.userId',
+            to: 'userGroups.groupId'
+          },
+          to: 'groups.id'
+        }
+      }
+    }
+  }
+
+  async $beforeUpdate(opt, context) {
+    await super.$beforeUpdate(opt, context)
+
+    this.updatedAt = new Date().toISOString()
+
+    if (!(opt.patch && this.password === undefined)) {
+      await this.generateHash()
+    }
+  }
+  async $beforeInsert(context) {
+    await super.$beforeInsert(context)
+
+    this.createdAt = new Date().toISOString()
+    this.updatedAt = new Date().toISOString()
+
+    await this.generateHash()
+  }
+
+  async generateHash() {
+    if (this.password) {
+      if (bcryptRegexp.test(this.password)) { return }
+      this.password = await bcrypt.hash(this.password, 12)
+    }
+  }
+
+  async verifyPassword(pwd) {
+    if (await bcrypt.compare(this.password, pwd) === true) {
+      return true
+    } else {
+      throw new WIKI.Error.AuthLoginFailed()
+    }
+  }
+
+  async enableTFA() {
+    let tfaInfo = tfa.generateSecret({
+      name: WIKI.config.site.title
+    })
+    return this.$query.patch({
+      tfaIsActive: true,
+      tfaSecret: tfaInfo.secret
+    })
+  }
+
+  async disableTFA() {
+    return this.$query.patch({
+      tfaIsActive: false,
+      tfaSecret: ''
+    })
+  }
+
+  async verifyTFA(code) {
+    let result = tfa.verifyToken(this.tfaSecret, code)
+    return (result && _.has(result, 'delta') && result.delta === 0)
+  }
+
+  static async processProfile(profile) {
+    let primaryEmail = ''
+    if (_.isArray(profile.emails)) {
+      let e = _.find(profile.emails, ['primary', true])
+      primaryEmail = (e) ? e.value : _.first(profile.emails).value
+    } else if (_.isString(profile.email) && profile.email.length > 5) {
+      primaryEmail = profile.email
+    } else if (_.isString(profile.mail) && profile.mail.length > 5) {
+      primaryEmail = profile.mail
+    } else if (profile.user && profile.user.email && profile.user.email.length > 5) {
+      primaryEmail = profile.user.email
+    } else {
+      return Promise.reject(new Error(WIKI.lang.t('auth:errors.invaliduseremail')))
+    }
+
+    profile.provider = _.lowerCase(profile.provider)
+    primaryEmail = _.toLower(primaryEmail)
+
+    let user = await WIKI.db.users.query().findOne({
+      email: primaryEmail,
+      provider: profile.provider
+    })
+    if (user) {
+      user.$query().patchAdnFetch({
+        email: primaryEmail,
+        provider: profile.provider,
+        providerId: profile.id,
+        name: profile.displayName || _.split(primaryEmail, '@')[0]
+      })
+    } else {
+      user = await WIKI.db.users.query().insertAndFetch({
+        email: primaryEmail,
+        provider: profile.provider,
+        providerId: profile.id,
+        name: profile.displayName || _.split(primaryEmail, '@')[0]
+      })
+    }
+
+    // Handle unregistered accounts
+    // if (!user && profile.provider !== 'local' && (WIKI.config.auth.defaultReadAccess || profile.provider === 'ldap' || profile.provider === 'azure')) {
+    //   let nUsr = {
+    //     email: primaryEmail,
+    //     provider: profile.provider,
+    //     providerId: profile.id,
+    //     password: '',
+    //     name: profile.displayName || profile.name || profile.cn,
+    //     rights: [{
+    //       role: 'read',
+    //       path: '/',
+    //       exact: false,
+    //       deny: false
+    //     }]
+    //   }
+    //   return WIKI.db.users.query().insert(nUsr)
+    // }
+
+    return user
+  }
+
+  static async login (opts, context) {
+    if (_.has(WIKI.config.auth.strategies, opts.provider)) {
+      _.set(context.req, 'body.email', opts.username)
+      _.set(context.req, 'body.password', opts.password)
+
+      // Authenticate
+      return new Promise((resolve, reject) => {
+        WIKI.auth.passport.authenticate(opts.provider, async (err, user, info) => {
+          if (err) { return reject(err) }
+          if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) }
+
+          // Is 2FA required?
+          if (user.tfaIsActive) {
+            try {
+              let loginToken = await securityHelper.generateToken(32)
+              await WIKI.redis.set(`tfa:${loginToken}`, user.id, 'EX', 600)
+              return resolve({
+                tfaRequired: true,
+                tfaLoginToken: loginToken
+              })
+            } catch (err) {
+              WIKI.logger.warn(err)
+              return reject(new WIKI.Error.AuthGenericError())
+            }
+          } else {
+            // No 2FA, log in user
+            return context.req.logIn(user, err => {
+              if (err) { return reject(err) }
+              resolve({
+                tfaRequired: false
+              })
+            })
+          }
+        })(context.req, context.res, () => {})
+      })
+    } else {
+      throw new WIKI.Error.AuthProviderInvalid()
+    }
+  }
+
+  static async loginTFA(opts, context) {
+    if (opts.securityCode.length === 6 && opts.loginToken.length === 64) {
+      let result = await WIKI.redis.get(`tfa:${opts.loginToken}`)
+      if (result) {
+        let userId = _.toSafeInteger(result)
+        if (userId && userId > 0) {
+          let user = await WIKI.db.users.query().findById(userId)
+          if (user && user.verifyTFA(opts.securityCode)) {
+            return Promise.fromCallback(clb => {
+              context.req.logIn(user, clb)
+            }).return({
+              succeeded: true,
+              message: 'Login Successful'
+            }).catch(err => {
+              WIKI.logger.warn(err)
+              throw new WIKI.Error.AuthGenericError()
+            })
+          } else {
+            throw new WIKI.Error.AuthTFAFailed()
+          }
+        }
+      }
+    }
+    throw new WIKI.Error.AuthTFAInvalid()
+  }
+}

+ 11 - 0
server/db/seeds/settings.js

@@ -0,0 +1,11 @@
+exports.seed = (knex, Promise) => {
+  return knex('settings')
+    .insert([
+      { key: 'auth', value: {} },
+      { key: 'features', value: {} },
+      { key: 'logging', value: {} },
+      { key: 'site', value: {} },
+      { key: 'theme', value: {} },
+      { key: 'uploads', value: {} }
+    ])
+}

+ 2 - 2
server/graph/resolvers/authentication.js

@@ -32,7 +32,7 @@ module.exports = {
   AuthenticationMutation: {
     async login(obj, args, context) {
       try {
-        let authResult = await WIKI.db.User.login(args, context)
+        let authResult = await WIKI.db.users.login(args, context)
         return {
           ...authResult,
           responseResult: graphHelper.generateSuccess('Login success')
@@ -43,7 +43,7 @@ module.exports = {
     },
     async loginTFA(obj, args, context) {
       try {
-        let authResult = await WIKI.db.User.loginTFA(args, context)
+        let authResult = await WIKI.db.users.loginTFA(args, context)
         return {
           ...authResult,
           responseResult: graphHelper.generateSuccess('TFA success')

+ 15 - 35
server/graph/resolvers/group.js

@@ -13,43 +13,32 @@ module.exports = {
   },
   GroupQuery: {
     async list(obj, args, context, info) {
-      return WIKI.db.Group.findAll({
-        attributes: {
-          include: [[WIKI.db.inst.fn('COUNT', WIKI.db.inst.col('users.id')), 'userCount']]
-        },
-        include: [{
-          model: WIKI.db.User,
-          attributes: [],
-          through: {
-            attributes: []
-          }
-        }],
-        raw: true,
-        // TODO: Figure out how to exclude these extra fields...
-        group: ['group.id', 'users->userGroups.createdAt', 'users->userGroups.updatedAt', 'users->userGroups.version', 'users->userGroups.userId', 'users->userGroups.groupId']
-      })
+      return WIKI.db.groups.query().select(
+        'groups.*',
+        WIKI.db.groups.relatedQuery('users').count().as('userCount')
+      )
     },
     async single(obj, args, context, info) {
-      return WIKI.db.Group.findById(args.id)
+      return WIKI.db.groups.query().findById(args.id)
     }
   },
   GroupMutation: {
     async assignUser(obj, args) {
-      const grp = await WIKI.db.Group.findById(args.groupId)
+      const grp = await WIKI.db.groups.query().findById(args.groupId)
       if (!grp) {
         throw new gql.GraphQLError('Invalid Group ID')
       }
-      const usr = await WIKI.db.User.findById(args.userId)
+      const usr = await WIKI.db.users.query().findById(args.userId)
       if (!usr) {
         throw new gql.GraphQLError('Invalid User ID')
       }
-      await grp.addUser(usr)
+      await grp.$relatedQuery('users').relate(usr.id)
       return {
         responseResult: graphHelper.generateSuccess('User has been assigned to group.')
       }
     },
     async create(obj, args) {
-      const group = await WIKI.db.Group.create({
+      const group = await WIKI.db.groups.query().insertAndFetch({
         name: args.name
       })
       return {
@@ -58,36 +47,27 @@ module.exports = {
       }
     },
     async delete(obj, args) {
-      await WIKI.db.Group.destroy({
-        where: {
-          id: args.id
-        },
-        limit: 1
-      })
+      await WIKI.db.groups.query().deleteById(args.id)
       return {
         responseResult: graphHelper.generateSuccess('Group has been deleted.')
       }
     },
     async unassignUser(obj, args) {
-      const grp = await WIKI.db.Group.findById(args.groupId)
+      const grp = await WIKI.db.groups.query().findById(args.groupId)
       if (!grp) {
         throw new gql.GraphQLError('Invalid Group ID')
       }
-      const usr = await WIKI.db.User.findById(args.userId)
+      const usr = await WIKI.db.users.query().findById(args.userId)
       if (!usr) {
         throw new gql.GraphQLError('Invalid User ID')
       }
-      await grp.removeUser(usr)
+      await grp.$relatedQuery('users').unrelate().where('userId', usr.id)
       return {
         responseResult: graphHelper.generateSuccess('User has been unassigned from group.')
       }
     },
     async update(obj, args) {
-      await WIKI.db.Group.update({
-        name: args.name
-      }, {
-        where: { id: args.id }
-      })
+      await WIKI.db.groups.query().patch({ name: args.name }).where('id', args.id)
       return {
         responseResult: graphHelper.generateSuccess('Group has been updated.')
       }
@@ -95,7 +75,7 @@ module.exports = {
   },
   Group: {
     users(grp) {
-      return grp.getUsers()
+      return grp.$relatedQuery('users')
     }
   }
 }

+ 1 - 6
server/graph/resolvers/localization.js

@@ -13,12 +13,7 @@ module.exports = {
   LocalizationQuery: {
     async locales(obj, args, context, info) {
       let remoteLocales = await WIKI.redis.get('locales')
-      let localLocales = await WIKI.db.Locale.findAll({
-        attributes: {
-          exclude: ['strings']
-        },
-        raw: true
-      })
+      let localLocales = await WIKI.db.locales.query().select('id', 'code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt')
       remoteLocales = (remoteLocales) ? JSON.parse(remoteLocales) : localLocales
       return _.map(remoteLocales, rl => {
         let isInstalled = _.some(localLocales, ['code', rl.code])

+ 6 - 3
server/graph/resolvers/system.js

@@ -10,7 +10,8 @@ const path = require('path')
 const dbTypes = {
   mysql: 'MySQL / MariaDB',
   postgres: 'PostgreSQL',
-  sqlite: 'SQLite'
+  sqlite: 'SQLite',
+  mssql: 'MS SQL Server'
 }
 
 module.exports = {
@@ -28,12 +29,14 @@ module.exports = {
         osLabel = `${os.type()} - ${osInfo.dist} (${osInfo.codename || os.platform()}) ${osInfo.release || os.release()} ${os.arch()}`
       }
 
+      console.info(WIKI.db.knex.client)
+
       return {
         configFile: path.join(process.cwd(), 'config.yml'),
         currentVersion: WIKI.version,
         dbType: _.get(dbTypes, WIKI.config.db.type, 'Unknown DB'),
-        dbVersion: WIKI.db.inst.options.databaseVersion,
-        dbHost: WIKI.db.inst.options.host,
+        dbVersion: _.get(WIKI.db, 'knex.client.version', 'Unknown version'),
+        dbHost: WIKI.config.db.host,
         latestVersion: WIKI.version, // TODO
         latestVersionReleaseDate: new Date(), // TODO
         operatingSystem: osLabel,

+ 12 - 29
server/graph/resolvers/user.js

@@ -10,52 +10,35 @@ module.exports = {
   },
   UserQuery: {
     async list(obj, args, context, info) {
-      return WIKI.db.User.findAll({
-        attributes: {
-          exclude: ['password', 'tfaSecret']
-        },
-        raw: true
-      })
+      return WIKI.db.users.query()
+        .select('id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt')
     },
     async search(obj, args, context, info) {
-      return WIKI.db.User.findAll({
-        where: {
-          $or: [
-            { email: { $like: `%${args.query}%` } },
-            { name: { $like: `%${args.query}%` } }
-          ]
-        },
-        limit: 10,
-        attributes: ['id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt'],
-        raw: true
-      })
+      return WIKI.db.users.query()
+        .where('email', 'like', `%${args.query}%`)
+        .orWhere('name', 'like', `%${args.query}%`)
+        .limit(10)
+        .select('id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt')
     },
     async single(obj, args, context, info) {
-      return WIKI.db.User.findById(args.id)
+      return WIKI.db.users.query().findById(args.id)
     }
   },
   UserMutation: {
     create(obj, args) {
-      return WIKI.db.User.create(args)
+      return WIKI.db.users.query().insertAndFetch(args)
     },
     delete(obj, args) {
-      return WIKI.db.User.destroy({
-        where: {
-          id: args.id
-        },
-        limit: 1
-      })
+      return WIKI.db.users.query().deleteById(args.id)
     },
     update(obj, args) {
-      return WIKI.db.User.update({
+      return WIKI.db.users.query().patch({
         email: args.email,
         name: args.name,
         provider: args.provider,
         providerId: args.providerId,
         role: args.role
-      }, {
-        where: { id: args.id }
-      })
+      }).where('id', args.id)
     },
     resetPassword(obj, args) {
       return false

+ 2 - 1
server/jobs/fetch-graph-locale.js

@@ -38,7 +38,8 @@ module.exports = async (job) => {
     const locales = await WIKI.redis.get('locales')
     if (locales) {
       const currentLocale = _.find(JSON.parse(locales), ['code', job.data.locale]) || {}
-      await WIKI.db.Locale.upsert({
+      await WIKI.db.locales.query().delete().where('code', job.data.locale)
+      await WIKI.db.locales.query().insert({
         code: job.data.locale,
         strings: lcObj,
         isRTL: currentLocale.isRTL,

+ 2 - 2
server/jobs/sync-graph-locales.js

@@ -60,13 +60,13 @@ module.exports = async (job) => {
         _.set(lcObj, row.key.replace(':', '.'), row.value)
       })
 
-      await WIKI.db.Locale.upsert({
+      await WIKI.db.locales.query().update({
         code: WIKI.config.site.lang,
         strings: lcObj,
         isRTL: currentLocale.isRTL,
         name: currentLocale.name,
         nativeName: currentLocale.nativeName
-      })
+      }).where('code', WIKI.config.site.lang)
     }
 
     WIKI.logger.info('Syncing locales with Graph endpoint: [ COMPLETED ]')

+ 0 - 210
server/models/user.js

@@ -1,210 +0,0 @@
-/* global WIKI */
-
-const Promise = require('bluebird')
-const bcrypt = require('bcryptjs-then')
-const _ = require('lodash')
-const tfa = require('node-2fa')
-const securityHelper = require('../helpers/security')
-
-/**
- * Users schema
- */
-module.exports = (sequelize, DataTypes) => {
-  let userSchema = sequelize.define('user', {
-    email: {
-      type: DataTypes.STRING,
-      allowNull: false,
-      validate: {
-        isEmail: true
-      }
-    },
-    provider: {
-      type: DataTypes.STRING,
-      allowNull: false
-    },
-    providerId: {
-      type: DataTypes.STRING,
-      allowNull: true
-    },
-    password: {
-      type: DataTypes.STRING,
-      allowNull: true
-    },
-    name: {
-      type: DataTypes.STRING,
-      allowNull: true
-    },
-    role: {
-      type: DataTypes.ENUM('admin', 'user', 'guest'),
-      allowNull: false
-    },
-    tfaIsActive: {
-      type: DataTypes.BOOLEAN,
-      allowNull: false,
-      defaultValue: false
-    },
-    tfaSecret: {
-      type: DataTypes.STRING,
-      allowNull: true
-    }
-  }, {
-    timestamps: true,
-    version: true,
-    indexes: [
-      {
-        unique: true,
-        fields: ['provider', 'email']
-      }
-    ]
-  })
-
-  userSchema.prototype.validatePassword = async function (rawPwd) {
-    if (await bcrypt.compare(rawPwd, this.password) === true) {
-      return true
-    } else {
-      throw new WIKI.Error.AuthLoginFailed()
-    }
-  }
-
-  userSchema.prototype.enableTFA = async function () {
-    let tfaInfo = tfa.generateSecret({
-      name: WIKI.config.site.title
-    })
-    this.tfaIsActive = true
-    this.tfaSecret = tfaInfo.secret
-    return this.save()
-  }
-
-  userSchema.prototype.disableTFA = async function () {
-    this.tfaIsActive = false
-    this.tfaSecret = ''
-    return this.save()
-  }
-
-  userSchema.prototype.verifyTFA = function (code) {
-    let result = tfa.verifyToken(this.tfaSecret, code)
-    return (result && _.has(result, 'delta') && result.delta === 0)
-  }
-
-  userSchema.login = async (opts, context) => {
-    if (_.has(WIKI.config.auth.strategies, opts.provider)) {
-      _.set(context.req, 'body.email', opts.username)
-      _.set(context.req, 'body.password', opts.password)
-
-      // Authenticate
-      return new Promise((resolve, reject) => {
-        WIKI.auth.passport.authenticate(opts.provider, async (err, user, info) => {
-          if (err) { return reject(err) }
-          if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) }
-
-          // Is 2FA required?
-          if (user.tfaIsActive) {
-            try {
-              let loginToken = await securityHelper.generateToken(32)
-              await WIKI.redis.set(`tfa:${loginToken}`, user.id, 'EX', 600)
-              return resolve({
-                tfaRequired: true,
-                tfaLoginToken: loginToken
-              })
-            } catch (err) {
-              WIKI.logger.warn(err)
-              return reject(new WIKI.Error.AuthGenericError())
-            }
-          } else {
-            // No 2FA, log in user
-            return context.req.logIn(user, err => {
-              if (err) { return reject(err) }
-              resolve({
-                tfaRequired: false
-              })
-            })
-          }
-        })(context.req, context.res, () => {})
-      })
-    } else {
-      throw new WIKI.Error.AuthProviderInvalid()
-    }
-  }
-
-  userSchema.loginTFA = async (opts, context) => {
-    if (opts.securityCode.length === 6 && opts.loginToken.length === 64) {
-      let result = await WIKI.redis.get(`tfa:${opts.loginToken}`)
-      if (result) {
-        let userId = _.toSafeInteger(result)
-        if (userId && userId > 0) {
-          let user = await WIKI.db.User.findById(userId)
-          if (user && user.verifyTFA(opts.securityCode)) {
-            return Promise.fromCallback(clb => {
-              context.req.logIn(user, clb)
-            }).return({
-              succeeded: true,
-              message: 'Login Successful'
-            }).catch(err => {
-              WIKI.logger.warn(err)
-              throw new WIKI.Error.AuthGenericError()
-            })
-          } else {
-            throw new WIKI.Error.AuthTFAFailed()
-          }
-        }
-      }
-    }
-    throw new WIKI.Error.AuthTFAInvalid()
-  }
-
-  userSchema.processProfile = (profile) => {
-    let primaryEmail = ''
-    if (_.isArray(profile.emails)) {
-      let e = _.find(profile.emails, ['primary', true])
-      primaryEmail = (e) ? e.value : _.first(profile.emails).value
-    } else if (_.isString(profile.email) && profile.email.length > 5) {
-      primaryEmail = profile.email
-    } else if (_.isString(profile.mail) && profile.mail.length > 5) {
-      primaryEmail = profile.mail
-    } else if (profile.user && profile.user.email && profile.user.email.length > 5) {
-      primaryEmail = profile.user.email
-    } else {
-      return Promise.reject(new Error(WIKI.lang.t('auth:errors.invaliduseremail')))
-    }
-
-    profile.provider = _.lowerCase(profile.provider)
-    primaryEmail = _.toLower(primaryEmail)
-
-    return WIKI.db.User.findOneAndUpdate({
-      email: primaryEmail,
-      provider: profile.provider
-    }, {
-      email: primaryEmail,
-      provider: profile.provider,
-      providerId: profile.id,
-      name: profile.displayName || _.split(primaryEmail, '@')[0]
-    }, {
-      new: true
-    }).then((user) => {
-      // Handle unregistered accounts
-      if (!user && profile.provider !== 'local' && (WIKI.config.auth.defaultReadAccess || profile.provider === 'ldap' || profile.provider === 'azure')) {
-        let nUsr = {
-          email: primaryEmail,
-          provider: profile.provider,
-          providerId: profile.id,
-          password: '',
-          name: profile.displayName || profile.name || profile.cn,
-          rights: [{
-            role: 'read',
-            path: '/',
-            exact: false,
-            deny: false
-          }]
-        }
-        return WIKI.db.User.create(nUsr)
-      }
-      return user || Promise.reject(new Error(WIKI.lang.t('auth:errors:notyetauthorized')))
-    })
-  }
-
-  userSchema.hashPassword = (rawPwd) => {
-    return bcrypt.hash(rawPwd)
-  }
-
-  return userSchema
-}

+ 0 - 0
server/models/_relations.js → server/models_old/_relations.js


+ 0 - 0
server/models/comment.js → server/models_old/comment.js


+ 0 - 0
server/models/document.js → server/models_old/document.js


+ 0 - 0
server/models/file.js → server/models_old/file.js


+ 0 - 0
server/models/folder.js → server/models_old/folder.js


+ 0 - 0
server/models/group.js → server/models_old/group.js


+ 0 - 0
server/models/locale.js → server/models_old/locale.js


+ 0 - 0
server/models/right.js → server/models_old/right.js


+ 0 - 0
server/models/setting.js → server/models_old/setting.js


+ 0 - 0
server/models/tag.js → server/models_old/tag.js


+ 1 - 1
server/modules/authentication/auth0.js

@@ -19,7 +19,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, function (accessToken, refreshToken, profile, cb) {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/azure.js

@@ -24,7 +24,7 @@ module.exports = {
         let waadProfile = jwt.decode(params.id_token)
         waadProfile.id = waadProfile.oid
         waadProfile.provider = 'azure'
-        WIKI.db.User.processProfile(waadProfile).then((user) => {
+        WIKI.db.users.processProfile(waadProfile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/discord.js

@@ -19,7 +19,7 @@ module.exports = {
         callbackURL: conf.callbackURL,
         scope: 'identify email'
       }, function (accessToken, refreshToken, profile, cb) {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/dropbox.js

@@ -19,7 +19,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, (accessToken, refreshToken, profile, cb) => {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/facebook.js

@@ -19,7 +19,7 @@ module.exports = {
         callbackURL: conf.callbackURL,
         profileFields: ['id', 'displayName', 'email']
       }, function (accessToken, refreshToken, profile, cb) {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/github.js

@@ -19,7 +19,7 @@ module.exports = {
         callbackURL: conf.callbackURL,
         scope: ['user:email']
       }, (accessToken, refreshToken, profile, cb) => {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/google.js

@@ -18,7 +18,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, (accessToken, refreshToken, profile, cb) => {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/ldap.js

@@ -33,7 +33,7 @@ module.exports = {
       }, (profile, cb) => {
         profile.provider = 'ldap'
         profile.id = profile.dn
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 4 - 6
server/modules/authentication/local.js

@@ -17,14 +17,12 @@ module.exports = {
         usernameField: 'email',
         passwordField: 'password'
       }, (uEmail, uPassword, done) => {
-        WIKI.db.User.findOne({
-          where: {
-            email: uEmail,
-            provider: 'local'
-          }
+        WIKI.db.users.query().findOne({
+          email: uEmail,
+          provider: 'local'
         }).then((user) => {
           if (user) {
-            return user.validatePassword(uPassword).then(() => {
+            return user.verifyPassword(uPassword).then(() => {
               return done(null, user) || true
             }).catch((err) => {
               return done(err, null)

+ 1 - 1
server/modules/authentication/microsoft.js

@@ -18,7 +18,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, function (accessToken, refreshToken, profile, cb) {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/oauth2.js

@@ -20,7 +20,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, (accessToken, refreshToken, profile, cb) => {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/slack.js

@@ -18,7 +18,7 @@ module.exports = {
         clientSecret: conf.clientSecret,
         callbackURL: conf.callbackURL
       }, (accessToken, refreshToken, profile, cb) => {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 1 - 1
server/modules/authentication/twitch.js

@@ -19,7 +19,7 @@ module.exports = {
         callbackURL: conf.callbackURL,
         scope: 'user_read'
       }, function (accessToken, refreshToken, profile, cb) {
-        WIKI.db.User.processProfile(profile).then((user) => {
+        WIKI.db.users.processProfile(profile).then((user) => {
           return cb(null, user) || true
         }).catch((err) => {
           return cb(err, null) || true

+ 30 - 6
server/setup.js

@@ -5,7 +5,7 @@ const path = require('path')
 module.exports = () => {
   WIKI.config.site = {
     path: '',
-    title: 'WIKI.js'
+    title: 'Wiki.js'
   }
 
   WIKI.system = require('./core/system')
@@ -298,22 +298,46 @@ module.exports = () => {
 
       // Save config to DB
       WIKI.logger.info('Persisting config to DB...')
-      await WIKI.configSvc.saveToDb()
+      await WIKI.db.settings.query().insert([
+        { key: 'auth', value: WIKI.config.auth },
+        { key: 'features', value: WIKI.config.features },
+        { key: 'logging', value: WIKI.config.logging },
+        { key: 'site', value: WIKI.config.site },
+        { key: 'theme', value: WIKI.config.theme },
+        { key: 'uploads', value: WIKI.config.uploads }
+      ])
 
       // Create root administrator
       WIKI.logger.info('Creating root administrator...')
-      await WIKI.db.User.upsert({
+      await WIKI.db.users.query().insert({
         email: req.body.adminEmail,
         provider: 'local',
-        password: await WIKI.db.User.hashPassword(req.body.adminPassword),
+        password: req.body.adminPassword,
         name: 'Administrator',
         role: 'admin',
         tfaIsActive: false
       })
 
+      // Create Guest account
+      WIKI.logger.info('Creating root administrator...')
+      const guestUsr = await WIKI.db.users.query().findOne({
+        provider: 'local',
+        email: 'guest@example.com'
+      })
+      if (!guestUsr) {
+        await WIKI.db.users.query().insert({
+          provider: 'local',
+          email: 'guest@example.com',
+          name: 'Guest',
+          password: '',
+          role: 'guest',
+          tfaIsActive: false
+        })
+      }
+
       // Create default locale
       WIKI.logger.info('Installing default locale...')
-      await WIKI.db.Locale.upsert({
+      await WIKI.db.locales.query().insert({
         code: 'en',
         strings: require('./locales/default.json'),
         isRTL: false,
@@ -330,7 +354,7 @@ module.exports = () => {
 
       WIKI.logger.info('Stopping Setup...')
       WIKI.server.destroy(() => {
-        WIKI.logger.info('Setup stopped. Starting WIKI.js...')
+        WIKI.logger.info('Setup stopped. Starting Wiki.js...')
         _.delay(() => {
           WIKI.kernel.bootMaster()
         }, 1000)

File diff suppressed because it is too large
+ 330 - 239
yarn.lock


Some files were not shown because too many files changed in this diff