feat: HA event handling + emitting

This commit is contained in:
NGPixel 2020-04-19 20:26:26 -04:00 committed by Nicolas Giard
parent 6a00a5dbce
commit bd4263ecb2
14 changed files with 109 additions and 33 deletions

View File

@ -120,27 +120,27 @@
v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``') 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')}} .overline.pa-4.grey--text {{$t('common:header.currentPage')}}
v-list-item.pl-4(@click='pageView', v-if='mode !== `view`') 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-title.body-2 {{$t('common:header.view')}}
v-list-item.pl-4(@click='pageEdit', v-if='mode !== `edit` && isAuthenticated') 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-title.body-2 {{$t('common:header.edit')}}
v-list-item.pl-4(@click='pageHistory', v-if='mode !== `history`') 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-content
v-list-item-title.body-2 {{$t('common:header.history')}} v-list-item-title.body-2 {{$t('common:header.history')}}
v-list-item.pl-4(@click='pageSource', v-if='mode !== `source`') 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-title.body-2 {{$t('common:header.viewSource')}}
v-list-item.pl-4(@click='pageDuplicate', v-if='isAuthenticated') 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-title.body-2 {{$t('common:header.duplicate')}}
v-list-item.pl-4(@click='pageMove', v-if='isAuthenticated') 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-content
v-list-item-title.body-2 {{$t('common:header.move')}} v-list-item-title.body-2 {{$t('common:header.move')}}
v-list-item.pl-4(@click='pageDelete', v-if='isAuthenticated') 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-list-item-title.body-2 {{$t('common:header.delete')}}
v-divider(vertical) v-divider(vertical)

View File

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

View File

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

View File

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

View File

@ -203,12 +203,22 @@ module.exports = {
WIKI.logger.debug(ev) WIKI.logger.debug(ev)
} }
}) })
// -> Outbound events handling
this.listener.addChannel('wiki', payload => { this.listener.addChannel('wiki', payload => {
if (_.has(payload.event) && payload.source !== WIKI.INSTANCE_ID) { 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 ]`) WIKI.logger.info(`High-Availability Listener initialized successfully: [ OK ]`)
}, },
@ -217,7 +227,8 @@ module.exports = {
*/ */
async unsubscribeToNotifications () { async unsubscribeToNotifications () {
if (this.listener) { if (this.listener) {
WIKI.events.offAny(this.notifyViaDB) WIKI.events.outbound.offAny(this.notifyViaDB)
WIKI.events.inbound.removeAllListeners()
this.listener.close() this.listener.close()
} }
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -436,7 +436,8 @@ module.exports = class Page extends Model {
localeCode: opts.destinationLocale, localeCode: opts.destinationLocale,
hash: destinationHash hash: destinationHash
}).findById(page.id) }).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 // -> Rebuild page tree
await WIKI.models.pages.rebuildTree() await WIKI.models.pages.rebuildTree()
@ -512,7 +513,8 @@ module.exports = class Page extends Model {
// -> Delete page // -> Delete page
await WIKI.models.pages.query().delete().where('id', page.id) 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 // -> Rebuild page tree
await WIKI.models.pages.rebuildTree() await WIKI.models.pages.rebuildTree()
@ -609,7 +611,8 @@ module.exports = class Page extends Model {
affectedHashes = qryHashes.map(h => h.hash) affectedHashes = qryHashes.map(h => h.hash)
} }
for (const hash of affectedHashes) { 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, ' ') .replace(/\s\s+/g, ' ')
.split(' ').filter(w => w.length > 1).join(' ').toLowerCase() .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()
})
}
} }

View File

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

View File

@ -2396,11 +2396,6 @@
dependencies: dependencies:
"@types/express" "*" "@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@*": "@types/pg-types@*":
version "1.11.5" version "1.11.5"
resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.5.tgz#1eebbe62b6772fcc75c18957a90f933d155e005b" resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.5.tgz#1eebbe62b6772fcc75c18957a90f933d155e005b"
@ -2414,6 +2409,11 @@
"@types/node" "*" "@types/node" "*"
"@types/pg-types" "*" "@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": "@types/q@^1.5.1":
version "1.5.2" version "1.5.2"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" 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" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== 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: pg-pool@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.1.0.tgz#65f24bbda56cf7368f03ecdfd65e1da571041901" 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" resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.2.1.tgz#60adffeef131418c58f0b20df01ae8f507a95370"
integrity sha512-IqZ+VUOqg3yydxSt5NgNKLVK9JgPBuzq4ZbA9GmrmIkQjQAszPT9DLqTtID0mKsLEZB68PU0gjLla561WZ2QkQ== 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: pg-pubsub@0.5.0:
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/pg-pubsub/-/pg-pubsub-0.5.0.tgz#5469737af32ac6d13fc3153dc3944f55da3d8840" 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" promised-retry "^0.3.0"
verror "^1.10.0" verror "^1.10.0"
pg-query-stream@3.0.0: pg-query-stream@3.0.6:
version "3.0.0" version "3.0.6"
resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.0.0.tgz#b2b4c3d5eb105df6cf75d3f95c4e157dd527a2ab" resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.0.6.tgz#12f405c2c8c9723d8d9f1616cf3d58ecd1d87c83"
integrity sha512-yxeFKwVCW0vmFYSkygV7hd4KVlGCMHGljSUQvYcIZUtfUaAITIl8QOq9oXfCMmk0L6JOuHesZKpurxyuOU8gKg== integrity sha512-/caOI36GVCz1pY35SkftzGowwym4p39e3Ku+sx8MZKNNf+G9WgE0h+Ui9FHTVV9HWf6WWu1GYt5aYfw5ZMeJsQ==
dependencies: dependencies:
pg-cursor "^2.1.9" pg-cursor "^2.1.9"