Jelajahi Sumber

feat: HA event handling + emitting

NGPixel 5 tahun lalu
induk
melakukan
bd4263ecb2

+ 7 - 7
client/components/common/nav-header.vue

@@ -120,27 +120,27 @@
               v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
                 .overline.pa-4.grey--text {{$t('common:header.currentPage')}}
                 v-list-item.pl-4(@click='pageView', v-if='mode !== `view`')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-file-document-box-outline
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-box-outline
                   v-list-item-title.body-2 {{$t('common:header.view')}}
                 v-list-item.pl-4(@click='pageEdit', v-if='mode !== `edit` && isAuthenticated')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-file-document-edit-outline
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-edit-outline
                   v-list-item-title.body-2 {{$t('common:header.edit')}}
                 v-list-item.pl-4(@click='pageHistory', v-if='mode !== `history`')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-history
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-history
                   v-list-item-content
                     v-list-item-title.body-2 {{$t('common:header.history')}}
                 v-list-item.pl-4(@click='pageSource', v-if='mode !== `source`')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-code-tags
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-code-tags
                   v-list-item-title.body-2 {{$t('common:header.viewSource')}}
                 v-list-item.pl-4(@click='pageDuplicate', v-if='isAuthenticated')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-content-duplicate
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-duplicate
                   v-list-item-title.body-2 {{$t('common:header.duplicate')}}
                 v-list-item.pl-4(@click='pageMove', v-if='isAuthenticated')
-                  v-list-item-avatar(size='24'): v-icon(color='indigo') mdi-content-save-move-outline
+                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-save-move-outline
                   v-list-item-content
                     v-list-item-title.body-2 {{$t('common:header.move')}}
                 v-list-item.pl-4(@click='pageDelete', v-if='isAuthenticated')
-                  v-list-item-avatar(size='24'): v-icon(color='red darken-2') mdi-trash-can-outline
+                  v-list-item-avatar(size='24', tile): v-icon(color='red darken-2') mdi-trash-can-outline
                   v-list-item-title.body-2 {{$t('common:header.delete')}}
             v-divider(vertical)
 

+ 1 - 1
config.sample.yml

@@ -130,7 +130,7 @@ offline: false
 # ---------------------------------------------------------------------
 # Set to true if you have multiple concurrent instances running off the
 # same DB (e.g. Kubernetes pods / load balanced instances). Leave false
-# otherwise.
+# otherwise. You MUST be using PostgreSQL to use this feature.
 
 ha: false
 

+ 17 - 0
server/core/auth.js

@@ -281,6 +281,7 @@ module.exports = {
   async reloadGroups () {
     const groupsArray = await WIKI.models.groups.query()
     this.groups = _.keyBy(groupsArray, 'id')
+    WIKI.auth.guest = await WIKI.models.users.getGuestUser()
   },
 
   /**
@@ -324,6 +325,7 @@ module.exports = {
     ])
 
     await WIKI.auth.activateStrategies()
+    WIKI.events.outbound.emit('reloadAuthStrategies')
 
     WIKI.logger.info('Regenerated certificates: [ COMPLETED ]')
   },
@@ -356,5 +358,20 @@ module.exports = {
     await guestUser.$relatedQuery('groups').relate(guestGroup.id)
 
     WIKI.logger.info('Guest user has been reset: [ COMPLETED ]')
+  },
+
+  /**
+   * Subscribe to HA propagation events
+   */
+  subscribeToEvents() {
+    WIKI.events.inbound.on('reloadGroups', () => {
+      WIKI.auth.reloadGroups()
+    })
+    WIKI.events.inbound.on('reloadApiKeys', () => {
+      WIKI.auth.reloadApiKeys()
+    })
+    WIKI.events.inbound.on('reloadAuthStrategies', () => {
+      WIKI.auth.activateStrategies()
+    })
   }
 }

+ 14 - 1
server/core/config.js

@@ -95,7 +95,7 @@ module.exports = {
    * @param {Array} keys Array of keys to save
    * @returns Promise
    */
-  async saveToDb(keys) {
+  async saveToDb(keys, propagate = true) {
     try {
       for (let key of keys) {
         let value = _.get(WIKI.config, key, null)
@@ -107,6 +107,9 @@ module.exports = {
           await WIKI.models.settings.query().insert({ key, value })
         }
       }
+      if (propagate) {
+        WIKI.events.outbound.emit('reloadConfig')
+      }
     } catch (err) {
       WIKI.logger.error(`Failed to save configuration to DB: ${err.message}`)
       return false
@@ -119,5 +122,15 @@ module.exports = {
    */
   async applyFlags() {
     WIKI.models.knex.client.config.debug = WIKI.config.flags.sqllog
+  },
+
+  /**
+   * Subscribe to HA propagation events
+   */
+  subscribeToEvents() {
+    WIKI.events.inbound.on('reloadConfig', async () => {
+      await WIKI.configSvc.loadFromDb()
+      await WIKI.configSvc.applyFlags()
+    })
   }
 }

+ 14 - 3
server/core/db.js

@@ -203,12 +203,22 @@ module.exports = {
         WIKI.logger.debug(ev)
       }
     })
+
+    // -> Outbound events handling
+
     this.listener.addChannel('wiki', payload => {
       if (_.has(payload.event) && payload.source !== WIKI.INSTANCE_ID) {
-        WIKI.events.emit(payload.event, payload.value)
+        WIKI.logger.debug(`Received event ${payload.event} from instance ${payload.source}: [ OK ]`)
+        WIKI.events.inbound.emit(payload.event, payload.value)
       }
     })
-    WIKI.events.onAny(this.notifyViaDB)
+    WIKI.events.outbound.onAny(this.notifyViaDB)
+
+    // -> Listen to inbound events
+
+    WIKI.auth.subscribeToEvents()
+    WIKI.configSvc.subscribeToEvents()
+    WIKI.models.pages.subscribeToEvents()
 
     WIKI.logger.info(`High-Availability Listener initialized successfully: [ OK ]`)
   },
@@ -217,7 +227,8 @@ module.exports = {
    */
   async unsubscribeToNotifications () {
     if (this.listener) {
-      WIKI.events.offAny(this.notifyViaDB)
+      WIKI.events.outbound.offAny(this.notifyViaDB)
+      WIKI.events.inbound.removeAllListeners()
       this.listener.close()
     }
   },

+ 6 - 2
server/core/kernel.js

@@ -36,8 +36,10 @@ module.exports = {
       WIKI.scheduler = require('./scheduler').init()
       WIKI.servers = require('./servers')
       WIKI.sideloader = require('./sideloader').init()
-      WIKI.events = new EventEmitter()
-      await WIKI.models.subscribeToNotifications()
+      WIKI.events = {
+        inbound: new EventEmitter(),
+        outbound: new EventEmitter()
+      }
     } catch (err) {
       WIKI.logger.error(err)
       process.exit(1)
@@ -77,6 +79,8 @@ module.exports = {
     await WIKI.models.searchEngines.initEngine()
     await WIKI.models.storage.initTargets()
     WIKI.scheduler.start()
+
+    await WIKI.models.subscribeToNotifications()
   },
   /**
    * Init Telemetry

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

@@ -67,8 +67,11 @@ module.exports = {
      */
     async createApiKey (obj, args, context) {
       try {
+        const key = await WIKI.models.apiKeys.createNewKey(args)
+        await WIKI.auth.reloadApiKeys()
+        WIKI.events.outbound.emit('reloadApiKeys')
         return {
-          key: await WIKI.models.apiKeys.createNewKey(args),
+          key,
           responseResult: graphHelper.generateSuccess('API Key created successfully')
         }
       } catch (err) {
@@ -158,6 +161,7 @@ module.exports = {
           isRevoked: true
         })
         await WIKI.auth.reloadApiKeys()
+        WIKI.events.outbound.emit('reloadApiKeys')
         return {
           responseResult: graphHelper.generateSuccess('API Key revoked successfully')
         }
@@ -190,6 +194,7 @@ module.exports = {
           }).where('key', str.key)
         }
         await WIKI.auth.activateStrategies()
+        WIKI.events.outbound.emit('reloadAuthStrategies')
         return {
           responseResult: graphHelper.generateSuccess('Strategies updated successfully')
         }

+ 3 - 0
server/graph/resolvers/group.js

@@ -54,6 +54,7 @@ module.exports = {
         isSystem: false
       })
       await WIKI.auth.reloadGroups()
+      WIKI.events.outbound.emit('reloadGroups')
       return {
         responseResult: graphHelper.generateSuccess('Group created successfully.'),
         group
@@ -62,6 +63,7 @@ module.exports = {
     async delete(obj, args) {
       await WIKI.models.groups.query().deleteById(args.id)
       await WIKI.auth.reloadGroups()
+      WIKI.events.outbound.emit('reloadGroups')
       return {
         responseResult: graphHelper.generateSuccess('Group has been deleted.')
       }
@@ -94,6 +96,7 @@ module.exports = {
       }).where('id', args.id)
 
       await WIKI.auth.reloadGroups()
+      WIKI.events.outbound.emit('reloadGroups')
 
       return {
         responseResult: graphHelper.generateSuccess('Group has been updated.')

+ 1 - 0
server/graph/resolvers/page.js

@@ -434,6 +434,7 @@ module.exports = {
     async flushCache(obj, args, context) {
       try {
         await WIKI.models.pages.flushCache()
+        WIKI.events.outbound.emit('flushCache')
         return {
           responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.')
         }

+ 1 - 0
server/graph/resolvers/system.js

@@ -205,6 +205,7 @@ module.exports = {
 
           if (args.groupMode !== `NONE`) {
             await WIKI.auth.reloadGroups()
+            WIKI.events.outbound.emit('reloadGroups')
           }
 
           client.close()

+ 1 - 1
server/index.js

@@ -4,7 +4,7 @@
 // ===========================================
 
 const path = require('path')
-const nanoid = require('nanoid')
+const { nanoid } = require('nanoid')
 
 let WIKI = {
   IS_DEBUG: process.env.NODE_ENV === 'development',

+ 18 - 3
server/models/pages.js

@@ -436,7 +436,8 @@ module.exports = class Page extends Model {
       localeCode: opts.destinationLocale,
       hash: destinationHash
     }).findById(page.id)
-    await WIKI.models.pages.deletePageFromCache(page)
+    await WIKI.models.pages.deletePageFromCache(page.hash)
+    WIKI.events.outbound.emit('deletePageFromCache', page.hash)
 
     // -> Rebuild page tree
     await WIKI.models.pages.rebuildTree()
@@ -512,7 +513,8 @@ module.exports = class Page extends Model {
 
     // -> Delete page
     await WIKI.models.pages.query().delete().where('id', page.id)
-    await WIKI.models.pages.deletePageFromCache(page)
+    await WIKI.models.pages.deletePageFromCache(page.hash)
+    WIKI.events.outbound.emit('deletePageFromCache', page.hash)
 
     // -> Rebuild page tree
     await WIKI.models.pages.rebuildTree()
@@ -609,7 +611,8 @@ module.exports = class Page extends Model {
       affectedHashes = qryHashes.map(h => h.hash)
     }
     for (const hash of affectedHashes) {
-      await WIKI.models.pages.deletePageFromCache({ hash })
+      await WIKI.models.pages.deletePageFromCache(hash)
+      WIKI.events.outbound.emit('deletePageFromCache', hash)
     }
   }
 
@@ -853,4 +856,16 @@ module.exports = class Page extends Model {
       .replace(/\s\s+/g, ' ')
       .split(' ').filter(w => w.length > 1).join(' ').toLowerCase()
   }
+
+  /**
+   * Subscribe to HA propagation events
+   */
+  static subscribeToEvents() {
+    WIKI.events.inbound.on('deletePageFromCache', hash => {
+      WIKI.models.pages.deletePageFromCache(hash)
+    })
+    WIKI.events.inbound.on('flushCache', () => {
+      WIKI.models.pages.flushCache()
+    })
+  }
 }

+ 1 - 1
server/setup.js

@@ -186,7 +186,7 @@ module.exports = () => {
         'telemetry',
         'theming',
         'title'
-      ])
+      ], false)
 
       // Truncate tables (reset from previous failed install)
       await WIKI.models.locales.query().where('code', '!=', 'x').del()

+ 19 - 13
yarn.lock

@@ -2396,11 +2396,6 @@
   dependencies:
     "@types/express" "*"
 
-"@types/prettier@^1.19.0":
-  version "1.19.1"
-  resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f"
-  integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==
-
 "@types/pg-types@*":
   version "1.11.5"
   resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.5.tgz#1eebbe62b6772fcc75c18957a90f933d155e005b"
@@ -2414,6 +2409,11 @@
     "@types/node" "*"
     "@types/pg-types" "*"
 
+"@types/prettier@^1.19.0":
+  version "1.19.1"
+  resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f"
+  integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==
+
 "@types/q@^1.5.1":
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
@@ -11744,6 +11744,16 @@ pg-int8@1.0.1:
   resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
   integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
 
+pg-packet-stream@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914"
+  integrity sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg==
+
+pg-pool@^2.0.10:
+  version "2.0.10"
+  resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a"
+  integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg==
+
 pg-pool@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.1.0.tgz#65f24bbda56cf7368f03ecdfd65e1da571041901"
@@ -11754,10 +11764,6 @@ pg-protocol@^1.2.1:
   resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.2.1.tgz#60adffeef131418c58f0b20df01ae8f507a95370"
   integrity sha512-IqZ+VUOqg3yydxSt5NgNKLVK9JgPBuzq4ZbA9GmrmIkQjQAszPT9DLqTtID0mKsLEZB68PU0gjLla561WZ2QkQ==
 
-pg-query-stream@3.0.6:
-  version "3.0.6"
-  resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.0.6.tgz#12f405c2c8c9723d8d9f1616cf3d58ecd1d87c83"
-  integrity sha512-/caOI36GVCz1pY35SkftzGowwym4p39e3Ku+sx8MZKNNf+G9WgE0h+Ui9FHTVV9HWf6WWu1GYt5aYfw5ZMeJsQ==
 pg-pubsub@0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/pg-pubsub/-/pg-pubsub-0.5.0.tgz#5469737af32ac6d13fc3153dc3944f55da3d8840"
@@ -11769,10 +11775,10 @@ pg-pubsub@0.5.0:
     promised-retry "^0.3.0"
     verror "^1.10.0"
 
-pg-query-stream@3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.0.0.tgz#b2b4c3d5eb105df6cf75d3f95c4e157dd527a2ab"
-  integrity sha512-yxeFKwVCW0vmFYSkygV7hd4KVlGCMHGljSUQvYcIZUtfUaAITIl8QOq9oXfCMmk0L6JOuHesZKpurxyuOU8gKg==
+pg-query-stream@3.0.6:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.0.6.tgz#12f405c2c8c9723d8d9f1616cf3d58ecd1d87c83"
+  integrity sha512-/caOI36GVCz1pY35SkftzGowwym4p39e3Ku+sx8MZKNNf+G9WgE0h+Ui9FHTVV9HWf6WWu1GYt5aYfw5ZMeJsQ==
   dependencies:
     pg-cursor "^2.1.9"