diff --git a/src/backend/src/Extension.js b/src/backend/src/Extension.js index b3dcbb168..c4222bb1c 100644 --- a/src/backend/src/Extension.js +++ b/src/backend/src/Extension.js @@ -1,7 +1,7 @@ const { AdvancedBase } = require("@heyputer/putility"); const EmitterFeature = require("@heyputer/putility/src/features/EmitterFeature"); const { Context } = require("./util/context"); -const { ExtensionService, ExtensionServiceState } = require("./ExtensionService"); +const { ExtensionServiceState } = require("./ExtensionService"); class Extension extends AdvancedBase { static FEATURES = [ @@ -56,7 +56,9 @@ class Extension extends AdvancedBase { return; } - this.service = new ExtensionServiceState(); + this.service = new ExtensionServiceState({ + extension: this, + }); } } diff --git a/src/backend/src/ExtensionService.js b/src/backend/src/ExtensionService.js index 2dd6c651f..346d5abd4 100644 --- a/src/backend/src/ExtensionService.js +++ b/src/backend/src/ExtensionService.js @@ -7,6 +7,8 @@ class ExtensionServiceState extends AdvancedBase { constructor (...a) { super(...a); + this.extension = a[0].extension; + this.endpoints_ = []; } register_route_handler_ (path, handler, options = {}) { @@ -44,11 +46,23 @@ class ExtensionServiceState extends AdvancedBase { */ class ExtensionService extends BaseService { _construct () { - this.extension = null; this.endpoints_ = []; } async _init (args) { this.state = args.state; + + // Propagate all events not from extensions to `core.` + const svc_event = this.services.get('event'); + svc_event.on_all((key, data, meta = {}) => { + meta.from_outside_of_extension = true; + this.state.extension.emit(`core.${key}`, data, meta); + }); + + this.state.extension.on_all((key, data, meta) => { + if ( meta.from_outside_of_extension ) return; + + svc_event.emit(key, data, meta); + }); } ['__on_install.routes'] (_, { app }) { diff --git a/src/backend/src/routers/save_account.js b/src/backend/src/routers/save_account.js index 317e68a63..5036ea5c7 100644 --- a/src/backend/src/routers/save_account.js +++ b/src/backend/src/routers/save_account.js @@ -74,7 +74,7 @@ router.post('/save_account', auth, express.json(), async (req, res, next)=>{ const svc_cleanEmail = req.services.get('clean-email') const clean_email = svc_cleanEmail.clean(req.body.email); - if ( ! svc_cleanEmail.validate(clean_email) ) { + if ( ! await svc_cleanEmail.validate(clean_email) ) { return res.status(400).send('This email domain is not allowed.'); } diff --git a/src/backend/src/routers/signup.js b/src/backend/src/routers/signup.js index 18f11a86c..f061d66d5 100644 --- a/src/backend/src/routers/signup.js +++ b/src/backend/src/routers/signup.js @@ -147,7 +147,7 @@ module.exports = eggspress(['/signup'], { const svc_cleanEmail = req.services.get('clean-email'); const clean_email = svc_cleanEmail.clean(req.body.email); - if ( ! svc_cleanEmail.validate(clean_email) ) { + if ( ! await svc_cleanEmail.validate(clean_email) ) { return res.status(400).send('This email domain is not allowed'); } diff --git a/src/backend/src/routers/user-protected/change-email.js b/src/backend/src/routers/user-protected/change-email.js index d96a0d726..e75d4db72 100644 --- a/src/backend/src/routers/user-protected/change-email.js +++ b/src/backend/src/routers/user-protected/change-email.js @@ -49,7 +49,7 @@ module.exports = { const svc_cleanEmail = req.services.get('clean-email'); const clean_email = svc_cleanEmail.clean(new_email); - if ( ! svc_cleanEmail.validate(clean_email) ) { + if ( ! await svc_cleanEmail.validate(clean_email) ) { throw APIError.create('email_not_allowed', undefined, { email: clean_email, }); diff --git a/src/backend/src/services/CleanEmailService.js b/src/backend/src/services/CleanEmailService.js index 70b225665..5fe291928 100644 --- a/src/backend/src/services/CleanEmailService.js +++ b/src/backend/src/services/CleanEmailService.js @@ -101,7 +101,7 @@ class CleanEmailService extends BaseService { return eml.local + '@' + eml.domain; } - validate (email) { + async validate (email) { email = this.clean(email); const config = this.global_config; @@ -112,6 +112,12 @@ class CleanEmailService extends BaseService { } } } + + const svc_event = this.services.get('event'); + const event = { allow: true, email }; + await svc_event.emit('email.validate', event); + + if ( ! event.allow ) return false; return true; } diff --git a/src/putility/src/features/EmitterFeature.js b/src/putility/src/features/EmitterFeature.js index 9ed2e3ffa..93aadb826 100644 --- a/src/putility/src/features/EmitterFeature.js +++ b/src/putility/src/features/EmitterFeature.js @@ -15,6 +15,7 @@ module.exports = ({ decorators }) => ({ // install the internal state const state = instance._.emitterFeature = {}; state.listeners_ = {}; + state.global_listeners_ = []; state.callbackDecorators = decorators || []; instance.emit = async (key, data, meta) => { @@ -22,6 +23,17 @@ module.exports = ({ decorators }) => ({ const parts = key.split('.'); const promises = []; + + for ( let i = 0 ; i < state.global_listeners_.length ; i++ ) { + let callback = state.global_listeners_[i]; + for ( const decorator of state.callbackDecorators ) { + callback = decorator(callback); + } + + promises.push(callback(key, data, + { ...meta, key })); + } + for ( let i = 0; i < parts.length; i++ ) { const part = i === parts.length - 1 ? parts.join('.') @@ -42,7 +54,7 @@ module.exports = ({ decorators }) => ({ })); } } - + return await Promise.all(promises); } @@ -63,6 +75,10 @@ module.exports = ({ decorators }) => ({ return det; } + + instance.on_all = (callback) => { + state.global_listeners_.push(callback); + }; } });