Browse Source

feat: locales remote fetch+ deps update + fixes

NGPixel 7 years ago
parent
commit
7786f9042f

+ 0 - 4
client/components/admin/admin-general.vue

@@ -34,12 +34,8 @@
 </template>
 
 <script>
-import IconHomeAlert from 'mdi/home-alert'
 
 export default {
-  components: {
-    IconHomeAlert
-  },
   data() {
     return {
       siteTitle: 'Wiki.js',

+ 54 - 44
client/components/admin/admin-locale.vue

@@ -8,19 +8,35 @@
           v-layout(row wrap)
             v-flex(lg6 xs12)
               v-card
-                v-toolbar(color='primary', dark, dense, flat)
+                v-toolbar(color='grey darken-3', dark, dense, flat)
                   v-toolbar-title
                     .subheading Locale Settings
                 v-card-text
-                  v-select(:items='locales', prepend-icon='public', v-model='selectedLocale', label='Site Locale', persistent-hint, hint='All UI text elements will be displayed in selected language.')
+                  v-select(:items='installedLocales'
+                    prepend-icon='public'
+                    v-model='selectedLocale'
+                    item-text='name'
+                    label='Site Locale'
+                    persistent-hint
+                    hint='All UI text elements will be displayed in selected language.'
+                  )
                     template(slot='item', slot-scope='data')
-                      v-list-tile-avatar
-                        v-avatar.blue.white--text(tile, size='40', v-html='data.item.value.toUpperCase()')
-                      v-list-tile-content
-                        v-list-tile-title(v-html='data.item.text')
-                        v-list-tile-sub-title(v-html='data.item.original')
+                      template(v-if='typeof data.item !== "object"')
+                        v-list-tile-content(v-text='data.item')
+                      template(v-else)
+                        v-list-tile-avatar
+                          v-avatar.blue.white--text(tile, size='40', v-html='data.item.code.toUpperCase()')
+                        v-list-tile-content
+                          v-list-tile-title(v-html='data.item.name')
+                          v-list-tile-sub-title(v-html='data.item.nativeName')
                   v-divider
-                  v-switch(v-model='rtlEnabled', label='RTL Display Mode', color='primary', persistent-hint, hint='For Right-to-Left languages, e.g. Arabic')
+                  v-switch(
+                    v-model='autoUpdate'
+                    label='Update Automatically'
+                    color='primary'
+                    persistent-hint
+                    hint='Automatically download updates to this locale as they become available.'
+                  )
                 v-divider
                 .px-3.pb-3
                   v-btn(color='primary') Save
@@ -30,52 +46,46 @@
                   v-toolbar-title
                     .subheading Download Locale
                 v-list
-                  v-list-tile(@click='')
+                  v-list-tile(v-for='lc in locales')
                     v-list-tile-avatar
-                      v-avatar.teal.white--text(tile, size='40') ZH
+                      v-avatar.teal.white--text(tile, size='40') {{lc.code.toUpperCase()}}
                     v-list-tile-content
-                      v-list-tile-title Chinese
-                      v-list-tile-sub-title 中文
-                    v-list-tile-action
-                      v-btn(icon)
-                        v-icon.grey--text cloud_download
-                  v-list-tile(@click='')
-                    v-list-tile-avatar
-                      v-avatar.teal.white--text(tile, size='40') EN
-                    v-list-tile-content
-                      v-list-tile-title English
-                      v-list-tile-sub-title English
-                    v-list-tile-action
+                      v-list-tile-title(v-html='lc.name')
+                      v-list-tile-sub-title(v-html='lc.nativeName')
+                    v-list-tile-action(v-if='lc.isInstalled && lc.installDate < lc.updatedAt')
+                      v-icon.blue--text cached
+                    v-list-tile-action(v-else-if='lc.isInstalled')
                       v-icon.green--text check
-                  v-list-tile(@click='')
-                    v-list-tile-avatar
-                      v-avatar.teal.white--text(tile, size='40') FR
-                    v-list-tile-content
-                      v-list-tile-title French
-                      v-list-tile-sub-title Français
-                    v-list-tile-action
-                      v-icon.green--text check
-                  v-list-tile(@click='')
-                    v-list-tile-avatar
-                      v-avatar.teal.white--text(tile, size='40') RU
-                    v-list-tile-content
-                      v-list-tile-title Russian
-                      v-list-tile-sub-title Русский
-                    v-list-tile-action
-                      v-btn(icon)
-                        v-icon.blue--text update
+                    v-list-tile-action(v-else)
+                      v-btn(icon, @click='')
+                        v-icon.grey--text cloud_download
 </template>
 
 <script>
+import _ from 'lodash'
+
+import localesQuery from 'gql/admin-locale-query-list.gql'
+
 export default {
   data() {
     return {
-      locales: [
-        { text: 'English', original: 'English', value: 'en' },
-        { text: 'French', original: 'Français', value: 'fr' }
-      ],
+      locales: [],
       selectedLocale: 'en',
-      rtlEnabled: false
+      autoUpdate: true
+    }
+  },
+  computed: {
+    installedLocales() {
+      return _.filter(this.locales, ['isInstalled', true])
+    }
+  },
+  apollo: {
+    locales: {
+      query: localesQuery,
+      update: (data) => data.localization.locales,
+      watchLoading (isLoading) {
+        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-locale-refresh')
+      }
     }
   }
 }

+ 5 - 5
client/components/admin/admin-theme.vue

@@ -8,7 +8,7 @@
           v-layout(row wrap)
             v-flex(lg6 xs12)
               v-card
-                v-toolbar(color='primary', dark, dense, flat)
+                v-toolbar(color='grey darken-3', dark, dense, flat)
                   v-toolbar-title
                     .subheading Theme
                 v-card-text
@@ -20,16 +20,16 @@
                         v-list-tile-title(v-html='data.item.text')
                         v-list-tile-sub-title(v-html='data.item.author')
                   v-divider
-                  v-switch(v-model='darkMode', label='Dark Mode', color='primary', persistent-hint, hint='Not recommended for accessibility')
+                  v-switch(v-model='darkMode', label='Dark Mode', color='primary', persistent-hint, hint='Not recommended for accessibility.')
                 v-divider
                 .px-3.pb-3
                   v-btn(color='primary') Save
             v-flex(lg6 xs12)
               v-card
-                v-toolbar(color='primary', dark, dense, flat)
+                v-toolbar(color='teal', dark, dense, flat)
                   v-toolbar-title
-                    .subheading ---
-                v-card-text ---
+                    .subheading Download Themes
+                v-card-text.caption -- Coming soon --
 </template>
 
 <script>

+ 17 - 4
client/components/admin/admin-utilities.vue

@@ -14,7 +14,7 @@
           v-layout(row, wrap)
             v-flex(xs12, sm6)
               v-card
-                v-toolbar(color='primary', dark, dense, flat)
+                v-toolbar(color='grey darken-3', dark, dense, flat)
                   v-toolbar-title
                     .subheading Authentication
                 v-subheader Flush User Sessions
@@ -31,14 +31,25 @@
                     span Proceed
             v-flex(xs12, sm6)
               v-card
-                v-toolbar(color='primary', dark, dense, flat)
+                v-toolbar(color='grey darken-3', dark, dense, flat)
                   v-toolbar-title
                     .subheading Maintenance Mode
                 v-card-text
                   .body-1 Maintenance mode restrict access to the site to administrators only, regarless of current permissions.
-                  v-btn.mt-3(color='orange darken-2', dark)
+                  v-btn.mt-3.ml-0(color='orange darken-2', depressed, dark)
                     icon-home-alert.mr-2(fillColor='#FFFFFF')
                     | Turn On Maintenance Mode
+              v-card.mt-3
+                v-toolbar(color='grey darken-3', dark, dense, flat)
+                  v-toolbar-title
+                    .subheading Graph Endpoint
+                v-card-text
+                  .body-1 The Graph API Endpoint from which remote resources like locales, themes and plugins are fetched.
+                  .caption Do not change unless you know what you're doing!
+                  v-text-field.my-2.grey.lighten-3(solo, flat, label='Graph Endpoint', value='https://graph.requarks.io')
+                  v-btn.ml-0(color='primary', depressed, dark)
+                    v-icon(left) chevron_right
+                    span Save
 
       v-tab-item(key='telemetry', :transition='false', :reverse-transition='false')
         v-card.pa-3
@@ -110,10 +121,12 @@
 
 <script>
 import IconGithubCircle from 'mdi/github-circle'
+import IconHomeAlert from 'mdi/home-alert'
 
 export default {
   components: {
-    IconGithubCircle
+    IconGithubCircle,
+    IconHomeAlert
   },
   data() {
     return {

+ 14 - 31
client/components/setup.vue

@@ -111,7 +111,7 @@
             v-form
               v-container
                 v-layout(row, wrap)
-                  v-flex(xs12)
+                  v-flex(xs12, sm6).pr-3
                     v-text-field(
                       v-model='conf.title',
                       label='Site Title',
@@ -124,24 +124,20 @@
                       data-vv-scope='general',
                       :error-messages='errors.collect(`siteTitle`)'
                     )
-                v-layout(row, wrap).mt-3
-                  v-flex.pr-3(xs12, sm4)
-                    v-select(
-                      v-model='conf.lang',
-                      :items='langs',
-                      label='Site UI Language',
-                      item-value='id',
-                      item-text='name',
+                  v-flex.pr-3(xs12, sm6)
+                    v-text-field(
+                      v-model='conf.port',
+                      label='Server Port',
                       persistent-hint,
-                      hint='The language in which navigation, help and other UI elements will be displayed.'
+                      hint='The port on which Wiki.js will listen to. Usually port 80 if connecting directly, or a random port (e.g. 3000) if using a web server in front of it. Set $(PORT) to use the PORT environment variable.',
+                      v-validate='{ required: true }',
+                      data-vv-name='port',
+                      data-vv-as='Port',
+                      data-vv-scope='general',
+                      :error-messages='errors.collect(`port`)'
                     )
-                      template(slot='item', slot-scope='data')
-                        v-list-tile-avatar
-                          v-avatar.blue.white--text(tile, size='40', v-html='data.item.id.toUpperCase()')
-                        v-list-tile-content
-                          v-list-tile-title(v-html='data.item.name')
-                          v-list-tile-sub-title(v-html='data.item.original')
-                  v-flex(xs12, sm8)
+                v-layout(row, wrap).mt-3
+                  v-flex(xs12, sm6).pr-3
                     v-text-field(
                       v-model='conf.pathContent',
                       label='Content Data Path',
@@ -153,20 +149,7 @@
                       data-vv-scope='general',
                       :error-messages='errors.collect(`pathContent`)'
                     )
-                v-layout(row, wrap).mt-3
-                  v-flex.pr-3(xs12, sm4)
-                    v-text-field(
-                      v-model='conf.port',
-                      label='Server Port',
-                      persistent-hint,
-                      hint='The port on which Wiki.js will listen to. Usually port 80 if connecting directly, or a random port (e.g. 3000) if using a web server in front of it. Set $(PORT) to use the PORT environment variable.',
-                      v-validate='{ required: true }',
-                      data-vv-name='port',
-                      data-vv-as='Port',
-                      data-vv-scope='general',
-                      :error-messages='errors.collect(`port`)'
-                    )
-                  v-flex(xs12, sm8)
+                  v-flex(xs12, sm6)
                     v-text-field(
                       v-model='conf.pathData',
                       label='Temporary Data Path',

+ 14 - 0
client/graph/admin-locale-query-list.gql

@@ -0,0 +1,14 @@
+{
+  localization {
+    locales {
+      code
+      createdAt
+      isInstalled
+      installDate
+      isRTL
+      name
+      nativeName
+      updatedAt
+    }
+  }
+}

+ 1 - 0
config.sample.yml

@@ -76,4 +76,5 @@ workers: 0
 
 ha:
   node: primary
+  uid: master
   readonly: false

+ 16 - 37
dev/webpack/webpack.dev.js

@@ -2,6 +2,7 @@ const webpack = require('webpack')
 const path = require('path')
 const fs = require('fs-extra')
 
+const { VueLoaderPlugin } = require('vue-loader')
 const CopyWebpackPlugin = require('copy-webpack-plugin')
 const HtmlWebpackPlugin = require('html-webpack-plugin')
 const HtmlWebpackPugPlugin = require('html-webpack-pug-plugin')
@@ -73,6 +74,12 @@ module.exports = {
             options: {
               sourceMap: false
             }
+          },
+          {
+            loader: 'sass-resources-loader',
+            options: {
+              resources: path.join(process.cwd(), '/client/scss/global.scss')
+            }
           }
         ]
       },
@@ -87,43 +94,14 @@ module.exports = {
       },
       {
         test: /\.vue$/,
-        loader: 'vue-loader',
-        options: {
-          loaders: {
-            scss: [
-              'vue-style-loader',
-              'css-loader',
-              'postcss-loader',
-              {
-                loader: 'sass-loader',
-                options: {
-                  sourceMap: false
-                }
-              },
-              {
-                loader: 'sass-resources-loader',
-                options: {
-                  resources: path.join(process.cwd(), '/client/scss/global.scss')
-                }
-              }
-            ],
-            js: [
-              {
-                loader: 'cache-loader',
-                options: {
-                  cacheDirectory: cacheDir
-                }
-              },
-              {
-                loader: 'babel-loader',
-                options: {
-                  babelrc: path.join(process.cwd(), '.babelrc'),
-                  cacheDirectory: babelDir
-                }
-              }
-            ]
-          }
-        }
+        loader: 'vue-loader'
+      },
+      {
+        test: /\.pug$/,
+        exclude: [
+          path.join(process.cwd(), 'dev')
+        ],
+        loader: 'pug-plain-loader'
       },
       {
         test: /\.(png|jpg|gif)$/,
@@ -182,6 +160,7 @@ module.exports = {
     ]
   },
   plugins: [
+    new VueLoaderPlugin(),
     new CopyWebpackPlugin([
       { from: 'client/static' },
       { from: './node_modules/graphql-voyager/dist/voyager.worker.js', to: 'js/' }

+ 21 - 43
dev/webpack/webpack.prod.js

@@ -11,6 +11,7 @@ const OfflinePlugin = require('offline-plugin')
 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
 const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
 const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin')
+const { VueLoaderPlugin } = require('vue-loader')
 
 const babelConfig = fs.readJsonSync(path.join(process.cwd(), '.babelrc'))
 const cacheDir = '.webpack-cache/cache'
@@ -26,8 +27,8 @@ module.exports = {
   output: {
     path: path.join(process.cwd(), 'assets'),
     publicPath: '/',
-    filename: 'js/[name].js',
-    chunkFilename: 'js/[name].js',
+    filename: 'js/[name].[hash].js',
+    chunkFilename: 'js/[name].[chunkhash].js',
     globalObject: 'this'
   },
   module: {
@@ -78,6 +79,12 @@ module.exports = {
             options: {
               sourceMap: false
             }
+          },
+          {
+            loader: 'sass-resources-loader',
+            options: {
+              resources: path.join(process.cwd(), '/client/scss/global.scss')
+            }
           }
         ]
       },
@@ -93,44 +100,14 @@ module.exports = {
       },
       {
         test: /\.vue$/,
-        loader: 'vue-loader',
-        options: {
-          loaders: {
-            scss: [
-              'vue-style-loader',
-              MiniCssExtractPlugin.loader,
-              'css-loader',
-              'postcss-loader',
-              {
-                loader: 'sass-loader',
-                options: {
-                  sourceMap: false
-                }
-              },
-              {
-                loader: 'sass-resources-loader',
-                options: {
-                  resources: path.join(process.cwd(), '/client/scss/global.scss')
-                }
-              }
-            ],
-            js: [
-              {
-                loader: 'cache-loader',
-                options: {
-                  cacheDirectory: cacheDir
-                }
-              },
-              {
-                loader: 'babel-loader',
-                options: {
-                  babelrc: path.join(process.cwd(), '.babelrc'),
-                  cacheDirectory: babelDir
-                }
-              }
-            ]
-          }
-        }
+        loader: 'vue-loader'
+      },
+      {
+        test: /\.pug$/,
+        exclude: [
+          path.join(process.cwd(), 'dev')
+        ],
+        loader: 'pug-plain-loader'
       },
       {
         test: /\.(png|jpg|gif)$/,
@@ -189,19 +166,20 @@ module.exports = {
     ]
   },
   plugins: [
+    new VueLoaderPlugin(),
     new webpack.BannerPlugin('Wiki.js - wiki.js.org - Licensed under AGPL'),
     new CopyWebpackPlugin([
       { from: 'client/static' },
       { from: './node_modules/graphql-voyager/dist/voyager.worker.js', to: 'js/' }
     ], {}),
     new MiniCssExtractPlugin({
-      filename: 'css/bundle.css',
-      chunkFilename: 'css/[name].css'
+      filename: 'css/bundle.[hash].css',
+      chunkFilename: 'css/[name].[chunkhash].css'
     }),
     new HtmlWebpackPlugin({
       template: 'dev/templates/master.pug',
       filename: '../server/views/master.pug',
-      hash: true,
+      hash: false,
       inject: 'head'
     }),
     new HtmlWebpackPugPlugin(),

+ 55 - 54
package.json

@@ -38,17 +38,17 @@
     "node": ">=8.9.3"
   },
   "dependencies": {
-    "apollo-server-express": "1.3.2",
+    "apollo-server-express": "1.3.6",
     "auto-load": "3.0.0",
     "axios": "0.18.0",
     "bcryptjs-then": "1.0.1",
     "bluebird": "3.5.1",
     "body-parser": "1.18.2",
     "bugsnag": "2.3.1",
-    "bull": "3.3.10",
+    "bull": "3.4.1",
     "cheerio": "1.0.0-rc.2",
     "child-process-promise": "2.2.1",
-    "chokidar": "2.0.2",
+    "chokidar": "2.0.3",
     "compression": "1.7.2",
     "connect-redis": "3.3.3",
     "cookie-parser": "1.4.3",
@@ -66,18 +66,18 @@
     "follow-redirects": "1.4.1",
     "fs-extra": "5.0.0",
     "graphql": "0.13.2",
-    "graphql-list-fields": "2.0.1",
-    "graphql-tools": "2.23.1",
-    "i18next": "10.5.1",
+    "graphql-list-fields": "2.0.2",
+    "graphql-tools": "3.0.0",
+    "i18next": "11.2.3",
     "i18next-express-middleware": "1.1.1",
     "i18next-localstorage-cache": "1.1.1",
     "i18next-node-fs-backend": "1.0.0",
     "image-size": "0.6.2",
     "ioredis": "3.2.2",
     "js-yaml": "3.11.0",
-    "jsonwebtoken": "8.2.0",
+    "jsonwebtoken": "8.2.1",
     "klaw": "2.1.1",
-    "lodash": "4.17.5",
+    "lodash": "4.17.10",
     "markdown-it": "8.4.1",
     "markdown-it-abbr": "1.0.4",
     "markdown-it-anchor": "4.0.0",
@@ -92,11 +92,11 @@
     "markdown-it-sub": "1.0.0",
     "markdown-it-sup": "1.0.0",
     "markdown-it-task-lists": "2.1.1",
-    "mathjax-node": "2.0.1",
+    "mathjax-node": "2.1.0",
     "mime-types": "2.1.18",
-    "moment": "2.21.0",
-    "moment-timezone": "0.5.14",
-    "mongodb": "3.0.4",
+    "moment": "2.22.1",
+    "moment-timezone": "0.5.16",
+    "mongodb": "3.0.7",
     "multer": "1.3.0",
     "mysql2": "1.5.3",
     "node-2fa": "1.1.2",
@@ -119,18 +119,18 @@
     "pg": "6.4.2",
     "pg-hstore": "2.3.2",
     "pg-promise": "7.5.3",
-    "pm2": "2.10.1",
-    "pug": "2.0.1",
+    "pm2": "2.10.3",
+    "pug": "2.0.3",
     "qr-image": "3.2.0",
-    "raven": "2.4.2",
+    "raven": "2.6.0",
     "read-chunk": "2.1.0",
     "remove-markdown": "0.2.2",
     "request": "2.85.0",
     "request-promise": "4.2.2",
     "scim-query-filter-parser": "1.1.0",
     "semver": "5.5.0",
-    "sequelize": "4.37.1",
-    "serve-favicon": "2.4.5",
+    "sequelize": "4.37.6",
+    "serve-favicon": "2.5.0",
     "sqlite3": "4.0.0",
     "uuid": "3.2.1",
     "validator": "9.4.1",
@@ -140,14 +140,14 @@
   },
   "devDependencies": {
     "@panter/vue-i18next": "0.9.1",
-    "@vue/cli": "3.0.0-beta.6",
+    "@vue/cli": "3.0.0-beta.9",
     "apollo-client-preset": "1.0.8",
     "apollo-fetch": "0.7.0",
-    "apollo-link-batch-http": "1.2.1",
-    "autoprefixer": "8.1.0",
+    "apollo-link-batch-http": "1.2.2",
+    "autoprefixer": "8.3.0",
     "babel-cli": "6.26.0",
-    "babel-core": "6.26.0",
-    "babel-eslint": "8.2.2",
+    "babel-core": "6.26.3",
+    "babel-eslint": "8.2.3",
     "babel-jest": "23.0.0-alpha.0",
     "babel-loader": "7.1.4",
     "babel-plugin-graphql-tag": "1.5.0",
@@ -164,74 +164,75 @@
     "copy-webpack-plugin": "4.5.1",
     "css-loader": "0.28.11",
     "cssnano": "4.0.0-rc.2",
-    "duplicate-package-checker-webpack-plugin": "2.1.0",
-    "eslint": "4.19.0",
+    "duplicate-package-checker-webpack-plugin": "3.0.0",
+    "eslint": "4.19.1",
     "eslint-config-requarks": "1.0.7",
     "eslint-config-standard": "11.0.0",
-    "eslint-plugin-import": "2.9.0",
+    "eslint-plugin-import": "2.11.0",
     "eslint-plugin-node": "6.0.1",
     "eslint-plugin-promise": "3.7.0",
-    "eslint-plugin-standard": "3.0.1",
-    "eslint-plugin-vue": "4.3.0",
+    "eslint-plugin-standard": "3.1.0",
+    "eslint-plugin-vue": "4.5.0",
     "file-loader": "1.1.11",
     "graphiql": "0.11.11",
-    "graphql-tag": "^2.8.0",
+    "graphql-tag": "^2.9.1",
     "graphql-voyager": "1.0.0-rc.15",
     "hammerjs": "2.0.8",
-    "html-webpack-plugin": "3.0.6",
+    "html-webpack-plugin": "3.2.0",
     "html-webpack-pug-plugin": "0.3.0",
     "i18next-xhr-backend": "1.5.1",
     "ignore-loader": "0.1.2",
-    "jest": "22.4.2",
-    "jest-junit": "3.6.0",
+    "jest": "22.4.3",
+    "jest-junit": "3.7.0",
     "js-cookie": "2.2.0",
-    "lodash-webpack-plugin": "0.11.4",
-    "mini-css-extract-plugin": "0.2.0",
-    "node-sass": "4.7.2",
-    "offline-plugin": "4.9.0",
+    "lodash-webpack-plugin": "0.11.5",
+    "mini-css-extract-plugin": "0.4.0",
+    "node-sass": "4.9.0",
+    "offline-plugin": "5.0.1",
     "optimize-css-assets-webpack-plugin": "4.0.0",
     "postcss-cssnext": "3.1.0",
     "postcss-flexbugs-fixes": "3.3.0",
     "postcss-flexibility": "2.0.0",
     "postcss-import": "11.1.0",
-    "postcss-loader": "2.1.2",
-    "postcss-selector-parser": "3.1.1",
+    "postcss-loader": "2.1.4",
+    "postcss-selector-parser": "4.0.0",
     "pug-lint": "2.5.0",
-    "pug-loader": "2.3.0",
+    "pug-loader": "2.4.0",
+    "pug-plain-loader": "1.0.0",
     "raw-loader": "0.5.1",
-    "react": "16.2.0",
-    "react-dom": "16.2.0",
-    "sass-loader": "6.0.7",
+    "react": "16.3.2",
+    "react-dom": "16.3.2",
+    "sass-loader": "7.0.1",
     "sass-resources-loader": "1.3.3",
     "script-ext-html-webpack-plugin": "2.0.1",
     "simple-progress-webpack-plugin": "1.1.2",
-    "style-loader": "0.20.3",
+    "style-loader": "0.21.0",
     "stylus": "0.54.5",
     "stylus-loader": "3.0.2",
     "twemoji-awesome": "1.0.6",
-    "vee-validate": "2.0.5",
+    "vee-validate": "2.0.8",
     "velocity-animate": "1.5.1",
     "vue": "2.5.16",
     "vue-apollo": "3.0.0-beta.5",
-    "vue-clipboards": "1.2.2",
-    "vue-codemirror": "4.0.3",
+    "vue-clipboards": "1.2.4",
+    "vue-codemirror": "4.0.5",
     "vue-hot-reload-api": "2.3.0",
-    "vue-loader": "14.2.1",
-    "vue-material-design-icons": "1.2.1",
+    "vue-loader": "15.0.4",
+    "vue-material-design-icons": "1.4.0",
     "vue-moment": "3.2.0",
     "vue-router": "3.0.1",
     "vue-simple-breakpoints": "1.0.3",
     "vue-template-compiler": "2.5.16",
-    "vuetify": "1.0.8",
+    "vuetify": "1.0.17",
     "vuex": "3.0.1",
-    "vuex-persistedstate": "2.5.1",
-    "webpack": "4.1.1",
+    "vuex-persistedstate": "2.5.2",
+    "webpack": "4.6.0",
     "webpack-bundle-analyzer": "2.11.1",
-    "webpack-cli": "2.0.12",
-    "webpack-dev-middleware": "3.0.1",
-    "webpack-hot-middleware": "2.21.2",
+    "webpack-cli": "2.0.15",
+    "webpack-dev-middleware": "3.1.3",
+    "webpack-hot-middleware": "2.22.1",
     "webpack-merge": "4.1.2",
-    "whatwg-fetch": "2.0.3",
+    "whatwg-fetch": "2.0.4",
     "write-file-webpack-plugin": "4.2.0"
   },
   "browserslist": [

+ 8 - 79
server/app/data.yml

@@ -27,6 +27,7 @@ defaults:
     workers: 0
     ha:
       node: primary
+      uid: master
       readonly: false
     # DB defaults
     auth:
@@ -62,88 +63,16 @@ configNamespaces:
 localeNamespaces:
   - auth
   - common
-queues:
-  - gitSync
-  - uplClearTemp
+jobs:
+  purgeUploads:
+    onInit: true
+    cron: '*/15 * * * *'
+  syncGraphLocales:
+    onInit: true
+    cron: '0 0 * * *'
 telemetry:
   BUGSNAG_ID: 'bb4b324d0675bcbba10025617fd2cec8'
   BUGSNAG_REMOTE: 'https://notify.bugsnag.com'
   GA_ID: 'UA-9094100-7'
   GA_REMOTE: 'https://www.google-analytics.com/collect'
-authProviders:
-  - local
-  - microsoft
-  - google
-  - facebook
-  - github
-  - slack
-  - ldap
-  - azure
-colors:
-  - red
-  - pink
-  - purple
-  - deep-purple
-  - indigo
-  - blue
-  - light-blue
-  - cyan
-  - teal
-  - green
-  - light-green
-  - lime
-  - yellow
-  - amber
-  - orange
-  - deep-orange
-  - brown
-  - grey
-  - blue-grey
-langs:
-  -
-    id: en
-    name: English
-    original: English
-  -
-    id: zh
-    name: Chinese
-    original: 中文
-  -
-    id: nl
-    name: Dutch
-    original: Nederlands
-  -
-    id: fr
-    name: French
-    original: Français
-  -
-    id: de
-    name: German
-    original: Deutsch
-  -
-    id: ja
-    name: Japanese
-    original: 日本語
-  -
-    id: ko
-    name: Korean
-    original: 한국어
-  -
-    id: fa
-    name: Persian (Fārsi)
-    original: فارسی
-  -
-    id: pt
-    name: Portuguese
-    original: Português
-  -
-    id: ru
-    name: Russian
-    original: Русский
-  -
-    id: es
-    name: Spanish
-    original: Español
-rtlLangs:
-  - fa
 # ---------------------------------

+ 2 - 8
server/core/kernel.js

@@ -10,7 +10,7 @@ module.exports = {
   init() {
     if (cluster.isMaster) {
       WIKI.logger.info('=======================================')
-      WIKI.logger.info('= WIKI.js =============================')
+      WIKI.logger.info('= Wiki.js =============================')
       WIKI.logger.info('=======================================')
 
       WIKI.redis = require('./redis').init()
@@ -55,13 +55,7 @@ module.exports = {
   async postBootMaster() {
     await require('../master')()
 
-    _.times(this.numWorkers, () => {
-      this.spawnWorker()
-    })
-
-    WIKI.queue.uplClearTemp.add({}, {
-      repeat: { cron: '*/15 * * * *' }
-    })
+    WIKI.queue.start()
 
     cluster.on('exit', (worker, code, signal) => {
       if (!global.DEV) {

+ 2 - 7
server/core/localization.js

@@ -1,9 +1,7 @@
 const _ = require('lodash')
 const dotize = require('dotize')
-const i18nBackend = require('i18next-node-fs-backend')
 const i18nMW = require('i18next-express-middleware')
 const i18next = require('i18next')
-const path = require('path')
 const Promise = require('bluebird')
 
 /* global WIKI */
@@ -14,17 +12,14 @@ module.exports = {
   init() {
     this.namespaces = WIKI.data.localeNamespaces
     this.engine = i18next
-    this.engine.use(i18nBackend).init({
+    this.engine.init({
       load: 'languageOnly',
       ns: this.namespaces,
       defaultNS: 'common',
       saveMissing: false,
       preload: [WIKI.config.site.lang],
       lng: WIKI.config.site.lang,
-      fallbackLng: 'en',
-      backend: {
-        loadPath: path.join(WIKI.SERVERPATH, 'locales/{{lng}}/{{ns}}.yml')
-      }
+      fallbackLng: 'en'
     })
     return this
   },

+ 2 - 3
server/core/logger.js

@@ -1,17 +1,16 @@
 const _ = require('lodash')
-const cluster = require('cluster')
 const winston = require('winston')
 
 /* global WIKI */
 
 module.exports = {
   loggers: {},
-  init() {
+  init(uid) {
     let logger = winston.createLogger({
       level: WIKI.config.logLevel,
       format: winston.format.combine(
         winston.format.colorize(),
-        winston.format.label({ label: (cluster.isMaster) ? 'MASTER' : `WORKER-${cluster.worker.id}` }),
+        winston.format.label({ label: uid }),
         winston.format.timestamp(),
         winston.format.printf(info => `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`)
       )

+ 23 - 6
server/core/queue.js

@@ -1,23 +1,40 @@
+const path = require('path')
 const Bull = require('bull')
 const Promise = require('bluebird')
+const _ = require('lodash')
 
 /* global WIKI */
 
 module.exports = {
+  job: {},
   init() {
-    WIKI.data.queues.forEach(queueName => {
-      this[queueName] = new Bull(queueName, {
-        prefix: `q-${WIKI.config.ha.nodeuid}`,
+    _.forOwn(WIKI.data.jobs, (queueParams, queueName) => {
+      this.job[queueName] = new Bull(queueName, {
+        prefix: `q-${WIKI.config.ha.uid}`,
         redis: WIKI.config.redis
       })
+      this.job[queueName].process(path.join(WIKI.SERVERPATH, `jobs/${_.kebabCase(queueName)}.js`))
     })
     return this
   },
-  clean() {
-    return Promise.each(WIKI.data.queues, queueName => {
+  start() {
+    _.forOwn(WIKI.data.jobs, (queueParams, queueName) => {
+      if (queueParams.onInit) {
+        this.job[queueName].add({}, {
+          removeOnComplete: true
+        })
+      }
+      this.job[queueName].add({}, {
+        repeat: { cron: queueParams.cron },
+        removeOnComplete: true
+      })
+    })
+  },
+  async clean() {
+    return Promise.each(_.keys(WIKI.data.jobs), queueName => {
       return new Promise((resolve, reject) => {
         let keyStream = WIKI.redis.scanStream({
-          match: `q-${WIKI.config.ha.nodeuid}:${queueName}:*`
+          match: `q-${WIKI.config.ha.uid}:${queueName}:*`
         })
         keyStream.on('data', resultKeys => {
           if (resultKeys.length > 0) {

+ 18 - 0
server/core/worker.js

@@ -0,0 +1,18 @@
+const path = require('path')
+
+let WIKI = {
+  IS_DEBUG: process.env.NODE_ENV === 'development',
+  ROOTPATH: process.cwd(),
+  SERVERPATH: path.join(process.cwd(), 'server'),
+  Error: require('../helpers/error'),
+  configSvc: require('./config')
+}
+global.WIKI = WIKI
+
+WIKI.configSvc.init()
+
+// ----------------------------------------
+// Init Logger
+// ----------------------------------------
+
+WIKI.logger = require('./logger').init('JOB')

+ 46 - 0
server/graph/resolvers/localization.js

@@ -0,0 +1,46 @@
+const graphHelper = require('../../helpers/graph')
+const _ = require('lodash')
+
+/* global WIKI */
+
+module.exports = {
+  Query: {
+    async localization() { return {} }
+  },
+  Mutation: {
+    async localization() { return {} }
+  },
+  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
+      })
+      remoteLocales = (remoteLocales) ? JSON.parse(remoteLocales) : localLocales
+      return _.map(remoteLocales, rl => {
+        let isInstalled = _.some(localLocales, ['code', rl.code])
+        return {
+          ...rl,
+          isInstalled,
+          installDate: isInstalled ? _.find(localLocales, ['code', rl.code]).updatedAt : null
+        }
+      })
+    }
+  },
+  LocalizationMutation: {
+    async updateLocale(obj, args, context) {
+      try {
+        let authResult = await WIKI.db.User.login(args, context)
+        return {
+          ...authResult,
+          responseResult: graphHelper.generateSuccess('Login success')
+        }
+      } catch (err) {
+        return graphHelper.generateError(err)
+      }
+    }
+  }
+}

+ 45 - 0
server/graph/schemas/localization.graphql

@@ -0,0 +1,45 @@
+# ===============================================
+# LOCALIZATION
+# ===============================================
+
+extend type Query {
+  localization: LocalizationQuery
+}
+
+extend type Mutation {
+  localization: LocalizationMutation
+}
+
+# -----------------------------------------------
+# QUERIES
+# -----------------------------------------------
+
+type LocalizationQuery {
+  locales: [LocalizationLocale]
+}
+
+# -----------------------------------------------
+# MUTATIONS
+# -----------------------------------------------
+
+type LocalizationMutation {
+  updateLocale(
+    localeId: String!
+    autoUpdate: Boolean!
+  ): DefaultResponse
+}
+
+# -----------------------------------------------
+# TYPES
+# -----------------------------------------------
+
+type LocalizationLocale {
+  code: String!
+  createdAt: Date!
+  installDate: Date
+  isInstalled: Boolean!
+  isRTL: Boolean!
+  name: String!
+  nativeName: String!
+  updatedAt: Date!
+}

+ 1 - 1
server/index.js

@@ -23,7 +23,7 @@ WIKI.configSvc.init()
 // Init Logger
 // ----------------------------------------
 
-WIKI.logger = require('./core/logger').init()
+WIKI.logger = require('./core/logger').init('MASTER')
 
 // ----------------------------------------
 // Init Telemetry

+ 33 - 0
server/jobs/purge-uploads.js

@@ -0,0 +1,33 @@
+require('../core/worker')
+
+/* global WIKI */
+
+const Promise = require('bluebird')
+const fs = Promise.promisifyAll(require('fs-extra'))
+const moment = require('moment')
+const path = require('path')
+
+module.exports = async (job) => {
+  WIKI.logger.info('Purging orphaned upload files...')
+
+  try {
+    const uplTempPath = path.resolve(process.cwd(), WIKI.config.paths.data, 'temp-upload')
+    const ls = await fs.readdirAsync(uplTempPath)
+    const fifteenAgo = moment().subtract(15, 'minutes')
+
+    await Promise.map(ls, (f) => {
+      return fs.statAsync(path.join(uplTempPath, f)).then((s) => { return { filename: f, stat: s } })
+    }).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
+      return Promise.map(arrFiles, (f) => {
+        if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
+          return fs.unlinkAsync(path.join(uplTempPath, f.filename))
+        }
+      })
+    })
+
+    WIKI.logger.info('Purging orphaned upload files: [ COMPLETED ]')
+  } catch (err) {
+    WIKI.logger.error('Purging orphaned upload files: [ FAILED ]')
+    WIKI.logger.error(err.message)
+  }
+}

+ 69 - 0
server/jobs/sync-git.js

@@ -0,0 +1,69 @@
+'use strict'
+
+// /* global WIKI */
+
+// const Promise = require('bluebird')
+// const fs = Promise.promisifyAll(require('fs-extra'))
+// const klaw = require('klaw')
+// const moment = require('moment')
+// const path = require('path')
+// const entryHelper = require('../helpers/entry')
+
+module.exports = (job) => {
+  return true
+  // return WIKI.git.resync().then(() => {
+  //   // -> Stream all documents
+
+  //   let cacheJobs = []
+  //   let jobCbStreamDocsResolve = null
+  //   let jobCbStreamDocs = new Promise((resolve, reject) => {
+  //     jobCbStreamDocsResolve = resolve
+  //   })
+
+  //   klaw(WIKI.REPOPATH).on('data', function (item) {
+  //     if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
+  //       let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
+  //       let cachePath = entryHelper.getCachePath(entryPath)
+
+  //       // -> Purge outdated cache
+
+  //       cacheJobs.push(
+  //         fs.statAsync(cachePath).then((st) => {
+  //           return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
+  //         }).catch((err) => {
+  //           return (err.code !== 'EEXIST') ? err : 'new'
+  //         }).then((fileStatus) => {
+  //           // -> Delete expired cache file
+
+  //           if (fileStatus === 'expired') {
+  //             return fs.unlinkAsync(cachePath).return(fileStatus)
+  //           }
+
+  //           return fileStatus
+  //         }).then((fileStatus) => {
+  //           // -> Update cache and search index
+
+  //           if (fileStatus !== 'active') {
+  //             return global.entries.updateCache(entryPath).then(entry => {
+  //               process.send({
+  //                 action: 'searchAdd',
+  //                 content: entry
+  //               })
+  //               return true
+  //             })
+  //           }
+
+  //           return true
+  //         })
+  //       )
+  //     }
+  //   }).on('end', () => {
+  //     jobCbStreamDocsResolve(Promise.all(cacheJobs))
+  //   })
+
+  //   return jobCbStreamDocs
+  // }).then(() => {
+  //   WIKI.logger.info('Git remote repository sync: DONE')
+  //   return true
+  // })
+}

+ 38 - 0
server/jobs/sync-graph-locales.js

@@ -0,0 +1,38 @@
+require('../core/worker')
+const _ = require('lodash')
+const { createApolloFetch } = require('apollo-fetch')
+
+/* global WIKI */
+
+WIKI.redis = require('../core/redis').init()
+const apollo = createApolloFetch({
+  uri: 'https://graph.requarks.io'
+})
+
+module.exports = async (job) => {
+  WIKI.logger.info('Syncing locales with Graph endpoint...')
+
+  try {
+    const resp = await apollo({
+      query: `{
+        localization {
+          locales {
+            code
+            name
+            nativeName
+            isRTL
+            createdAt
+            updatedAt
+          }
+        }
+      }`
+    })
+    const locales = _.sortBy(_.get(resp, 'data.localization.locales', []), 'name').map(lc => ({...lc, isInstalled: (lc.code === 'en')}))
+    WIKI.redis.set('locales', JSON.stringify(locales))
+
+    WIKI.logger.info('Syncing locales with Graph endpoint: [ COMPLETED ]')
+  } catch (err) {
+    WIKI.logger.error('Syncing locales with Graph endpoint: [ FAILED ]')
+    WIKI.logger.error(err.message)
+  }
+}

+ 38 - 0
server/locales/default.json

@@ -0,0 +1,38 @@
+{
+  "auth": {
+    "actions": {
+      "login": "Log In"
+    },
+    "errors": {
+      "invalidLogin": "Invalid Login",
+      "invalidLoginMsg": "The email or password is invalid.",
+      "invalidUserEmail": "Invalid User Email",
+      "loginError": "Login error",
+      "notYetAuthorized": "You have not been authorized to login to this site yet.",
+      "tooManyAttempts": "Too many attempts!",
+      "tooManyAttemptsMsg": "You've made too many failed attempts in a short period of time, please try again {{time}}.",
+      "userNotFound": "User not found"
+    },
+    "fields": {
+      "emailUser": "Email / Username",
+      "password": "Password"
+    },
+    "loginRequired": "Login required",
+    "providers": {
+      "azure": "Azure Active Directory",
+      "facebook": "Facebook",
+      "github": "GitHub",
+      "google": "Google ID",
+      "ldap": "LDAP / Active Directory",
+      "local": "Local",
+      "slack": "Slack",
+      "windowslive": "Microsoft Account"
+    },
+    "tfa": {
+      "placeholder": "XXXXXX",
+      "subtitle": "Security code required:",
+      "title": "Two Factor Authentication",
+      "verifyToken": "Verify"
+    }
+  }
+}

+ 39 - 0
server/models/locale.js

@@ -0,0 +1,39 @@
+/**
+ * Locale schema
+ */
+module.exports = (sequelize, DataTypes) => {
+  let localeSchema = sequelize.define('locale', {
+    code: {
+      type: DataTypes.STRING,
+      allowNull: false
+    },
+    strings: {
+      type: DataTypes.JSON,
+      allowNull: true
+    },
+    isRTL: {
+      type: DataTypes.BOOLEAN,
+      allowNull: false,
+      defaultValue: false
+    },
+    name: {
+      type: DataTypes.STRING,
+      allowNull: false
+    },
+    nativeName: {
+      type: DataTypes.STRING,
+      allowNull: false
+    }
+  }, {
+    timestamps: true,
+    version: true,
+    indexes: [
+      {
+        unique: true,
+        fields: ['code']
+      }
+    ]
+  })
+
+  return localeSchema
+}

+ 0 - 68
server/queues/git-sync.js

@@ -1,68 +0,0 @@
-'use strict'
-
-/* global WIKI */
-
-const Promise = require('bluebird')
-const fs = Promise.promisifyAll(require('fs-extra'))
-const klaw = require('klaw')
-const moment = require('moment')
-const path = require('path')
-const entryHelper = require('../helpers/entry')
-
-module.exports = (job) => {
-  return WIKI.git.resync().then(() => {
-    // -> Stream all documents
-
-    let cacheJobs = []
-    let jobCbStreamDocsResolve = null
-    let jobCbStreamDocs = new Promise((resolve, reject) => {
-      jobCbStreamDocsResolve = resolve
-    })
-
-    klaw(WIKI.REPOPATH).on('data', function (item) {
-      if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
-        let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
-        let cachePath = entryHelper.getCachePath(entryPath)
-
-        // -> Purge outdated cache
-
-        cacheJobs.push(
-          fs.statAsync(cachePath).then((st) => {
-            return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
-          }).catch((err) => {
-            return (err.code !== 'EEXIST') ? err : 'new'
-          }).then((fileStatus) => {
-            // -> Delete expired cache file
-
-            if (fileStatus === 'expired') {
-              return fs.unlinkAsync(cachePath).return(fileStatus)
-            }
-
-            return fileStatus
-          }).then((fileStatus) => {
-            // -> Update cache and search index
-
-            if (fileStatus !== 'active') {
-              return global.entries.updateCache(entryPath).then(entry => {
-                process.send({
-                  action: 'searchAdd',
-                  content: entry
-                })
-                return true
-              })
-            }
-
-            return true
-          })
-        )
-      }
-    }).on('end', () => {
-      jobCbStreamDocsResolve(Promise.all(cacheJobs))
-    })
-
-    return jobCbStreamDocs
-  }).then(() => {
-    WIKI.logger.info('Git remote repository sync: DONE')
-    return true
-  })
-}

+ 0 - 29
server/queues/upl-clear-temp.js

@@ -1,29 +0,0 @@
-'use strict'
-
-/* global WIKI */
-
-const Promise = require('bluebird')
-const fs = Promise.promisifyAll(require('fs-extra'))
-const moment = require('moment')
-const path = require('path')
-
-module.exports = (job) => {
-  return fs.readdirAsync(WIKI.UPLTEMPPATH).then((ls) => {
-    let fifteenAgo = moment().subtract(15, 'minutes')
-
-    return Promise.map(ls, (f) => {
-      return fs.statAsync(path.join(WIKI.UPLTEMPPATH, f)).then((s) => { return { filename: f, stat: s } })
-    }).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
-      return Promise.map(arrFiles, (f) => {
-        if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
-          return fs.unlinkAsync(path.join(WIKI.UPLTEMPPATH, f.filename))
-        } else {
-          return true
-        }
-      })
-    })
-  }).then(() => {
-    WIKI.logger.info('Purging temporary upload files: DONE')
-    return true
-  })
-}

+ 11 - 1
server/setup.js

@@ -283,7 +283,7 @@ module.exports = () => {
 
       // Site namespace
       _.set(WIKI.config.site, 'title', req.body.title)
-      _.set(WIKI.config.site, 'lang', req.body.lang)
+      _.set(WIKI.config.site, 'lang', 'en')
       _.set(WIKI.config.site, 'rtl', _.includes(WIKI.data.rtlLangs, req.body.lang))
       _.set(WIKI.config.site, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
 
@@ -310,6 +310,16 @@ module.exports = () => {
         tfaIsActive: false
       })
 
+      // Create default locale
+      WIKI.logger.info('Installing default locale...')
+      await WIKI.db.Locale.upsert({
+        code: 'en',
+        strings: require('./locales/default.json'),
+        isRTL: false,
+        name: 'English',
+        nativeName: 'English'
+      })
+
       WIKI.logger.info('Setup is complete!')
       res.json({
         ok: true,

+ 1 - 1
server/views/main/setup.pug

@@ -3,4 +3,4 @@ extends ../master.pug
 block body
   body
     #app
-      setup(telemetry-id=telemetryClientID, wiki-version=packageObj.version, :langs!=JSON.stringify(data.langs).replace(/"/g, "'"))
+      setup(telemetry-id=telemetryClientID, wiki-version=packageObj.version)

+ 0 - 62
server/worker.js

@@ -1,62 +0,0 @@
-const Promise = require('bluebird')
-
-/* global WIKI */
-
-module.exports = Promise.join(
-  WIKI.db.onReady,
-  WIKI.configSvc.loadFromDb(['features', 'logging', 'site', 'uploads'])
-).then(() => {
-  const path = require('path')
-
-  WIKI.REPOPATH = path.resolve(WIKI.ROOTPATH, WIKI.config.paths.repo)
-  WIKI.DATAPATH = path.resolve(WIKI.ROOTPATH, WIKI.config.paths.data)
-  WIKI.UPLTEMPPATH = path.join(WIKI.DATAPATH, 'temp-upload')
-
-  // ----------------------------------------
-  // Load global modules
-  // ----------------------------------------
-
-  WIKI.lang = require('i18next')
-
-  // ----------------------------------------
-  // Localization Engine
-  // ----------------------------------------
-
-  const i18nBackend = require('i18next-node-fs-backend')
-  WIKI.lang.use(i18nBackend).init({
-    load: 'languageOnly',
-    ns: ['common', 'admin', 'auth', 'errors'],
-    defaultNS: 'common',
-    saveMissing: false,
-    preload: [WIKI.config.lang],
-    lng: WIKI.config.lang,
-    fallbackLng: 'en',
-    backend: {
-      loadPath: path.join(WIKI.SERVERPATH, 'locales/{{lng}}/{{ns}}.yml')
-    }
-  })
-
-  // ----------------------------------------
-  // Start Queues
-  // ----------------------------------------
-
-  const Bull = require('bull')
-  const autoload = require('auto-load')
-
-  let queues = autoload(path.join(WIKI.SERVERPATH, 'queues'))
-
-  for (let queueName in queues) {
-    new Bull(queueName, {
-      prefix: `q-${WIKI.config.ha.nodeuid}`,
-      redis: WIKI.config.redis
-    }).process(queues[queueName])
-  }
-
-  // ----------------------------------------
-  // Shutdown gracefully
-  // ----------------------------------------
-
-  process.on('disconnect', () => {
-    process.exit()
-  })
-})

File diff suppressed because it is too large
+ 312 - 222
yarn.lock


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