From 30dbf0cf406608b5ef0d57109b20d356bebe71de Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Fri, 29 Jun 2018 22:40:29 -0700 Subject: [PATCH] Use nested config structure --- client/app.js | 3 + client/components/overview-dot.js | 10 +- client/config.js | 39 +--- client/device-page.js | 4 +- client/devices-page.js | 6 +- client/overview-page.js | 16 +- client/pie-chart-component.js | 4 +- client/store.js | 3 +- common/filter.js | 5 + common/func-cache.js | 27 +++ config.json | 374 ++++++++++++++++++++---------- server/api-functions.js | 16 +- server/config.js | 93 +++----- server/db.js | 2 +- server/server.js | 15 +- 15 files changed, 366 insertions(+), 251 deletions(-) create mode 100644 common/func-cache.js diff --git a/client/app.js b/client/app.js index fef3830..ae72780 100644 --- a/client/app.js +++ b/client/app.js @@ -11,6 +11,7 @@ import * as devicePage from "./device-page"; import * as errorPage from "./error-page"; import * as faultsPage from "./faults-page"; import Authorizer from "../common/authorizer"; +import * as funcCache from "../common/func-cache"; window.authorizer = new Authorizer(window.permissionSets); @@ -68,6 +69,8 @@ function pagify(pageName, page) { return component; } +setInterval(funcCache.purge, 120000); + m.route(document.body, "/overview", { "/login": pagify("login", loginPage), "/overview": pagify("overview", overviewPage), diff --git a/client/components/overview-dot.js b/client/components/overview-dot.js index 228021c..e306be8 100644 --- a/client/components/overview-dot.js +++ b/client/components/overview-dot.js @@ -1,17 +1,21 @@ "use strict"; import m from "mithril"; -import * as config from "../config"; +import config from "../config"; import * as store from "../store"; +import * as funcCache from "../../common/func-cache"; +import Filter from "../../common/filter"; -const CHARTS = config.get("ui.overview.charts"); +const CHARTS = config.ui.overview.charts; const component = { view: vnode => { const device = vnode.attrs.device; const chart = CHARTS[vnode.attrs.chart]; for (let slice of Object.values(chart.slices)) { - const filter = store.unpackFilter(slice.filter); + const filter = store.unpackFilter( + funcCache.get(Filter.parse, slice.filter) + ); if (filter.test(device)) { const dot = m( "svg", diff --git a/client/config.js b/client/config.js index 48ec035..8a1dfcf 100644 --- a/client/config.js +++ b/client/config.js @@ -1,40 +1,3 @@ "use strict"; -import * as Filter from "../common/filter"; - -const configFile = window.clientConfig; - -const cache = {}; - -function parse(name, value) { - if (/^ui\.overview\.charts\.[^.]+\.slices\.[^.]+\.filter$/.test(name)) - return new Filter(value); - - return value; -} - -function get(name) { - let v = cache[name]; - if (v === undefined) { - v = configFile[name]; - if (v != null) { - v = parse(name, v); - } else { - const prefix = `${name}.`; - for (let k of Object.keys(configFile)) - if (k.startsWith(prefix)) { - v = v || {}; - let ar = k.slice(prefix.length).split("."); - let ref = v; - for (let a of ar.slice(0, -1)) ref = ref[a] = ref[a] || {}; - ref[ar[ar.length - 1]] = parse(k, configFile[k]); - } - } - if (v) cache[name] = v; - else cache[name] = null; - } - - return v; -} - -export { get }; +export default window.clientConfig; diff --git a/client/device-page.js b/client/device-page.js index 8eef07e..a42a045 100644 --- a/client/device-page.js +++ b/client/device-page.js @@ -2,7 +2,7 @@ import m from "mithril"; -import * as config from "./config"; +import config from "./config"; import Filter from "../common/filter"; import * as store from "./store"; import * as components from "./components"; @@ -25,7 +25,7 @@ const component = { let dev = store.fetch("devices", vnode.attrs.deviceFilter).value; if (!dev.length) return "Loading"; - const conf = config.get("ui.device"); + const conf = config.ui.device; const cmps = []; for (let c of Object.values(conf)) diff --git a/client/devices-page.js b/client/devices-page.js index 3c2a823..777e947 100644 --- a/client/devices-page.js +++ b/client/devices-page.js @@ -2,7 +2,7 @@ import m from "mithril"; -import * as config from "./config"; +import config from "./config"; import filterComponent from "./filter-component"; import * as filterParser from "../common/filter-parser"; import Filter from "../common/filter"; @@ -17,7 +17,7 @@ function init(args) { return reject(new Error("You are not authorized to view this page")); let filter = new Filter(args.filter); - let indexParameters = Object.values(config.get("ui.index")).map(p => + let indexParameters = Object.values(config.ui.index).map(p => Object.assign({}, p, { parameter: filterParser.parseParameter(p.parameter) }) @@ -302,7 +302,7 @@ const component = { return [ m("h1", "Listing devices"), m(filterComponent, { - predefined: Object.values(config.get("ui.filters")), + predefined: Object.values(config.ui.filters), filter: vnode.attrs.filter ? vnode.attrs.filter.ast : null, onChange: onFilterChanged }), diff --git a/client/overview-page.js b/client/overview-page.js index 740bfc6..362902a 100644 --- a/client/overview-page.js +++ b/client/overview-page.js @@ -1,23 +1,27 @@ "use strict"; import m from "mithril"; -import * as config from "./config"; +import config from "./config"; import * as store from "./store"; import pieChartComponent from "./pie-chart-component"; +import Filter from "../common/filter"; +import * as funcCache from "../common/func-cache"; -const GROUPS = config.get("ui.overview.groups"); +const GROUPS = config.ui.overview.groups; const CHARTS = {}; for (let group of Object.values(GROUPS)) for (let chartName of Object.values(group["charts"])) - CHARTS[chartName] = config.get("ui.overview.charts")[chartName]; + CHARTS[chartName] = config.ui.overview.charts[chartName]; function queryCharts(charts) { charts = Object.assign({}, charts); for (let [chartName, chart] of Object.entries(charts)) { charts[chartName] = chart = Object.assign({}, chart); + chart.slices = Object.assign({}, chart.slices); for (let [sliceName, slice] of Object.entries(chart.slices)) { + const filter = funcCache.get(Filter.parse, slice.filter); chart.slices[sliceName] = slice = Object.assign({}, slice); - slice.count = store.count("devices", slice.filter); + slice.count = store.count("devices", filter); } } return charts; @@ -33,7 +37,7 @@ const init = function() { }; const component = { - view: () => { + view: vnode => { document.title = "Overview - GenieACS"; let children = []; for (let group of Object.values(GROUPS)) { @@ -41,7 +45,7 @@ const component = { let groupChildren = []; for (let chartName of Object.values(group.charts)) { - const chart = CHARTS[chartName]; + const chart = vnode.attrs.charts[chartName]; let chartChildren = []; if (chart.label) chartChildren.push(m("h2", chart.label)); diff --git a/client/pie-chart-component.js b/client/pie-chart-component.js index 025fd07..745e116 100644 --- a/client/pie-chart-component.js +++ b/client/pie-chart-component.js @@ -28,7 +28,7 @@ const drawChart = function(chartData) { "a", { href: `/#!/devices/?${m.buildQueryString({ - filter: slice.filter.toString() + filter: slice.filter })}` }, slice.count.value || 0 @@ -68,7 +68,7 @@ const drawChart = function(chartData) { "a", { href: `/#!/devices/?${m.buildQueryString({ - filter: slice.filter.toString() + filter: slice.filter })}` }, [ diff --git a/client/store.js b/client/store.js index 410f9f4..ac8435f 100644 --- a/client/store.js +++ b/client/store.js @@ -2,8 +2,9 @@ import m from "mithril"; import Filter from "../common/filter"; -import * as config from "./config"; +import config from "./config"; import * as filterParser from "../common/filter-parser"; +import * as funcCache from "../common/func-cache"; let fulfillTimestamp = 0; diff --git a/common/filter.js b/common/filter.js index a0ebd62..24788d1 100644 --- a/common/filter.js +++ b/common/filter.js @@ -3,6 +3,7 @@ const filterParser = require("./filter-parser"); const filterCnf = require("./filter-cnf"); const satSolver = require("./sat-solver"); +const funcCache = require("./func-cache"); function* permute(arr) { if (arr.length <= 1) { @@ -24,6 +25,10 @@ class Filter { else this.ast = filterParser.parse(str); } + static parse(str) { + return new Filter(str); + } + toString() { if (!this.string) if (this.ast) this.string = filterParser.stringify(this.ast); diff --git a/common/func-cache.js b/common/func-cache.js new file mode 100644 index 0000000..837f01d --- /dev/null +++ b/common/func-cache.js @@ -0,0 +1,27 @@ +"use strict"; + +let cache1 = new WeakMap(); +let cache2 = new WeakMap(); + +function get(func, k) { + let c1 = cache1.get(func); + if (!c1) cache1.set(func, (c1 = new Map())); + if (c1.has(k)) return c1.get(k); + const c2 = cache2.get(func); + const v = c2 && c2.has(k) ? c2.get(k) : func(k); + c1.set(k, v); + return v; +} + +function getter(func) { + return k => get(func, k); +} + +function purge() { + cache2 = cache1; + cache1 = new WeakMap(); +} + +exports.get = get; +exports.getter = getter; +exports.purge = purge; diff --git a/config.json b/config.json index 6016c8c..f1286fd 100644 --- a/config.json +++ b/config.json @@ -1,119 +1,259 @@ { - "server.port": 3000, - "server.nbi": "http://127.0.0.1:7557/", - "server.mongodbConnectionUrl": "mongodb://127.0.0.1:27017", - "server.jwtSecret": "jwtSecret", - "auth.simple.users.admin.password": "admin", - "auth.simple.users.admin.roles": "admin", - "auth.simple.users.view.password": "view", - "auth.simple.users.view.roles": "view", - "permissions.admin.1.devices.access": 3, - "permissions.admin.1.devices.validate.pass": true, - "permissions.admin.1.faults.access": 3, - "permissions.admin.1.faults.validate.pass": true, - "permissions.admin.1.files.access": 3, - "permissions.view.1.devices.access": 2, - "permissions.view.1.faults.access": 2, - "permissions.view.1.files.access": 2, - "ui.pageSize": 10, - "ui.filters.1.label": "Serial number", - "ui.filters.1.parameter": "DeviceID.SerialNumber", - "ui.filters.1.type": "string", - "ui.filters.2.label": "Product class", - "ui.filters.2.parameter": "DeviceID.ProductClass", - "ui.filters.2.type": "string", - "ui.filters.3.label": "Tag", - "ui.filters.3.parameter": "tag", - "ui.filters.3.type": "string", - "ui.index.1.label": "Serial number", - "ui.index.1.parameter": "DeviceID.SerialNumber", - "ui.index.2.label": "Product class", - "ui.index.2.parameter": "DeviceID.ProductClass", - "ui.index.3.label": "Software version", - "ui.index.3.parameter": "InternetGatewayDevice.DeviceInfo.SoftwareVersion", - "ui.index.4.label": "MAC", - "ui.index.4.parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress", - "ui.index.4.label": "IP", - "ui.index.4.parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", - "ui.index.5.label": "SSID", - "ui.index.5.parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", - "ui.index.6.label": "Last inform", - "ui.index.6.parameter": "Events.Inform", - "ui.index.6.type": "container", - "ui.index.6.element": "span.inform", - "ui.index.6.components.1.type": "parameter", - "ui.index.6.components.2.type": "overview-dot", - "ui.index.6.components.2.chart": "online", - "ui.index.7.label": "Tags", - "ui.index.7.parameter": "Tags", - "ui.index.7.type": "tags", - "ui.index.7.writable": false, - "ui.overview.groups.online.label": "", - "ui.overview.groups.online.charts.1": "online", - "ui.overview.charts.online.label": "Online status", - "ui.overview.charts.online.slices.onlineNow.label": "Online now", - "ui.overview.charts.online.slices.onlineNow.color": "#31a354", - "ui.overview.charts.online.slices.onlineNow.filter": "Events.Inform > NOW() - 5 * 60 * 1000", - "ui.overview.charts.online.slices.past24.label": "Past 24 hours", - "ui.overview.charts.online.slices.past24.color": "#addd8e", - "ui.overview.charts.online.slices.past24.filter": "Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)", - "ui.overview.charts.online.slices.others.label": "Others", - "ui.overview.charts.online.slices.others.color": "#f7fcb9", - "ui.overview.charts.online.slices.others.filter": "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)", - "ui.device.10.type": "tags", - "ui.device.25.type": "ping", - "ui.device.30.type": "parameter-list", - "ui.device.30.parameters.1.type": "container", - "ui.device.30.parameters.1.label": "Last inform", - "ui.device.30.parameters.1.element": "span.inform", - "ui.device.30.parameters.1.parameter": "Events.Inform", - "ui.device.30.parameters.1.components.1.type": "parameter", - "ui.device.30.parameters.1.components.2.type": "overview-dot", - "ui.device.30.parameters.1.components.2.chart": "online", - "ui.device.30.parameters.1.components.3.type": "summon-button", - "ui.device.30.parameters.1.components.3.parameters.1": "InternetGatewayDevice.DeviceInfo.HardwareVersion", - "ui.device.30.parameters.1.components.3.parameters.2": "InternetGatewayDevice.DeviceInfo.SoftwareVersion", - "ui.device.30.parameters.1.components.3.parameters.3": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress", - "ui.device.30.parameters.1.components.3.parameters.4": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", - "ui.device.30.parameters.1.components.3.parameters.5": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", - "ui.device.30.parameters.1.components.3.parameters.6": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase", - "ui.device.30.parameters.1.components.3.parameters.7": "InternetGatewayDevice.LANDevice.1.Hosts.Host", - "ui.device.30.parameters.2.label": "Last boot", - "ui.device.30.parameters.2.parameter": "Events.1_BOOT", - "ui.device.30.parameters.3.label": "Last bootstrap", - "ui.device.30.parameters.3.parameter": "Events.0_BOOTSTRAP", - "ui.device.30.parameters.4.label": "Registered", - "ui.device.30.parameters.4.parameter": "Events.Registered", - "ui.device.30.parameters.2.label": "Serial number", - "ui.device.30.parameters.2.parameter": "InternetGatewayDevice.DeviceInfo.SerialNumber", - "ui.device.30.parameters.3.label": "Product class", - "ui.device.30.parameters.3.parameter": "InternetGatewayDevice.DeviceInfo.ProductClass", - "ui.device.30.parameters.4.label": "OUI", - "ui.device.30.parameters.4.parameter": "InternetGatewayDevice.DeviceInfo.ManufacturerOUI", - "ui.device.30.parameters.5.label": "Manufacturer", - "ui.device.30.parameters.5.parameter": "InternetGatewayDevice.DeviceInfo.Manufacturer", - "ui.device.30.parameters.6.label": "Hardware version", - "ui.device.30.parameters.6.parameter": "InternetGatewayDevice.DeviceInfo.HardwareVersion", - "ui.device.30.parameters.7.label": "Software version", - "ui.device.30.parameters.7.parameter": "InternetGatewayDevice.DeviceInfo.SoftwareVersion", - "ui.device.30.parameters.8.label": "MAC", - "ui.device.30.parameters.8.parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress", - "ui.device.30.parameters.9.label": "IP", - "ui.device.30.parameters.9.parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", - "ui.device.30.parameters.10.label": "WLAN SSID", - "ui.device.30.parameters.10.parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", - "ui.device.30.parameters.11.label": "WLAN passphrase", - "ui.device.30.parameters.11.parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase", - "ui.device.40.type": "parameter-table", - "ui.device.40.label": "WLAN hosts", - "ui.device.40.parameter": "InternetGatewayDevice.LANDevice.1.Hosts.Host", - "ui.device.40.childParameters.1.label": "Host name", - "ui.device.40.childParameters.1.parameter": "HostName", - "ui.device.40.childParameters.2.label": "IP address", - "ui.device.40.childParameters.2.parameter": "IPAddress", - "ui.device.40.childParameters.3.label": "MAC address", - "ui.device.40.childParameters.3.parameter": "MACAddress", - "ui.device.50.type": "device-faults", - "ui.device.60.type": "all-parameters", - "ui.device.70.type": "device-actions" + "server": { + "port": 3000, + "nbi": "http://127.0.0.1:7557/", + "mongodbConnectionUrl": "mongodb://127.0.0.1:27017", + "jwtSecret": "jwtSecret" + }, + "auth": { + "simple": { + "users": { + "admin": { + "password": "admin", + "roles": "admin" + }, + "view": { + "password": "view", + "roles": "view" + } + } + } + }, + "permissions": { + "admin": [ + { + "devices": { + "access": 3, + "validate": { + "pass": true + } + }, + "faults": { + "access": 3, + "validate": { + "pass": true + } + }, + "files": { + "access": 3 + } + } + ], + "view": [ + { + "devices": { + "access": 2 + }, + "faults": { + "access": 2 + }, + "files": { + "access": 2 + } + } + ] + }, + "ui": { + "pageSize": 10, + "filters": [ + { + "label": "Serial number", + "parameter": "DeviceID.SerialNumber", + "type": "string" + }, + { + "label": "Product class", + "parameter": "DeviceID.ProductClass", + "type": "string" + }, + { + "label": "Tag", + "parameter": "tag", + "type": "string" + } + ], + "index": [ + { + "label": "Serial number", + "parameter": "DeviceID.SerialNumber" + }, + { + "label": "Product class", + "parameter": "DeviceID.ProductClass" + }, + { + "label": "Software version", + "parameter": "InternetGatewayDevice.DeviceInfo.SoftwareVersion" + }, + { + "label": "IP", + "parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress" + }, + { + "label": "SSID", + "parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID" + }, + { + "label": "Last inform", + "parameter": "Events.Inform", + "type": "container", + "element": "span.inform", + "components": [ + { + "type": "parameter" + }, + { + "type": "overview-dot", + "chart": "online" + } + ] + }, + { + "label": "Tags", + "parameter": "Tags", + "type": "tags", + "writable": false + } + ], + "overview": { + "groups": { + "online": { + "label": "", + "charts": [ + "online" + ] + } + }, + "charts": { + "online": { + "label": "Online status", + "slices": { + "onlineNow": { + "label": "Online now", + "color": "#31a354", + "filter": "Events.Inform > NOW() - 5 * 60 * 1000" + }, + "past24": { + "label": "Past 24 hours", + "color": "#addd8e", + "filter": "Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)" + }, + "others": { + "label": "Others", + "color": "#f7fcb9", + "filter": "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)" + } + } + } + } + }, + "device": [ + { + "type": "tags" + }, + { + "type": "ping" + }, + { + "type": "parameter-list", + "parameters": [ + { + "type": "container", + "label": "Last inform", + "element": "span.inform", + "parameter": "Events.Inform", + "components": [ + { + "type": "parameter" + }, + { + "type": "overview-dot", + "chart": "online" + }, + { + "type": "summon-button", + "parameters": [ + "InternetGatewayDevice.DeviceInfo.HardwareVersion", + "InternetGatewayDevice.DeviceInfo.SoftwareVersion", + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress", + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", + "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", + "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase", + "InternetGatewayDevice.LANDevice.1.Hosts.Host" + ] + } + ] + }, + { + "label": "Serial number", + "parameter": "InternetGatewayDevice.DeviceInfo.SerialNumber" + }, + { + "label": "Product class", + "parameter": "InternetGatewayDevice.DeviceInfo.ProductClass" + }, + { + "label": "OUI", + "parameter": "InternetGatewayDevice.DeviceInfo.ManufacturerOUI" + }, + { + "label": "Manufacturer", + "parameter": "InternetGatewayDevice.DeviceInfo.Manufacturer" + }, + { + "label": "Hardware version", + "parameter": "InternetGatewayDevice.DeviceInfo.HardwareVersion" + }, + { + "label": "Software version", + "parameter": "InternetGatewayDevice.DeviceInfo.SoftwareVersion" + }, + { + "label": "MAC", + "parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress" + }, + { + "label": "IP", + "parameter": "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress" + }, + { + "label": "WLAN SSID", + "parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID" + }, + { + "label": "WLAN passphrase", + "parameter": "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase" + } + ] + }, + { + "type": "parameter-table", + "label": "WLAN hosts", + "parameter": "InternetGatewayDevice.LANDevice.1.Hosts.Host", + "childParameters": [ + { + "label": "Host name", + "parameter": "HostName" + }, + { + "label": "IP address", + "parameter": "IPAddress" + }, + { + "label": "MAC address", + "parameter": "MACAddress" + } + ] + }, + { + "type": "device-faults" + }, + { + "type": "all-parameters" + }, + { + "type": "device-actions" + } + ] + } } diff --git a/server/api-functions.js b/server/api-functions.js index 8353690..c10c476 100644 --- a/server/api-functions.js +++ b/server/api-functions.js @@ -92,7 +92,7 @@ function count(resource, filter, limit) { if (limit) qs.limit = limit; let options = url.parse( - `${config.get("server.nbi")}${resource}?${querystring.stringify(qs)}` + `${config.server.nbi}${resource}?${querystring.stringify(qs)}` ); options.method = "HEAD"; @@ -133,7 +133,7 @@ function query(resource, filter, limit, skip, projection, callback) { qs.sort = JSON.stringify({ _id: 1 }); let options = url.parse( - `${config.get("server.nbi")}${resource}?${querystring.stringify(qs)}` + `${config.server.nbi}${resource}?${querystring.stringify(qs)}` ); let _http = options.protocol === "https:" ? https : http; @@ -209,7 +209,7 @@ function query(resource, filter, limit, skip, projection, callback) { function deleteResource(resource, id) { return new Promise((resolve, reject) => { let options = url.parse( - `${config.get("server.nbi")}${resource}/${encodeURIComponent(id)}` + `${config.server.nbi}${resource}/${encodeURIComponent(id)}` ); options.method = "DELETE"; @@ -229,7 +229,7 @@ function deleteResource(resource, id) { function postTask(task, connectionRequest, callback) { let options = url.parse( - `${config.get("server.nbi")}devices/${task.device}/tasks${ + `${config.server.nbi}devices/${task.device}/tasks${ connectionRequest ? "?connection_request" : "" }` ); @@ -346,9 +346,9 @@ function updateTags(deviceId, tags) { ([tag, onOff]) => new Promise((resolve, reject) => { const options = url.parse( - `${config.get( - "server.nbi" - )}devices/${deviceId}/tags/${encodeURIComponent(tag)}` + `${config.server.nbi}devices/${deviceId}/tags/${encodeURIComponent( + tag + )}` ); if (onOff) options.method = "POST"; else options.method = "DELETE"; @@ -370,7 +370,7 @@ function updateTags(deviceId, tags) { function ping(host) { return new Promise((resolve, reject) => { const options = url.parse( - `${config.get("server.nbi")}ping/${encodeURIComponent(host)}` + `${config.server.nbi}ping/${encodeURIComponent(host)}` ); let _http = options.protocol === "https:" ? https : http; diff --git a/server/config.js b/server/config.js index c94460a..b7c5a92 100644 --- a/server/config.js +++ b/server/config.js @@ -1,72 +1,37 @@ "use strict"; -const filterParser = require("../common/filter-parser.js"); -const Filter = require("../common/filter.js"); - const configFile = require("../config.json"); -const cache = {}; - -function parse(name, value) { - if (/^ui\.overview\.charts\.[^.]+\.slices\.[^.]+\.filter$/.test(name)) - return new Filter(value); - else if (/^permissions\.[^.]+\.[^.]+\.devices\.filter$/.test(name)) - return new Filter(value); - else if (/^auth\.[^.]+\.users\.[^.]+\.roles$/.test(name)) - return value - .split(",") - .map(x => x.trim()) - .sort(); - - return value; -} - -function get(name) { - let v = cache[name]; - if (v === undefined) { - v = configFile[name]; - if (v != null) { - v = parse(name, v); - } else { - const prefix = `${name}.`; - for (let k of Object.keys(configFile)) - if (k.startsWith(prefix)) { - v = v || {}; - let ar = k.slice(prefix.length).split("."); - let ref = v; - for (let a of ar.slice(0, -1)) ref = ref[a] = ref[a] || {}; - ref[ar[ar.length - 1]] = parse(k, configFile[k]); - } - } - if (v) cache[name] = v; - else cache[name] = null; - } - - return v; -} +let config; function getClientConfig() { - const regex = [ - /^ui\.pageSize$/, - /^ui\.filters\.[^.]+\.label$/, - /^ui\.filters\.[^.]+\.parameter$/, - /^ui\.filters\.[^.]+\.type$/, - /^ui\.index\./, - /^ui\.overview\.groups\./, - /^ui\.overview\.charts\./, - /^ui\.device/ - ]; - - const clientConfig = {}; - for (let [k, v] of Object.entries(configFile)) - for (let r of regex) - if (r.test(k)) { - clientConfig[k] = v; - break; - } - - return clientConfig; + return Object.assign({}, config, { + server: null, + auth: null, + permissions: null + }); } -exports.get = get; -exports.getClientConfig = getClientConfig; +function loadConfig(conf, root = {}) { + for (const [key, value] of Object.entries(conf)) { + const keys = key.split("."); + let ref = root; + while (keys.length > 1) { + const k = keys.shift(); + if (typeof ref[k] !== "object") ref[k] = {}; + ref = ref[k]; + } + + if (typeof value === "object") { + if (typeof ref[keys[0]] !== "object") ref[keys[0]] = {}; + loadConfig(value, ref[keys[0]]); + } else { + ref[keys[0]] = value; + } + } + return root; +} + +config = loadConfig(configFile); + +module.exports = Object.assign({}, config, { getClientConfig }); diff --git a/server/db.js b/server/db.js index 0cb0418..088c7d6 100644 --- a/server/db.js +++ b/server/db.js @@ -10,7 +10,7 @@ function getClient() { if (_client) return Promise.resolve(_client); return new Promise((resolve, reject) => { - const CONNECTION_URL = config.get("server.mongodbConnectionUrl"); + const CONNECTION_URL = config.server.mongodbConnectionUrl; MongoClient.connect(CONNECTION_URL, (err, client) => { if (err) return reject(err); resolve((_client = client.db("genieacs-ui"))); diff --git a/server/server.js b/server/server.js index 6785e11..bf5bdfd 100644 --- a/server/server.js +++ b/server/server.js @@ -15,11 +15,11 @@ const Authorizer = require("../common/authorizer"); const koa = new Koa(); const router = new Router(); -const JWT_SECRET = config.get("server.jwtSecret"); +const JWT_SECRET = config.server.jwtSecret; const JWT_COOKIE = "genieacs-ui-jwt"; function getPermissionSets(roles) { - const allPermissions = config.get("permissions"); + const allPermissions = config.permissions; const permissionSets = roles.map(r => Object.values(allPermissions[r] || {})); return permissionSets; } @@ -46,7 +46,7 @@ router.post("/login", async ctx => { const username = ctx.request.body.username; const password = ctx.request.body.password; - const user = config.get(`auth.simple.users.${username}`); + const user = config.auth.simple.users[username]; if (!user || user.password !== password) { ctx.status = 400; @@ -54,7 +54,10 @@ router.post("/login", async ctx => { return; } - let token = jwt.sign({ username: username, roles: user.roles }, JWT_SECRET); + let token = jwt.sign( + { username: username, roles: user.roles.split(",") }, + JWT_SECRET + ); ctx.cookies.set(JWT_COOKIE, token); ctx.body = JSON.stringify(token); }); @@ -101,7 +104,7 @@ koa.use( koa.use(router.routes()); koa.use(koaStatic("./public")); -koa.listen(config.get("server.port"), () => { +koa.listen(config.server.port, () => { // eslint-disable-next-line no-console - console.log(`Server listening on port ${config.get("server.port")}`); + console.log(`Server listening on port ${config.server.port}`); });