From 26d8045f4d40d576e9f696bbcddb33010567ef46 Mon Sep 17 00:00:00 2001 From: Nikolay Vasilchuk Date: Tue, 8 Aug 2023 13:55:31 +0300 Subject: [PATCH 1/2] Fix REGISTER_API call from custom modules --- API/api.js | 74 ++++++++++++++++++++++++-------------------------- node_helper.js | 16 +++++------ 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/API/api.js b/API/api.js index dcc9b16..dc2955f 100644 --- a/API/api.js +++ b/API/api.js @@ -15,7 +15,7 @@ const bodyParser = require("body-parser"); const express = require("express"); module.exports = { - /* getApiKey + /* getApiKey * Middleware method for ExpressJS to check if an API key is provided. * Only checks for an API key if one is defined in the module's config section. */ @@ -36,7 +36,7 @@ module.exports = { this.secureEndpoints = true; } } - + }, @@ -78,8 +78,6 @@ module.exports = { let modActions = getActions(Module.notificationHandler[mod.module]); if (modActions.length > 0) { - let pathGuess = mod.module.replace(/MMM-/g, '').replace(/-/g, '').toLowerCase(); - // Generate formatted actions object let actionsGuess = {}; @@ -87,10 +85,10 @@ module.exports = { actionsGuess[a.replace(/[-_]/g, '').toLowerCase()] = { notification: a, guessed: true }; }); - if (pathGuess in this.externalApiRoutes) { - this.externalApiRoutes[pathGuess].actions = Object.assign({}, actionsGuess, this.externalApiRoutes[pathGuess].actions); + if (mod.module in this.externalApiRoutes) { + this.externalApiRoutes[mod.module].actions = Object.assign({}, actionsGuess, this.externalApiRoutes[mod.module].actions); } else { - this.externalApiRoutes[pathGuess] = { + this.externalApiRoutes[mod.module] = { module: mod.module, path: mod.module.replace(/MMM-/g, '').replace(/-/g, '').toLowerCase(), actions: actionsGuess @@ -112,11 +110,11 @@ module.exports = { this.expressApp.use(bodyParser.urlencoded({ extended: true })); this.expressApp.use(bodyParser.json()); - + this.expressApp.use('/api/docs', express.static(path.join(__dirname, '../docs'))); // Docs without apikey this.expressRouter = express.Router(); - + // Route for testing the api at http://mirror:8080/api/test this.expressRouter.route(['/test','/']) // Test without apiKey .get((req, res) => { @@ -183,7 +181,7 @@ module.exports = { console.log(req.path); self.executeQuery(this.checkDelay({ action: r }, req), res); }); - + this.expressRouter.route('/classes/:value') .get((req, res) => { var classes = self.getConfig().modules.find(m => m.module === "MMM-Remote-Control").config || {}; @@ -194,7 +192,7 @@ module.exports = { res.status(400).json({ success: false, message: `Invalid value ${val} provided in request. Use /api/classes to see actual values` }); } }); - + this.expressRouter.route('/command/:value') .get((req, res) => { if(!this.apiKey && this.secureEndpoints) return res.status(403).json({ success: false, message: "Forbidden: API Key Not Provided in Config! Use secureEndpoints to bypass this message" }); @@ -238,7 +236,7 @@ module.exports = { res.status(400).json({ success: false, message: "Invalid URL provided in request body" }); } }); - + //edit config, payload is completely new config object with your changes(edits). this.expressRouter.route('/config/edit') .get((req, res) => { @@ -326,12 +324,12 @@ module.exports = { } return query; }, - + mergeData: function() { var extApiRoutes = this.externalApiRoutes; var modules = this.configData.moduleData var query = {success: true, data: []}; - + modules.forEach((mod) => { if (extApiRoutes[mod.name] === undefined) { query.data.push(mod); @@ -339,19 +337,19 @@ module.exports = { query.data.push(Object.assign({},mod, {actions: extApiRoutes[mod.name].actions})); } }) - + return query; }, answerModuleApi: function(req, res) { if (!this.checkInititialized(res)) { return; } var dataMerged = this.mergeData().data - + if (!req.params.moduleName) { res.json({ success: true, data: dataMerged }); return; } - + let modData = []; if(req.params.moduleName !== 'all') { modData = dataMerged.filter(m => { @@ -365,26 +363,26 @@ module.exports = { } else { modData = dataMerged; } - + if (!modData.length) { res.status(400).json({ success: false, message: "Module Name or Identifier Not Found!" }); return; - } - + } + if (!req.params.action) { res.json({ success: true, data: modData }); return; } - + var action = req.params.action.toUpperCase(); - + if (["SHOW", "HIDE", "FORCE", "TOGGLE", "DEFAULTS"].indexOf(action) !== -1) { // /api/modules part of the code - + if (action === "DEFAULTS") { this.answerGet({ data: "defaultConfig", module: mod.name }, res); return; } - + if (req.params.moduleName === "all") { let query = { module: "all" }; if (action === "FORCE") { @@ -396,7 +394,7 @@ module.exports = { this.executeQuery(this.checkDelay(query, req), res); return; } - + modData.forEach(mod => { let query = { module: mod.identifier }; if (action === "FORCE") { @@ -410,9 +408,9 @@ module.exports = { this.sendSocketNotification("UPDATE"); return; } - + action = modData[0].actions[req.params.action] - + if (action) { if ("method" in action && action.method !== req.method) { res.status(400).json({ success: false, info: `Method ${req.method} is not allowed for ${moduleName}/${req.params.action}.` }); @@ -420,9 +418,9 @@ module.exports = { } this.answerNotifyApi(req, res, action); } - + }, - + answerNotifyApi: function(req, res, action) { // Build the payload to send with our notification. let n = ""; @@ -500,26 +498,26 @@ module.exports = { icon: "window-restore", items: [] }; - Object.keys(this.externalApiRoutes).forEach(r => { + Object.values(this.externalApiRoutes).forEach(r => { let sub = { - id: "mc-" + r, + id: "mc-" + r.path, type: "menu", icon: "bars", - text: this.formatName(this.externalApiRoutes[r].module), + text: this.formatName(r.module), items: [] }; - Object.keys(this.externalApiRoutes[r].actions).forEach(a => { + Object.keys(r.actions).forEach(a => { let item = { - id: `mc-${r}-${a}`, + id: `mc-${r.path}-${a}`, menu: "item", icon: "dot-circle-o", action: "NOTIFICATION", - content: this.externalApiRoutes[r].actions[a] + content: r.actions[a] }; - if ("prettyName" in this.externalApiRoutes[r].actions[a]) { - item.text = this.translate(this.externalApiRoutes[r].actions[a].prettyName); + if ("prettyName" in r.actions[a]) { + item.text = this.translate(r.actions[a].prettyName); } else { - item.text = this.translate(this.externalApiRoutes[r].actions[a].notification).toLowerCase().replace(/(^|_)(\w)/g, function($0, $1, $2) { + item.text = this.translate(r.actions[a].notification).toLowerCase().replace(/(^|_)(\w)/g, function($0, $1, $2) { return ($1 && ' ') + $2.toUpperCase(); }); } diff --git a/node_helper.js b/node_helper.js index 5be03fe..bbcd2c8 100644 --- a/node_helper.js +++ b/node_helper.js @@ -83,9 +83,9 @@ module.exports = NodeHelper.create(Object.assign({ loadTimers: function() { var delay = 24*3600; - + var self = this; - + clearTimeout(this.delayedQueryTimers['update']) this.delayedQueryTimers['update'] = setTimeout(function () { self.updateModuleList(); @@ -699,7 +699,7 @@ module.exports = NodeHelper.create(Object.assign({ } if (query.action === "COMMAND") { if (this.thisConfig.customCommand && this.thisConfig.customCommand[query.command]) { - exec(this.thisConfig.customCommand[query.command], opts, (error, stdout, stderr) => { + exec(this.thisConfig.customCommand[query.command], opts, (error, stdout, stderr) => { self.checkForExecError(error, stdout, stderr, res, { stdout: stdout }); }); } else { @@ -842,7 +842,7 @@ module.exports = NodeHelper.create(Object.assign({ return; } if (query.action === "DELAYED") { - /* Expects a nested query object + /* Expects a nested query object * { * action: "DELAYED", * did: "SOME_UNIQUE_ID", @@ -942,7 +942,7 @@ module.exports = NodeHelper.create(Object.assign({ } }); }); - + }, checkForExecError: function(error, stdout, stderr, res, data) { @@ -1136,7 +1136,7 @@ module.exports = NodeHelper.create(Object.assign({ } var backupPath = path.resolve("config/config.js.backup" + iteration); var req = require(backupPath) - + this.answerPost({ data: "config" }, { body: req }, { isSocket: true }); } if (notification === "NEW_CONFIG") { @@ -1159,10 +1159,10 @@ module.exports = NodeHelper.create(Object.assign({ if (notification === "REGISTER_API") { if ("module" in payload) { if ("actions" in payload && payload.actions !== {}) { - this.externalApiRoutes[payload.path] = payload; + this.externalApiRoutes[payload.module] = payload; } else { // Blank actions means the module has requested to be removed from API - delete this.externalApiRoutes[payload.path]; + delete this.externalApiRoutes[payload.module]; } this.updateModuleApiMenu(); } From 662b1a1b8464e20c4dafa588362355a40f7494de Mon Sep 17 00:00:00 2001 From: Nikolay Vasilchuk Date: Tue, 8 Aug 2023 14:23:34 +0300 Subject: [PATCH 2/2] path support --- API/api.js | 14 +++++--------- MMM-Remote-Control.js | 5 +++-- node_helper.js | 1 + 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/API/api.js b/API/api.js index dc2955f..ba0e853 100644 --- a/API/api.js +++ b/API/api.js @@ -334,7 +334,7 @@ module.exports = { if (extApiRoutes[mod.name] === undefined) { query.data.push(mod); } else { - query.data.push(Object.assign({},mod, {actions: extApiRoutes[mod.name].actions})); + query.data.push(Object.assign({}, mod, {actions: extApiRoutes[mod.name].actions, urlpath: extApiRoutes[mod.name].path})); } }) @@ -350,16 +350,12 @@ module.exports = { return; } - let modData = []; - if(req.params.moduleName !== 'all') { + let modData; + if (req.params.moduleName !== 'all') { modData = dataMerged.filter(m => { - return (req.params.moduleName.includes(m.identifier)); + const name = req.params.moduleName; + return name.includes(m.identifier) || name.includes(m.name) || name.includes(m.urlpath); }); - if (!modData) { - modData = dataMerged.filter(m => { - return (req.params.moduleName.includes(m.name)); - }); - } } else { modData = dataMerged; } diff --git a/MMM-Remote-Control.js b/MMM-Remote-Control.js index 8424816..145e7ca 100644 --- a/MMM-Remote-Control.js +++ b/MMM-Remote-Control.js @@ -55,7 +55,7 @@ Module.register("MMM-Remote-Control", { socketNotificationReceived: function(notification, payload) { if (notification === "UPDATE") { this.sendCurrentData(); - } + } if (notification === "IP_ADDRESSES") { this.addresses = payload; if (this.data.position) { @@ -68,7 +68,7 @@ Module.register("MMM-Remote-Control", { this.updateDom(); } } - + if (notification === "USER_PRESENCE") { this.sendNotification(notification, payload); } @@ -272,6 +272,7 @@ Module.register("MMM-Remote-Control", { let modData = Object.assign({}, module.data); modData.hidden = module.hidden; modData.lockStrings = module.lockStrings; + modData.urlpath = module.name.replace(/MMM-/g, '').replace(/-/g, '').toLowerCase(); modData.config = module.config; currentModuleData.push(modData); }); diff --git a/node_helper.js b/node_helper.js index bbcd2c8..b9ae9c4 100644 --- a/node_helper.js +++ b/node_helper.js @@ -1002,6 +1002,7 @@ module.exports = NodeHelper.create(Object.assign({ simpleModuleData[k].identifier = moduleData[k].identifier; simpleModuleData[k].hidden = moduleData[k].hidden; simpleModuleData[k].lockStrings = moduleData[k].lockStrings; + simpleModuleData[k].urlpath = moduleData[k].urlpath; } var text = JSON.stringify({