Ver Fonte

dev: hook extensions into service event bus

KernelDeimos há 7 meses atrás
pai
commit
84989367a7

+ 4 - 2
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,
+        });
     }
 }
 

+ 15 - 1
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 }) {

+ 1 - 1
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.');
     }
 

+ 1 - 1
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');
     }
 

+ 1 - 1
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,
             });

+ 7 - 1
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;
     }

+ 17 - 1
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);
+        };
     }
 });