From 0a81b8433ba02ae563f5024c60b7c43667dea3a9 Mon Sep 17 00:00:00 2001 From: Andrew DeVries Date: Sat, 24 Feb 2024 20:53:13 -0500 Subject: [PATCH] bug fixes --- package.json | 2 +- plugins/baseClientSide.js | 34 ++-- plugins/baseServerSide.mjs | 10 +- plugins/uisptools/serverSide.mjs | 65 +++++++ .../widgets/coveragearea/coveragearea.htm | 97 ++++++++++ .../widgets/coveragearea/coveragearea.js | 172 ++++++++++++++++++ .../uisptools/widgets/detectdfs/detectdfs.js | 1 + plugins/wilcowireless/serverSide.mjs | 43 ++++- .../widgets/freqmapper/freqmapper.js | 2 +- server.js | 9 +- uispToolsApiRequestHandler.js | 93 +++++++--- 11 files changed, 473 insertions(+), 55 deletions(-) create mode 100644 plugins/uisptools/widgets/coveragearea/coveragearea.htm create mode 100644 plugins/uisptools/widgets/coveragearea/coveragearea.js diff --git a/package.json b/package.json index c102ff3..9b3ec9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uisp-tools", - "version": "0.0.25", + "version": "0.0.26", "description": "Tools for UISP", "main": "server.js", "license": "GPL-3.0-or-later", diff --git a/plugins/baseClientSide.js b/plugins/baseClientSide.js index 1f5b17c..2150a7b 100644 --- a/plugins/baseClientSide.js +++ b/plugins/baseClientSide.js @@ -17,8 +17,17 @@ init(){ //this is where if we needed to excute some one time code would go only called first time widgetFactory is created $.logToConsole("INFO: widgetfactory " + this.namespace + " " + this.widgetFactoryJSPath + " init"); + let self = this; return new Promise((resolve, reject) => { - resolve(); + $.uisptools.ajax("scriptsettings.json").then( + function(scriptSettings){ + self.scriptSettings = scriptSettings; + resolve(); + }, + function(err){ + reject(err); + } + ); }); } @@ -37,9 +46,7 @@ return widgetFactoryFolder; } - getBaseUrlPath(){ - return '/' + this.scriptSettings.urlPrefix; - } + }, /** The widget class for the UISPTools framework @@ -64,26 +71,21 @@ //this is called to start the widget in motion $.logToConsole("INFO: widget " + this.widgetFactory.namespace + " " + this.widgetname + " init"); return new Promise((resolve, reject) => { - $.uisptools.ajax("scriptsettings.json").then( - function(scriptSettings){ - this.scriptSettings = scriptSettings; - resolve(); - }, - function(err){ - reject(err); - } - ); - + resolve(); }); } getPluginUserData(options){ if(options && options.pluginId) { - return $.uisptools.ajax("/" + this.uispToolsApiRequestHandler.options.urlPrefix + "/api/pluginUserData/" + options.pluginId); + return $.uisptools.ajax("/" + this.widgetFactory.scriptSettings.urlPrefix + "api/pluginUserData/" + options.pluginId); }else{ - return $.uisptools.ajax("/" + this.uispToolsApiRequestHandler.options.urlPrefix + "/api/pluginUserData/" + this.widgetFactory.namespace + "." + this.widgetname); + return $.uisptools.ajax("/" + this.widgetFactory.scriptSettings.urlPrefix + "api/pluginUserData/" + this.widgetFactory.namespace + "." + this.widgetname); } } + + getBaseUrlPath(){ + return this.widgetFactory.scriptSettings.urlPrefix; + } } diff --git a/plugins/baseServerSide.mjs b/plugins/baseServerSide.mjs index d410331..9a9eb20 100644 --- a/plugins/baseServerSide.mjs +++ b/plugins/baseServerSide.mjs @@ -84,6 +84,9 @@ this.uispToolsApiRequestHandler.checkSuperAdminApiAccess (req, res, next) } + + + getPluginUserData = function(res){ try { let options = { @@ -96,8 +99,11 @@ } } - getPluginData(){ - let options = {pluginName : this.namespace} + getPluginData(options){ + if (options == null) { + options = {pluginName : this.namespace} + } + //let options = {pluginName : this.namespace} this.uispToolsApiRequestHandler.getPluginData(options); } diff --git a/plugins/uisptools/serverSide.mjs b/plugins/uisptools/serverSide.mjs index e1db347..798a8a1 100644 --- a/plugins/uisptools/serverSide.mjs +++ b/plugins/uisptools/serverSide.mjs @@ -28,11 +28,76 @@ var uisptools = { //Any Routes above this line are not Checked for Auth and are Public router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/*', this.checkApiAccess); router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/getMenuItems', this.getMenuItems); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/nms/devices', this.getNMSDevices.bind(this)); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/nms/devices/*', this.getNMSDevices.bind(this)); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/nms/sites', this.getNMSSites.bind(this)); + //router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/nms/devices/airmaxes/:deviceid/config/wireless', this.getNMSDevices.bind(this)); + ///airos/" + deviceId + "/configuration + ///airmaxes/" + deviceId + "/config/wireless } catch (ex) { this.debug("error", ex.msg, ex.stack); } } + + getNMSSites(req, res){ + + let url = 'sites?type=site'; + + + var options = { + url: url, + method: 'GET', + accessToken : res.locals.accessToken + } + this.uispToolsApiRequestHandler.nmsApiQuery(options).then( + function(data){ + //clean the data so only Site Lat Lon and Names are returned + res.json(data); + }, + function(err){ + res.status(500).json({ "msg": "An Error Occured!", "error": err }); + } + ) + } + + getNMSDevices(req, res){ + + let url = req.orginalUrl.substring(('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'uisptools/api/nms/').length); + + + if(req.query){ + let queryString = ""; + for (const [key, value] of Object.entries(req.query)) { + if(key === "_"){ + + }else{ + if(queryString ===""){ + queryString = queryString + "?"; + }else{ + queryString = queryString + "&"; + } + queryString = queryString + key + "=" + encodeURIComponent(value); + } + } + url = url + queryString; + } + var options = { + url: url, + method: 'GET', + accessToken : res.locals.accessToken + } + this.uispToolsApiRequestHandler.nmsApiQuery(options).then( + function(data){ + res.json(data); + }, + function(err){ + res.status(500).json({ "msg": "An Error Occured!", "error": err }); + } + ) + } + + getMenuItems(siteId){ return $.uisptools.ajax("/" + this.uispToolsApiRequestHandler.options.urlPrefix + "uisptools/api/nms/sites/" + siteId + "/clients"); } diff --git a/plugins/uisptools/widgets/coveragearea/coveragearea.htm b/plugins/uisptools/widgets/coveragearea/coveragearea.htm new file mode 100644 index 0000000..96b06e1 --- /dev/null +++ b/plugins/uisptools/widgets/coveragearea/coveragearea.htm @@ -0,0 +1,97 @@ +
+
+
+
+ + + + + + + + + + + + +
NameFrequency
+
+
+
+
+
+ +
+ + + +
\ No newline at end of file diff --git a/plugins/uisptools/widgets/coveragearea/coveragearea.js b/plugins/uisptools/widgets/coveragearea/coveragearea.js new file mode 100644 index 0000000..97c99a1 --- /dev/null +++ b/plugins/uisptools/widgets/coveragearea/coveragearea.js @@ -0,0 +1,172 @@ + "use strict" + import baseClientSide from "../../../baseClientSide.js"; + /* + * uisptools 1.0 + * Copyright (c) 2023 Digital Example + * Date: 2024-2-21 + */ + + /** + @name uisptools.widget.coveragearea + @class This is the coveragearea widget class for the UISPTools widget framework + @description We make a call to nms.sites then for each site draw a coverage circle on a google map + */ + + class freqmapper extends baseClientSide.widget + { + + self = null; + + loadTemplate(){ + return new Promise((resolve, reject) => { + try{ + let $element = $(this.element); + let url = this.widgetFactory.getWidgetFactoryFolder() + "/widgets/coveragearea/coveragearea.htm"; + $.uisptools.ajax({ + method: 'GET', + url: url, + dataType: 'html' + }).then( + function (widgetHtml) { + $element.html(widgetHtml); + resolve(); + }, + function (err) { + $.logToConsole("Error: uisptools.loadWidget.onLoad.getWidgetHtml failed " + err); + reject(err); + } + ); + }catch(ex){ + $.logToConsole("ERROR uisptools.loadWidget: " + ex.toString()); + reject(ex); + } + }); + } + + + + + bindTowerSites(towerSites){ + let map = this.map; + let $element = $(self.element); + let latlngbounds = new google.maps.LatLngBounds(); + for(var i = 0; i < devices.length; i++){ + + + let device = devices[i]; + + + if(device.identification.type !== "airCube"){ + + if(device.overview && device.overview.status === "active"){ + + const deviceMarker = new google.maps.Marker({ + position: { lat: device.location.latitude, lng: device.location.longitude }, + map, + icon: gpsLightImage, + title: device.identification.displayName + " " + device.overview.frequency + }); + latlngbounds.extend(new google.maps.LatLng(device.location.latitude, device.location.longitude)) + let $deviceItem = $deviceItemTemplate.clone(); + let $deviceMapItem = $deviceMapItemTemplate.clone(); + this.updateDeviceItem({device:device, $deviceItem: $deviceItem}); + this.updateDeviceItem({device:device, $deviceItem: $deviceMapItem}); + $deviceList.append($deviceItem); + $deviceMapList.append($deviceMapItem); + const infowindow = new google.maps.InfoWindow({ + content: $deviceMapItem[0], + ariaLabel: device.identification.displayName, + }); + deviceMarker.addListener("click", () => { + infowindow.open({ + anchor: deviceMarker, + map, + }); + }); + } + + } + } + this.map.fitBounds(latlngbounds); + + // $deviceList.find(".btnDeviceOpen").on("click",this.onDeviceOpenClick) + // $deviceList.find(".btnDeviceRestart").on("click", this.onDeviceRestartClick) + // $deviceList.find(".btnDeviceRefresh").on("click", this.onDeviceRefreshClick) + + } + + bind(){ + return new Promise((resolve, reject) => { + const parsedUrl = new URL(window.location.href); + + const mapOptions = { + center: { + lat: 42.4117322, + lng: -85.4871588 + }, + zoom: 4, + gestureHandling:"auto", + fullscreenControl: false, + disableDoubleClickZoom: true, + disableDefaultUI: false, + keyboardShortcuts: false, + scrollwheel: true, + streetViewControl: false + }; + this.waypoints = []; + this.map = new google.maps.Map(document.getElementById("map"), mapOptions); + Promise.all([self.getPluginData({pluginName: 'uisptools.coveragearea' })]).then( + (results) => { + if(results){ + let towerSites = results[0]; + self.bindTowerSites(towerSites); + } + resolve(); + } + ) + }); + } + + init(){ + self = this; + return new Promise((resolve, reject) => { + try{ + const googleLoader = new google.maps.plugins.loader.Loader({ + apiKey: $.uisptools.common.settings.system.googleApiKey, + version: "weekly", + libraries: [] + }); + googleLoader.loadCallback((ex) => { + if (ex) { + $.logToConsole("ERROR google loader: " + ex.toString()); + } else { + // new google.maps.Map(document.getElementById("map"), mapOptions); + } + }); + Promise.all([googleLoader.load(),this.loadTemplate()]).then( + function(){ + self.bind().then( + function(){ + resolve(); + }, + function(err){ + reject(err) + } + + ); + }, + function(err){ + reject(err) + } + ) + }catch(ex){ + $.logToConsole("ERROR loadWidget: " + ex.toString()); + reject(ex); + } + }) + } + + } + + export {freqmapper} + export default freqmapper diff --git a/plugins/uisptools/widgets/detectdfs/detectdfs.js b/plugins/uisptools/widgets/detectdfs/detectdfs.js index 9d2507e..85243ac 100644 --- a/plugins/uisptools/widgets/detectdfs/detectdfs.js +++ b/plugins/uisptools/widgets/detectdfs/detectdfs.js @@ -48,6 +48,7 @@ fetchApDevices(){ return $.uisptools.ajax("/uisptools/api/nms/devices?role=ap"); + } fetchDevice(options){ return $.uisptools.ajax("/uisptools/api/nms/devices/" + options.deviceId); diff --git a/plugins/wilcowireless/serverSide.mjs b/plugins/wilcowireless/serverSide.mjs index 16e84ab..8b4f2d9 100644 --- a/plugins/wilcowireless/serverSide.mjs +++ b/plugins/wilcowireless/serverSide.mjs @@ -26,15 +26,50 @@ var wilcowireless = { try { super.bindRoutes(router); //Any Routes above this line are not Checked for Auth and are Public - router.get('/uisptools/wilcowireless/api/*', this.checkApiAccess); - router.get('/uisptools/wilcowireless/api/towerclients', this.getTowerSitesClients); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'wilcowireless/api/*', this.checkApiAccess.bind(this)); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'wilcowireless/api/freqmapper/devices', this.getFreqMapperNMSDevices.bind(this)); + router.get('/' + this.uispToolsApiRequestHandler.options.urlPrefix + 'wilcowireless/api/freqmapper/devices:deviceid', this.getFreqMapperNMSDevices.bind(this)); + } catch (ex) { this.debug("error", ex.msg, ex.stack); } } - fetchSiteClients(siteId){ - return $.uisptools.ajax("/uisptools/api/nms/sites/" + siteId + "/clients"); + + getFreqMapperNMSDevices(req, res){ + let url = "devices"; + if(req.params.deviceid){ + url = url + "/" + req.params.deviceid; + } + if(req.query){ + let queryString = ""; + for (const [key, value] of Object.entries(req.query)) { + if(key === "_"){ + + }else{ + if(queryString ===""){ + queryString = queryString + "?"; + }else{ + queryString = queryString + "&"; + } + queryString = queryString + key + "=" + encodeURIComponent(value); + } + } + url = url + queryString; + } + var options = { + url: url, + method: 'GET', + accessToken : res.locals.accessToken + } + this.uispToolsApiRequestHandler.nmsApiQuery(options).then( + function(data){ + res.json(data); + }, + function(err){ + res.status(500).json({ "msg": "An Error Occured!", "error": err }); + } + ) } fetchSiteDetails(siteId){ diff --git a/plugins/wilcowireless/widgets/freqmapper/freqmapper.js b/plugins/wilcowireless/widgets/freqmapper/freqmapper.js index 0b28d55..3fb6169 100644 --- a/plugins/wilcowireless/widgets/freqmapper/freqmapper.js +++ b/plugins/wilcowireless/widgets/freqmapper/freqmapper.js @@ -44,7 +44,7 @@ } fetchApDevices(){ - return $.uisptools.ajax("/uisptools/api/nms/devices?role=ap"); + return $.uisptools.ajax("/" + this.getBaseUrlPath() + "wilcowireless/api/freqmapper/devices?role=ap"); } wifi5GhzChannelToFrequency(channel){ diff --git a/server.js b/server.js index 22e3550..60a127f 100644 --- a/server.js +++ b/server.js @@ -399,7 +399,7 @@ app.use(fileUpload({ })); var routes = express.Router(); - +var pluginRoutes = express.Router(); var handlePluginPublicFileRequest = function (req, res) { let filePath = req.path; @@ -513,8 +513,9 @@ var handlePublicFileRequest = function (req, res) { }else if (fs.existsSync(path.join(__dirname, 'public',filePath)) === true) { res.sendFile(filePath, { root: path.join(__dirname, 'public') }); } else { + let fileExt = path.extname(filePath); - if(fileExt === "" || fileExt === ".htm" || fileExt === ".html"){ + if( filePath.includes("/api/") == false && (fileExt === "" || fileExt === ".htm" || fileExt === ".html")){ filePath = "/index.htm"; res.sendFile(filePath, { root: path.join(__dirname, 'public') }); }else{ @@ -574,7 +575,7 @@ for (let index = 0; index < objOptions.plugins.length; index++) { plugin.init().then( function(){ try{ - plugin.bindRoutes(routes); + plugin.bindRoutes(pluginRoutes); }catch(ex){ logUtilHelper.log(appLogName, "app", "error", "plugin serverSide create", pluginName, serverSideJSPath, ex ) } @@ -611,6 +612,8 @@ routes.get('/' + urlPrefix + '*', function (req, res) { }); +app.use('/', pluginRoutes); + app.use('/', routes); diff --git a/uispToolsApiRequestHandler.js b/uispToolsApiRequestHandler.js index 5f80c59..5fe8e4b 100644 --- a/uispToolsApiRequestHandler.js +++ b/uispToolsApiRequestHandler.js @@ -15,7 +15,8 @@ var UispToolsApiRequestHandler = function (options) { var defaultOptions = { logUtilHelper:null, uispToolsApiHandler: null, - urlPrefix: "" + urlPrefix: "", + allowDirectUispQuerys: true }; self.options = extend({}, defaultOptions, options); @@ -81,13 +82,14 @@ var UispToolsApiRequestHandler = function (options) { // routes.delete('/' + self.options.urlPrefix + '/api/pluginUserData/:pluginName', deletePluginUserData); //Only Admin plugins can call these directly plugins should only expose what should exposed as you can pull credit card data etc. - routes.get('/' + self.options.urlPrefix + 'api/crm/*', getCRM); - routes.get('/' + self.options.urlPrefix + 'api/nms/*', getNMS); - routes.post('/' + self.options.urlPrefix + 'api/crm/*', getCRM); - routes.post('/' + self.options.urlPrefix + 'api/nms/*', getNMS); - routes.delete('/' + self.options.urlPrefix + 'api/crm/*', getCRM); - routes.delete('/' + self.options.urlPrefix + 'api/nms/*', getNMS); - + if(self.options.allowDirectUispQuerys){ + routes.get('/' + self.options.urlPrefix + 'api/crm/*', getCRMData); + routes.get('/' + self.options.urlPrefix + 'api/nms/*', getNMSData); + routes.post('/' + self.options.urlPrefix + 'api/crm/*', getCRMData); + routes.post('/' + self.options.urlPrefix + 'api/nms/*', getNMSData); + routes.delete('/' + self.options.urlPrefix + 'api/crm/*', getCRMData); + routes.delete('/' + self.options.urlPrefix + 'api/nms/*', getNMSData); + } } catch (ex) { debug("error", ex.msg, ex.stack); } @@ -221,7 +223,29 @@ var UispToolsApiRequestHandler = function (options) { return url; } - var getCRM = function (req, res) { + var crmApiQuery = function (options) { + return new Promise((resolve, reject) => { + //Need to add Code to Validate that the AccessToken is valid, requester is a Valid NMS Login and then get the NMS Auth Token from nmsLoginData + let crmOptions = { + url:options.url, + rejectUnauthorized: options.rejectUnauthorized || true, + method : options.method, + sendAppKey: options.sendAppKey || true, + } + self.options.uispToolsApiHandler.handleCrmRequest(crmOptions).then( + function(data){ + resolve(data); + }, + function(err){ + debug("error", "crmApiQuery", { "msg": err.message, "stack": err.stack }); + reject(err); + } + ) + }); + }; + + + var getCRMData = function (req, res) { try { //Validate that the AccessToken is valid and get the CRM Auth Token from let accessToken = res.locals.accessToken; @@ -234,7 +258,7 @@ var UispToolsApiRequestHandler = function (options) { method : req.method //ucrmAppKey:accessToken.loginData.nmsLoginData.x-auth-token } - self.options.uispToolsApiHandler.handleUcrmRequest(ucrmOptions).then( + crmApiQuery(ucrmOptions).then( function(data){ res.json(data); }, @@ -281,40 +305,51 @@ var UispToolsApiRequestHandler = function (options) { } - var getNMS = function (req, res) { - try { + var nmsApiQuery = function (options) { + return new Promise((resolve, reject) => { //Need to add Code to Validate that the AccessToken is valid, requester is a Valid NMS Login and then get the NMS Auth Token from nmsLoginData - - let accessToken = res.locals.accessToken; - - let nmsUrl = getNMSUrl(req); let unmsOptions = { - url:nmsUrl, //figure this out from request - rejectUnauthorized: true, - nmsAuthToken:accessToken.loginData.nmsLoginData["x-auth-token"], - method : req.method + url:options.url, //figure this out from request + rejectUnauthorized: options.rejectUnauthorized || true, + nmsAuthToken:options.accessToken.loginData.nmsLoginData["x-auth-token"], + method : options.method } self.options.uispToolsApiHandler.handleUnmsRequest(unmsOptions).then( + function(data){ + resolve(data); + }, + function(err){ + debug("error", "nmsApiQuery", { "msg": err.message, "stack": err.stack }); + reject(err); + } + ) + }); + }; + + var getNMSData = function (req, res) { + try { + let unmsOptions = { + url:getNMSUrl(req), + method : req.method, + accessToken:res.locals.accessToken, + rejectUnauthorized: true + } + nmsApiQuery(unmsOptions).then( function(data){ res.json(data); }, function(err){ - debug("error", "getNMS", { "msg": err.message, "stack": err.stack }); + debug("error", "getCRM", { "msg": err.message, "stack": err.stack }); res.status(500).json({ "msg": "An Error Occured!", "error": err }); } - - ) - - + ); } catch (ex) { - debug("error", "getCRM", { "msg": ex.message, "stack": ex.stack }); + debug("error", "getNMSRaw", { "msg": ex.message, "stack": ex.stack }); res.status(500).json({ "msg": "An Error Occured!", "error": ex }); } }; - - var getUserInfo = function (req, res) { try { @@ -637,6 +672,8 @@ var getMenuItems = function (req, res, next) { self.getErrorObject = getErrorObject; self.checkApiAccess = checkApiAccess; self.checkSuperAdminApiAccess = checkSuperAdminApiAccess; + self.nmsApiQuery = nmsApiQuery; + self.crmApiQuery = crmApiQuery; }; module.exports = UispToolsApiRequestHandler; \ No newline at end of file