diff --git a/.eslintrc.json b/.eslintrc.json index a3fa8c9..e5342a5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,8 @@ "node": true }, "parserOptions": { - "ecmaVersion": 8 + "ecmaVersion": 8, + "sourceType": "module" }, "extends": "eslint:recommended", "rules": { diff --git a/bin/genieacs-cwmp b/bin/genieacs-cwmp index 51e4cdf..63fcbc6 100755 --- a/bin/genieacs-cwmp +++ b/bin/genieacs-cwmp @@ -1,20 +1,20 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm "use strict"; -const path = require("path"); -const fs = require("fs"); +import path from "path"; +import fs from "fs"; process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); -const config = require("../lib/config"); -const logger = require("../lib/logger"); -const cluster = require("../lib/cluster"); -const server = require("../lib/server"); -const cwmp = require("../lib/cwmp"); -const db = require("../lib/db"); -const extensions = require("../lib/extensions"); -const cache = require("../lib/cache"); +import * as config from "../lib/config"; +import * as logger from "../lib/logger"; +import * as cluster from "../lib/cluster"; +import * as server from "../lib/server"; +import * as cwmp from "../lib/cwmp"; +import * as db from "../lib/db"; +import * as extensions from "../lib/extensions"; +import * as cache from "../lib/cache"; -const VERSION = require("../package.json").version; +import { version as VERSION } from "../package.json"; logger.init("cwmp", VERSION); diff --git a/bin/genieacs-fs b/bin/genieacs-fs index 03a33d6..a733826 100755 --- a/bin/genieacs-fs +++ b/bin/genieacs-fs @@ -1,19 +1,18 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm "use strict"; -const path = require("path"); -const fs = require("fs"); +import path from "path"; +import fs from "fs"; process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); -const config = require("../lib/config"); -const logger = require("../lib/logger"); -const cluster = require("../lib/cluster"); -const server = require("../lib/server"); -const _fs = require("../lib/fs"); -const db = require("../lib/db"); -const cache = require("../lib/cache"); - -const VERSION = require("../package.json").version; +import * as config from "../lib/config"; +import * as logger from "../lib/logger"; +import * as cluster from "../lib/cluster"; +import * as server from "../lib/server"; +import { listener } from "../lib/fs"; +import * as db from "../lib/db"; +import * as cache from "../lib/cache"; +import { version as VERSION } from "../package.json"; logger.init("fs", VERSION); @@ -99,13 +98,7 @@ if (!cluster.worker) { if (err) throw err; cache.connect(err => { if (err) throw err; - server.start( - SERVICE_PORT, - SERVICE_ADDRESS, - ssl, - _fs.listener, - _fs.onConnection - ); + server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, listener); }); }); } diff --git a/bin/genieacs-nbi b/bin/genieacs-nbi index 04e3a06..e1c4b7b 100755 --- a/bin/genieacs-nbi +++ b/bin/genieacs-nbi @@ -1,20 +1,19 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm "use strict"; -const path = require("path"); -const fs = require("fs"); +import path from "path"; +import fs from "fs"; process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); -const config = require("../lib/config"); -const logger = require("../lib/logger"); -const cluster = require("../lib/cluster"); -const server = require("../lib/server"); -const nbi = require("../lib/nbi"); -const db = require("../lib/db"); -const extensions = require("../lib/extensions"); -const cache = require("../lib/cache"); - -const VERSION = require("../package.json").version; +import * as config from "../lib/config"; +import * as logger from "../lib/logger"; +import * as cluster from "../lib/cluster"; +import * as server from "../lib/server"; +import { listener } from "../lib/nbi"; +import * as db from "../lib/db"; +import * as extensions from "../lib/extensions"; +import * as cache from "../lib/cache"; +import { version as VERSION } from "../package.json"; logger.init("nbi", VERSION); @@ -103,13 +102,7 @@ if (!cluster.worker) { if (err) throw err; cache.connect(err => { if (err) throw err; - server.start( - SERVICE_PORT, - SERVICE_ADDRESS, - ssl, - nbi.listener, - nbi.onConnection - ); + server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, listener); }); }); } diff --git a/bin/genieacs-ui b/bin/genieacs-ui index ff864a3..221513d 100755 --- a/bin/genieacs-ui +++ b/bin/genieacs-ui @@ -1,20 +1,19 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm "use strict"; -const path = require("path"); -const fs = require("fs"); +import * as path from "path"; +import * as fs from "fs"; process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); -const config = require("../lib/config"); -const logger = require("../lib/logger"); -const cluster = require("../lib/cluster"); -const server = require("../lib/server"); -const ui = require("../lib/ui"); -const db = require("../lib/ui/db"); -const db2 = require("../lib/db"); -const cache = require("../lib/cache"); - -const VERSION = require("../package.json").version; +import * as config from "../lib/config"; +import * as logger from "../lib/logger"; +import * as cluster from "../lib/cluster"; +import * as server from "../lib/server"; +import { listener } from "../lib/ui"; +import * as db from "../lib/ui/db"; +import * as db2 from "../lib/db"; +import * as cache from "../lib/cache"; +import { version as VERSION } from "../package.json"; logger.init("ui", VERSION); @@ -102,22 +101,16 @@ if (!cluster.worker) { .catch(exitWorkerUngracefully); }); - const listener = (req, res) => { + const _listener = (req, res) => { if (stopping) res.setHeader("Connection", "close"); - ui.listener(req, res); + listener(req, res); }; db2.connect(err => { if (err) throw err; cache.connect(err => { if (err) throw err; - server.start( - SERVICE_PORT, - SERVICE_ADDRESS, - ssl, - listener, - ui.onConnection - ); + server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener); }); }); } diff --git a/build/build.js b/build/build.js index 3ed7979..20dfa2f 100644 --- a/build/build.js +++ b/build/build.js @@ -1,8 +1,11 @@ const path = require("path"); const fs = require("fs"); -const util = require("util"); +const { promisify } = require("util"); +const { rollup } = require("rollup"); +const rollupReplace = require("rollup-plugin-replace"); +const rollupJson = require("rollup-plugin-json"); +const { terser } = require("rollup-plugin-terser"); const webpack = require("webpack"); -const nodeExternals = require("webpack-node-externals"); const postcss = require("postcss"); const postcssImport = require("postcss-import"); const postcssCssNext = require("postcss-cssnext"); @@ -18,6 +21,37 @@ const BUILD_METADATA = new Date() const INPUT_DIR = path.resolve(__dirname, ".."); const OUTPUT_DIR = path.resolve(__dirname, "../dist"); +const externals = [ + "path", + "fs", + "cluster", + "os", + "http", + "https", + "zlib", + "crypto", + "mongodb", + "libxmljs", + "vm", + "later", + "parsimmon", + "seedrandom", + "querystring", + "child_process", + "dgram", + "url", + "koa", + "koa-router", + "koa-compress", + "koa-bodyparser", + "koa-jwt", + "koa-static", + "jsonwebtoken", + "stream", + "mithril", + "parsimmon" +]; + function rmDirSync(dirPath) { if (!fs.existsSync(dirPath)) return; const files = fs.readdirSync(dirPath); @@ -30,22 +64,6 @@ function rmDirSync(dirPath) { fs.rmdirSync(dirPath); } -function cpDirSync(src, dst) { - fs.mkdirSync(dst); - const files = fs.readdirSync(src); - for (const file of files) { - const current = fs.lstatSync(path.join(src, file)); - if (current.isDirectory()) { - cpDirSync(path.join(src, file), path.join(dst, file)); - } else if (current.isSymbolicLink()) { - const symlink = fs.readlinkSync(path.join(src, file)); - fs.symlinkSync(symlink, path.join(dst, file)); - } else { - fs.copyFileSync(path.join(src, file), path.join(dst, file)); - } - } -} - async function init() { // Delete any old output directory rmDirSync(OUTPUT_DIR); @@ -56,6 +74,7 @@ async function init() { fs.mkdirSync(OUTPUT_DIR + "/config"); fs.mkdirSync(OUTPUT_DIR + "/debug"); fs.mkdirSync(OUTPUT_DIR + "/public"); + fs.mkdirSync(OUTPUT_DIR + "/tools"); // Create package.json const packageJson = require("../package.json"); @@ -90,11 +109,6 @@ async function copyStatic() { path.resolve(OUTPUT_DIR, file) ); } - - cpDirSync( - path.resolve(INPUT_DIR, "tools"), - path.resolve(OUTPUT_DIR, "tools") - ); } async function generateCss() { @@ -109,82 +123,137 @@ async function generateCss() { fs.writeFileSync(cssOutPath, cssOut.css); } -async function generateJs() { - const backendConf = { - mode: MODE, - target: "node", - node: false, - entry: { - "genieacs-cwmp": path.resolve(INPUT_DIR, "bin/genieacs-cwmp"), - "genieacs-nbi": path.resolve(INPUT_DIR, "bin/genieacs-nbi"), - "genieacs-fs": path.resolve(INPUT_DIR, "bin/genieacs-fs"), - "genieacs-ui": path.resolve(INPUT_DIR, "bin/genieacs-ui") - }, - resolve: { - alias: { - [path.resolve(INPUT_DIR, "package.json")]: path.resolve( - OUTPUT_DIR, - "package.json" - ) - } - }, - externals: [nodeExternals()], - output: { - path: path.resolve(OUTPUT_DIR, "bin") - }, - module: { - rules: [ - { - include: path.resolve(INPUT_DIR, "bin"), - use: path.join(__dirname, "./shebang-loader") - } +async function generateToolsJs() { + for (const bin of ["configure-ui", "dump-data-model"]) { + const inputFile = path.resolve(INPUT_DIR, `tools/${bin}`); + const outputFile = path.resolve(OUTPUT_DIR, `tools/${bin}`); + const bundle = await rollup({ + input: inputFile, + external: externals, + acorn: { + allowHashBang: true + }, + plugins: [ + rollupReplace({ + delimiters: ["", ""], + "#!/usr/bin/env -S node -r esm": "" + }), + MODE === "production" ? terser() : null ] - }, - plugins: [ - new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) - ] - }; + }); - const frontendConf = { - mode: MODE, - entry: path.resolve(INPUT_DIR, "ui/app.js"), - output: { - path: path.resolve(OUTPUT_DIR, "public"), - filename: "app.js" - }, - resolve: { - alias: { - [path.resolve(INPUT_DIR, "package.json")]: path.resolve( - OUTPUT_DIR, - "package.json" - ) - } - } - }; + await bundle.write({ + format: "cjs", + preferConst: true, + banner: "#!/usr/bin/env node", + file: outputFile + }); - const stats = await util.promisify(webpack)([backendConf, frontendConf]); + // Mark as executable + const mode = fs.statSync(outputFile).mode; + fs.chmodSync(outputFile, mode | 73); + } +} - // Remove js ext and mark as executable - for (const file of [ +async function generateBackendJs() { + for (const bin of [ "genieacs-cwmp", "genieacs-nbi", "genieacs-fs", "genieacs-ui" ]) { - fs.renameSync( - path.resolve(OUTPUT_DIR, `bin/${file}.js`), - path.resolve(OUTPUT_DIR, `bin/${file}`) - ); - const mode = fs.statSync(path.resolve(OUTPUT_DIR, `bin/${file}`)).mode; - fs.chmodSync(path.resolve(OUTPUT_DIR, `bin/${file}`), mode | 73); - } + const inputFile = path.resolve(INPUT_DIR, `bin/${bin}`); + const outputFile = path.resolve(OUTPUT_DIR, `bin/${bin}`); + const bundle = await rollup({ + input: inputFile, + external: externals, + acorn: { + allowHashBang: true + }, + treeshake: { + propertyReadSideEffects: false, + pureExternalModules: true + }, + plugins: [ + rollupReplace({ + delimiters: ["", ""], + "#!/usr/bin/env -S node -r esm": "" + }), + rollupJson({ preferConst: true }), + { + resolveId: (importee, importer) => { + if (importee.endsWith("/package.json")) { + const p = path.resolve(path.dirname(importer), importee); + if (p === path.resolve(INPUT_DIR, "package.json")) + return path.resolve(OUTPUT_DIR, "package.json"); + } + return null; + } + }, + MODE === "production" ? terser() : null + ] + }); + await bundle.write({ + format: "cjs", + preferConst: true, + banner: "#!/usr/bin/env node", + file: outputFile + }); + + // Mark as executable + const mode = fs.statSync(outputFile).mode; + fs.chmodSync(outputFile, mode | 73); + } +} + +async function generateFrontendJs() { + const inputFile = path.resolve(INPUT_DIR, "ui/app.js"); + const outputFile = path.resolve(OUTPUT_DIR, "public/app.js"); + + const bundle = await rollup({ + input: inputFile, + external: externals, + plugins: [rollupJson({ preferConst: true })], + inlineDynamicImports: true, + treeshake: { + propertyReadSideEffects: false, + pureExternalModules: true + }, + onwarn: (warning, warn) => { + // Ignore circular dependency warnings + if (warning.code !== "CIRCULAR_DEPENDENCY") warn(warning); + } + }); + + await bundle.write({ + preferConst: true, + format: "esm", + file: outputFile + }); + + const webpackConf = { + mode: MODE, + entry: outputFile, + output: { + path: path.resolve(OUTPUT_DIR, "public"), + filename: "app.js" + } + }; + + const stats = await promisify(webpack)(webpackConf); process.stdout.write(stats.toString({ colors: true }) + "\n"); } init() .then(() => { - Promise.all([copyStatic(), generateCss(), generateJs()]) + Promise.all([ + copyStatic(), + generateCss(), + generateToolsJs(), + generateBackendJs(), + generateFrontendJs() + ]) .then(() => {}) .catch(err => { process.stderr.write(err.stack + "\n"); diff --git a/build/shebang-loader.js b/build/shebang-loader.js deleted file mode 100644 index 2d4b80d..0000000 --- a/build/shebang-loader.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function(source) { - return source.replace(/^#![^\n\r]*[\r\n]/, ""); -}; diff --git a/lib/api-functions.js b/lib/api-functions.js index 7ccf014..c438879 100644 --- a/lib/api-functions.js +++ b/lib/api-functions.js @@ -18,15 +18,15 @@ */ "use strict"; -const crypto = require("crypto"); -const dgram = require("dgram"); -const URL = require("url"); -const http = require("http"); -const config = require("./config"); -const db = require("./db"); -const common = require("./common"); -const auth = require("./auth"); -const cache = require("./cache"); +import crypto from "crypto"; +import dgram from "dgram"; +import URL from "url"; +import http from "http"; +import * as config from "./config"; +import * as db from "./db"; +import * as common from "./common"; +import * as auth from "./auth"; +import * as cache from "./cache"; function udpConReq(address, un, key, callback) { if (!address) return false; @@ -187,7 +187,7 @@ function httpConReq( }); } -function connectionRequest(deviceId, callback) { +export function connectionRequest(deviceId, callback) { const CONNECTION_REQUEST_TIMEOUT = config.get( "CONNECTION_REQUEST_TIMEOUT", deviceId @@ -284,7 +284,7 @@ function connectionRequest(deviceId, callback) { }); } -function watchTask(deviceId, taskId, timeout, callback) { +export function watchTask(deviceId, taskId, timeout, callback) { setTimeout(() => { db.tasksCollection.findOne( { _id: taskId }, @@ -324,7 +324,7 @@ function sanitizeTask(task, callback) { callback(task); } -function insertTasks(tasks, callback) { +export function insertTasks(tasks, callback) { if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks]; else if (!tasks || tasks.length === 0) return void callback(null, tasks || []); @@ -344,7 +344,7 @@ function insertTasks(tasks, callback) { } } -function deleteDevice(deviceId, callback) { +export function deleteDevice(deviceId, callback) { db.tasksCollection.deleteMany({ device: deviceId }, err => { if (err) return void callback(err); db.devicesCollection.deleteOne({ _id: deviceId }, err => { @@ -373,8 +373,3 @@ function deleteDevice(deviceId, callback) { }); }); } - -exports.connectionRequest = connectionRequest; -exports.watchTask = watchTask; -exports.insertTasks = insertTasks; -exports.deleteDevice = deleteDevice; diff --git a/lib/auth.js b/lib/auth.js index 6f1c04a..9eec5c6 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -18,9 +18,9 @@ */ "use strict"; -const crypto = require("crypto"); +import { createHash } from "crypto"; -function parseAuthHeader(authHeader) { +export function parseAuthHeader(authHeader) { authHeader = authHeader.trim(); const method = authHeader.split(" ", 1)[0]; const res = { method: method }; @@ -53,11 +53,11 @@ function parseAuthHeader(authHeader) { return res; } -function basic(username, password) { +export function basic(username, password) { return "Basic " + Buffer.from(`${username}:${password}`).toString("base64"); } -function digest(username, password, uri, httpMethod, body, authHeader) { +export function digest(username, password, uri, httpMethod, body, authHeader) { const cnonce = "0a4f113b"; const nc = "00000001"; let qop; @@ -68,7 +68,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) { else qop = authHeader.qop; } - let ha1 = crypto.createHash("md5"); + let ha1 = createHash("md5"); ha1 .update(username) .update(":") @@ -78,7 +78,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) { // TODO support "MD5-sess" algorithm directive ha1 = ha1.digest("hex"); - let ha2 = crypto.createHash("md5"); + let ha2 = createHash("md5"); ha2 .update(httpMethod) .update(":") @@ -86,7 +86,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) { if (qop === "auth-int") ha2.update(":").update(body); ha2 = ha2.digest("hex"); - const hash = crypto.createHash("md5"); + const hash = createHash("md5"); hash .update(ha1) .update(":") @@ -113,7 +113,3 @@ function digest(username, password, uri, httpMethod, body, authHeader) { return authString; } - -exports.parseAuthHeader = parseAuthHeader; -exports.basic = basic; -exports.digest = digest; diff --git a/lib/cache.js b/lib/cache.js index 7d8f078..64478c5 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -18,7 +18,8 @@ */ "use strict"; -const config = require("./config"); +import { MongoClient } from "mongodb"; +import * as config from "./config"; const MAX_CACHE_TTL = config.get("MAX_CACHE_TTL"); @@ -26,11 +27,10 @@ let mongoClient; let mongoCollection; let mongoTimeOffset = 0; -function connect(callback) { - const mongodb = require("mongodb"); +export function connect(callback) { const MONGODB_CONNECTION_URL = config.get("MONGODB_CONNECTION_URL"); - mongodb.MongoClient.connect( + MongoClient.connect( MONGODB_CONNECTION_URL, { useNewUrlParser: true }, (err, client) => { @@ -51,11 +51,11 @@ function connect(callback) { ); } -function disconnect() { +export function disconnect() { if (mongoClient) mongoClient.close(); } -function get(key, callback) { +export function get(key, callback) { const expire = new Date(Date.now() - mongoTimeOffset); if (Array.isArray(key)) { mongoCollection.find({ _id: { $in: key } }).toArray((err, res) => { @@ -83,13 +83,13 @@ function get(key, callback) { } } -function del(key, callback) { +export function del(key, callback) { if (Array.isArray(key)) mongoCollection.deleteMany({ _id: { $in: key } }, callback); else mongoCollection.deleteOne({ _id: key }, callback); } -function set(key, value, ttl, callback) { +export function set(key, value, ttl, callback) { if (!callback && typeof v === "function") { callback = ttl; ttl = null; @@ -106,7 +106,7 @@ function set(key, value, ttl, callback) { ); } -function pop(key, callback) { +export function pop(key, callback) { mongoCollection.findOneAndDelete({ _id: key }, (err, res) => { if (err || !res["value"]) return void callback(err); @@ -117,7 +117,7 @@ function pop(key, callback) { }); } -function lock(lockName, ttl, callback) { +export function lock(lockName, ttl, callback) { const token = Math.random() .toString(36) .slice(2); @@ -159,11 +159,3 @@ function lock(lockName, ttl, callback) { } ); } - -exports.connect = connect; -exports.disconnect = disconnect; -exports.get = get; -exports.set = set; -exports.del = del; -exports.pop = pop; -exports.lock = lock; diff --git a/lib/cluster.js b/lib/cluster.js index 21d8325..63647f8 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -18,8 +18,9 @@ */ "use strict"; -const cluster = require("cluster"); -const logger = require("./logger"); +import cluster from "cluster"; +import { cpus } from "os"; +import * as logger from "./logger"; let respawnTimestamp = 0; let tooManyCrashesTimestamp = 0; @@ -62,7 +63,7 @@ function restartWorker(worker, code, signal) { }, respawnTimestamp - now); } -function start(workerCount, servicePort, serviceAddress) { +export function start(workerCount, servicePort, serviceAddress) { cluster.on("listening", (worker, address) => { if ( (address.addressType === 4 || address.addressType === 6) && @@ -80,16 +81,14 @@ function start(workerCount, servicePort, serviceAddress) { cluster.on("exit", restartWorker); - if (!workerCount) workerCount = Math.max(2, require("os").cpus().length); + if (!workerCount) workerCount = Math.max(2, cpus().length); for (let i = 0; i < workerCount; ++i) cluster.fork(); } -function stop() { +export function stop() { cluster.removeListener("exit", restartWorker); for (const pid in cluster.workers) cluster.workers[pid].kill(); } -exports.start = start; -exports.stop = stop; -exports.worker = cluster.worker; +export const worker = cluster.worker; diff --git a/lib/common.js b/lib/common.js index 15a3609..e94e145 100644 --- a/lib/common.js +++ b/lib/common.js @@ -18,19 +18,19 @@ */ "use strict"; -const UNDEFINED_TYPE = "[object Undefined]"; -const NULL_TYPE = "[object Null]"; -const BOOLEAN_TYPE = "[object Boolean]"; -const NUMBER_TYPE = "[object Number]"; -const STRING_TYPE = "[object String]"; -const ARRAY_TYPE = "[object Array]"; -const OBJECT_TYPE = "[object Object]"; -const REGEXP_TYPE = "[object RegExp]"; -const DATE_TYPE = "[object Date]"; +export const UNDEFINED_TYPE = "[object Undefined]"; +export const NULL_TYPE = "[object Null]"; +export const BOOLEAN_TYPE = "[object Boolean]"; +export const NUMBER_TYPE = "[object Number]"; +export const STRING_TYPE = "[object String]"; +export const ARRAY_TYPE = "[object Array]"; +export const OBJECT_TYPE = "[object Object]"; +export const REGEXP_TYPE = "[object RegExp]"; +export const DATE_TYPE = "[object Date]"; -const typeOf = obj => Object.prototype.toString.call(obj); +export const typeOf = obj => Object.prototype.toString.call(obj); -function generateDeviceId(deviceIdStruct) { +export function generateDeviceId(deviceIdStruct) { // Escapes everything except alphanumerics and underscore function esc(str) { return str.replace(/[^A-Za-z0-9_]/g, chr => { @@ -54,7 +54,7 @@ function generateDeviceId(deviceIdStruct) { return esc(deviceIdStruct["OUI"]) + "-" + esc(deviceIdStruct["SerialNumber"]); } -function parseAlias(pattern, start, res) { +export function parseAlias(pattern, start, res) { const aliases = []; let i = start; while (i < pattern.length && pattern[i] !== "]") { @@ -126,7 +126,7 @@ function parseAlias(pattern, start, res) { return i; } -function parsePath(pattern, start, res) { +export function parsePath(pattern, start, res) { const path = []; path.wildcard = 0; path.alias = 0; @@ -163,7 +163,7 @@ function parsePath(pattern, start, res) { return i; } -function addPathMeta(path) { +export function addPathMeta(path) { if (path.alias != null && path.wildcard != null) return path; path.alias = 0; @@ -183,22 +183,6 @@ function addPathMeta(path) { } // Source: http://stackoverflow.com/a/6969486 -function escapeRegExp(str) { +export function escapeRegExp(str) { return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); } - -exports.UNDEFINED_TYPE = UNDEFINED_TYPE; -exports.NULL_TYPE = NULL_TYPE; -exports.BOOLEAN_TYPE = BOOLEAN_TYPE; -exports.NUMBER_TYPE = NUMBER_TYPE; -exports.STRING_TYPE = STRING_TYPE; -exports.ARRAY_TYPE = ARRAY_TYPE; -exports.OBJECT_TYPE = OBJECT_TYPE; -exports.DATE_TYPE = DATE_TYPE; -exports.REGEXP_TYPE = REGEXP_TYPE; - -exports.typeOf = typeOf; -exports.generateDeviceId = generateDeviceId; -exports.parsePath = parsePath; -exports.addPathMeta = addPathMeta; -exports.escapeRegExp = escapeRegExp; diff --git a/lib/common/authorizer.js b/lib/common/authorizer.js index 82bb3b1..b12f64f 100644 --- a/lib/common/authorizer.js +++ b/lib/common/authorizer.js @@ -1,11 +1,11 @@ "use strict"; -const validators = { - pass: require("./validators/pass"), - test: require("./validators/test") -}; +import pass from "./validators/pass"; +import test from "./validators/test"; -class Authorizer { +const validators = { pass, test }; + +export default class Authorizer { constructor(permissionSets) { this.permissionSets = permissionSets; this.validatorCache = new WeakMap(); @@ -77,5 +77,3 @@ class Authorizer { return validator; } } - -module.exports = Authorizer; diff --git a/lib/common/expression-cnf.js b/lib/common/expression-cnf.js index 51960a4..a38b483 100644 --- a/lib/common/expression-cnf.js +++ b/lib/common/expression-cnf.js @@ -1,6 +1,6 @@ "use strict"; -const expressionParser = require("./expression-parser"); +import { map, parseLikePattern } from "./expression-parser"; function or(cnf1, cnf2) { if (cnf1.length === 0) return cnf2; @@ -19,10 +19,10 @@ function or(cnf1, cnf2) { return res; } -function booleanCnf(exp) { +export function booleanCnf(exp) { const ranges = new Map(); const likePatterns = new Map(); - expressionParser.map(exp, e => { + map(exp, e => { if (!Array.isArray(e)) return e; if (e[0] === "LIKE" || e[0] === "NOT LIKE") { if ( @@ -32,10 +32,7 @@ function booleanCnf(exp) { const key = JSON.stringify(e[1]); let s = likePatterns.get(key); if (!s) likePatterns.set(key, (s = new Map())); - s.set( - `${e[3] || ""}:${e[2]}`, - expressionParser.parseLikePattern(e[2], e[3]) - ); + s.set(`${e[3] || ""}:${e[2]}`, parseLikePattern(e[2], e[3])); } } else if ([">", ">=", "<", "<=", "=", "<>"].includes(e[0])) { if (!Array.isArray(e[2])) { @@ -251,5 +248,3 @@ function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0) { if (idx1 === pat1.length && idx2 === pat2.length) return true; return false; } - -exports.booleanCnf = booleanCnf; diff --git a/lib/common/expression-parser.js b/lib/common/expression-parser.js index f9b5490..964d9d0 100644 --- a/lib/common/expression-parser.js +++ b/lib/common/expression-parser.js @@ -1,6 +1,6 @@ "use strict"; -const parsimmon = require("parsimmon"); +import parsimmon from "parsimmon"; // Turn escaped characters into real ones (e.g. "\\n" becomes "\n"). function interpretEscapes(str) { @@ -22,7 +22,7 @@ function interpretEscapes(str) { }); } -function map(exp, callback) { +export function map(exp, callback) { if (!Array.isArray(exp)) return callback(exp); let clone; @@ -271,12 +271,12 @@ const lang = parsimmon.createLanguage({ } }); -function parse(str) { +export function parse(str) { if (!str) return null; return lang.Expression.tryParse(str); } -function stringify(exp, level = 0) { +export function stringify(exp, level = 0) { if (!Array.isArray(exp)) return JSON.stringify(exp); const opLevels = { @@ -360,7 +360,7 @@ function stringify(exp, level = 0) { } } -function parseLikePattern(pat, esc) { +export function parseLikePattern(pat, esc) { const chars = pat.split(""); for (let i = 0; i < chars.length; ++i) { @@ -377,8 +377,3 @@ function parseLikePattern(pat, esc) { } return chars.filter(c => c); } - -exports.parse = parse; -exports.stringify = stringify; -exports.map = map; -exports.parseLikePattern = parseLikePattern; diff --git a/lib/common/expression.js b/lib/common/expression.js index 699e581..df0fc29 100644 --- a/lib/common/expression.js +++ b/lib/common/expression.js @@ -1,8 +1,8 @@ "use strict"; -const expressionParser = require("./expression-parser"); -const expressionCnf = require("./expression-cnf"); -const satSolver = require("./sat-solver"); +import { map, parseLikePattern } from "./expression-parser"; +import { booleanCnf } from "./expression-cnf"; +import { naiveDpll } from "./sat-solver"; const isArray = Array.isArray; @@ -26,7 +26,7 @@ function reduce(exp, callback) { return exp; } -function evaluate(exp, obj, now, cb) { +export function evaluate(exp, obj, now, cb) { function getRegExp(pat, esc) { const k = `${esc || ""}:${pat}`; let c = regExpCache.get(exp); @@ -35,7 +35,7 @@ function evaluate(exp, obj, now, cb) { return c[k]; } - return expressionParser.map(exp, e => { + return map(exp, e => { if (cb) e = cb(e); if (!isArray(e)) return e; @@ -162,7 +162,7 @@ function evaluate(exp, obj, now, cb) { }); } -function and(exp1, exp2) { +export function and(exp1, exp2) { if (!isArray(exp1)) return exp1 ? exp2 : exp1; if (!isArray(exp2)) return exp2 ? exp1 : exp2; @@ -177,7 +177,7 @@ function and(exp1, exp2) { return res; } -function or(exp1, exp2) { +export function or(exp1, exp2) { if (!isArray(exp1)) return exp1 ? exp1 : exp2; if (!isArray(exp2)) return exp2 ? exp2 : exp1; @@ -192,19 +192,19 @@ function or(exp1, exp2) { return res; } -function not(exp) { +export function not(exp) { if (isArray(exp) && exp[0] === "NOT") return exp[1]; return ["NOT", exp]; } -function subset(exp1, exp2) { +export function subset(exp1, exp2) { const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]); if (!isArray(e)) return !e; - const { vars, clauses } = expressionCnf.booleanCnf(e); - return !satSolver.naiveDpll(clauses, vars); + const { vars, clauses } = booleanCnf(e); + return !naiveDpll(clauses, vars); } -function likePatternToRegExp(pat, esc = "", flags = "") { +export function likePatternToRegExp(pat, esc = "", flags = "") { const convChars = { "-": "\\-", "/": "\\/", @@ -225,7 +225,7 @@ function likePatternToRegExp(pat, esc = "", flags = "") { "\\%": ".*", "\\_": "." }; - let chars = expressionParser.parseLikePattern(pat, esc); + let chars = parseLikePattern(pat, esc); if (!chars.length) return new RegExp("^$", flags); chars = chars.map(c => convChars[c] || c); chars[0] = chars[0] === ".*" ? "" : "^" + chars[0]; @@ -234,21 +234,11 @@ function likePatternToRegExp(pat, esc = "", flags = "") { return new RegExp(chars.join(""), flags); } -function extractParams(exp) { +export function extractParams(exp) { const params = new Set(); - expressionParser.map(exp, e => { + map(exp, e => { if (isArray(e) && e[0] === "PARAM") params.add(e[1]); return e; }); return Array.from(params); } - -exports.evaluate = evaluate; -exports.and = and; -exports.or = or; -exports.not = not; -exports.subset = subset; -exports.parse = expressionParser.parse; -exports.stringify = expressionParser.stringify; -exports.likePatternToRegExp = likePatternToRegExp; -exports.extractParams = extractParams; diff --git a/lib/common/memoize.js b/lib/common/memoize.js index 9a13007..20c0f7e 100644 --- a/lib/common/memoize.js +++ b/lib/common/memoize.js @@ -22,7 +22,7 @@ function getKey(obj) { return k; } -function memoize(func) { +export default function memoize(func) { return (...args) => { const key = JSON.stringify(args.map(getKey)) + getKey(func); @@ -43,5 +43,3 @@ const interval = setInterval(() => { // Don't hold Node.js process if (interval.unref) interval.unref(); - -module.exports = memoize; diff --git a/lib/common/sat-solver.js b/lib/common/sat-solver.js index 4800345..a18ea29 100644 --- a/lib/common/sat-solver.js +++ b/lib/common/sat-solver.js @@ -167,10 +167,8 @@ function unitPropagate(clauses, varVals) { return derivedVars; } -function naiveDpll(clauses, maxvarnr) { +export function naiveDpll(clauses, maxvarnr) { // variable values are 0 if not set, 1 if positive, -1 if negative const varVals = new Int32Array(maxvarnr + 1); return satisfiableAt(clauses, varVals, 0, 0); } - -exports.naiveDpll = naiveDpll; diff --git a/lib/common/validators/pass.js b/lib/common/validators/pass.js index e01267a..5bc45c2 100644 --- a/lib/common/validators/pass.js +++ b/lib/common/validators/pass.js @@ -1,5 +1,3 @@ -function pass() { +export default function pass() { return 1; } - -module.exports = pass; diff --git a/lib/common/validators/test.js b/lib/common/validators/test.js index 6795cbc..7467371 100644 --- a/lib/common/validators/test.js +++ b/lib/common/validators/test.js @@ -1,4 +1,4 @@ -function test(resourceType, resource) { +export default function test(resourceType, resource) { if (resourceType === "devices") { if (resource["Tags.test"] && resource["Tags.test"].value[0]) return 1; } else if (resourceType === "presets") { @@ -10,5 +10,3 @@ function test(resourceType, resource) { } return -1; } - -module.exports = test; diff --git a/lib/config.js b/lib/config.js index 61768e0..c5fa3e2 100644 --- a/lib/config.js +++ b/lib/config.js @@ -18,8 +18,8 @@ */ "use strict"; -const path = require("path"); -const fs = require("fs"); +import { resolve } from "path"; +import { readFileSync } from "fs"; const options = { CONFIG_DIR: { type: "path", default: "config" }, @@ -133,7 +133,7 @@ function setConfig(name, value, commandLineArgument) { return String(val); case "path": if (!val) return ""; - return path.resolve(val); + return resolve(val); default: return null; } @@ -182,7 +182,7 @@ setConfig("CONFIG_DIR", options["CONFIG_DIR"]["default"]); // Configuration file const configFile = JSON.parse( - fs.readFileSync(path.resolve(allConfig.CONFIG_DIR, "config.json")) + readFileSync(resolve(allConfig.CONFIG_DIR, "config.json")) ); for (const [k, v] of Object.entries(configFile)) { if (!setConfig(k, v)) @@ -194,7 +194,7 @@ for (const [k, v] of Object.entries(configFile)) { for (const [k, v] of Object.entries(options)) if (v["default"] != null) setConfig(k, v["default"]); -function get(optionName, deviceId) { +export function get(optionName, deviceId) { if (!deviceId) return allConfig[optionName]; optionName = `${optionName}-${deviceId}`; @@ -222,28 +222,20 @@ function get(optionName, deviceId) { return null; } -function getDefault(optionName) { +export function getDefault(optionName) { const option = options[optionName]; if (!option) return null; let val = option["default"]; - if (val && option.type === "path") val = path.resolve(val); + if (val && option.type === "path") val = resolve(val); return val; } +export let auth; // Load authentication scripts try { - const authScript = fs.readFileSync( - path.resolve(allConfig.CONFIG_DIR, "auth.js") - ); - exports.auth = (function(exports) { - eval(authScript.toString()); - return exports; - })({}); + auth = require(resolve(allConfig.CONFIG_DIR, "auth.js")); } catch (error) { // No auth.js exists } - -exports.get = get; -exports.getDefault = getDefault; diff --git a/lib/cwmp.js b/lib/cwmp.js index 2f9fb14..b92272f 100644 --- a/lib/cwmp.js +++ b/lib/cwmp.js @@ -18,20 +18,20 @@ */ "use strict"; -const zlib = require("zlib"); -const crypto = require("crypto"); -const fs = require("fs"); -const config = require("./config"); -const common = require("./common"); -const soap = require("./soap"); -const session = require("./session"); -const query = require("./query"); -const device = require("./device"); -const cache = require("./cache"); -const localCache = require("./local-cache"); -const db = require("./db"); -const logger = require("./logger"); -const scheduling = require("./scheduling"); +import zlib from "zlib"; +import crypto from "crypto"; +import fs from "fs"; +import * as config from "./config"; +import * as common from "./common"; +import * as soap from "./soap"; +import * as session from "./session"; +import * as query from "./query"; +import * as device from "./device"; +import * as cache from "./cache"; +import * as localCache from "./local-cache"; +import * as db from "./db"; +import * as logger from "./logger"; +import * as scheduling from "./scheduling"; const MAX_CYCLES = 4; const MAX_CONCURRENT_REQUESTS = config.get("MAX_CONCURRENT_REQUESTS"); @@ -771,7 +771,7 @@ function getSession(connection, sessionId, callback) { const currentSessions = new WeakMap(); // When socket closes, store active sessions in cache -function onConnection(socket) { +export function onConnection(socket) { // The property remoteAddress may be undefined after the connection is // closed, unless we read it at least once (caching?) socket.remoteAddress; @@ -996,7 +996,7 @@ function processRequest(sessionContext, rpc) { } } -function listener(httpRequest, httpResponse) { +export function listener(httpRequest, httpResponse) { stats.totalRequests += 1; if (httpRequest.method !== "POST") { @@ -1243,6 +1243,3 @@ function listener(httpRequest, httpResponse) { }); }); } - -exports.listener = listener; -exports.onConnection = onConnection; diff --git a/lib/db.js b/lib/db.js index ff38aa2..9b3c16c 100644 --- a/lib/db.js +++ b/lib/db.js @@ -18,12 +18,12 @@ */ "use strict"; -const mongodb = require("mongodb"); -const config = require("./config"); -const common = require("./common"); -const expressionParser = require("./common/expression-parser"); +import { MongoClient, ObjectID } from "mongodb"; +import { get } from "./config"; +import { escapeRegExp } from "./common"; +import { parse } from "./common/expression-parser"; -let tasksCollection, +export let tasksCollection, devicesCollection, presetsCollection, objectsCollection, @@ -34,32 +34,28 @@ let tasksCollection, operationsCollection, configCollection; -function connect(callback) { - mongodb.MongoClient.connect( - config.get("MONGODB_CONNECTION_URL"), +export let client; + +export function connect(callback) { + MongoClient.connect( + get("MONGODB_CONNECTION_URL"), { useNewUrlParser: true }, - (err, client) => { + (err, _client) => { if (err) return void callback(err); - exports.client = client; + client = _client; const db = client.db(); - exports.tasksCollection = tasksCollection = db.collection("tasks"); + tasksCollection = db.collection("tasks"); tasksCollection.createIndex({ device: 1, timestamp: 1 }); - exports.devicesCollection = devicesCollection = db.collection("devices"); - exports.presetsCollection = presetsCollection = db.collection("presets"); - exports.objectsCollection = objectsCollection = db.collection("objects"); - exports.filesCollection = filesCollection = db.collection("fs.files"); - exports.provisionsCollection = provisionsCollection = db.collection( - "provisions" - ); - exports.virtualParametersCollection = virtualParametersCollection = db.collection( - "virtualParameters" - ); - exports.faultsCollection = faultsCollection = db.collection("faults"); - exports.operationsCollection = operationsCollection = db.collection( - "operations" - ); + devicesCollection = db.collection("devices"); + presetsCollection = db.collection("presets"); + objectsCollection = db.collection("objects"); + filesCollection = db.collection("fs.files"); + provisionsCollection = db.collection("provisions"); + virtualParametersCollection = db.collection("virtualParameters"); + faultsCollection = db.collection("faults"); + operationsCollection = db.collection("operations"); configCollection = db.collection("config"); callback(); @@ -67,8 +63,8 @@ function connect(callback) { ); } -function disconnect() { - if (exports.client) exports.client.close(); +export function disconnect() { + if (client) client.close(); } // Optimize projection by removing overlaps @@ -92,8 +88,8 @@ function optimizeProjection(obj) { return obj; } -function fetchDevice(id, timestamp, patterns, callback) { - const MAX_DEPTH = config.get("MAX_DEPTH", id); +export function fetchDevice(id, timestamp, patterns, callback) { + const MAX_DEPTH = get("MAX_DEPTH", id); if (!patterns || !patterns.length) patterns = [[[], (1 << MAX_DEPTH) - 1]]; @@ -476,7 +472,13 @@ function fetchDevice(id, timestamp, patterns, callback) { }); } -function saveDevice(deviceId, deviceData, isNew, sessionTimestamp, callback) { +export function saveDevice( + deviceId, + deviceData, + isNew, + sessionTimestamp, + callback +) { const update = { $set: {}, $unset: {}, $addToSet: {}, $pull: {} }; for (const diff of deviceData.timestamps.diff()) { @@ -724,9 +726,9 @@ function saveDevice(deviceId, deviceData, isNew, sessionTimestamp, callback) { ); } -function getFaults(deviceId, callback) { +export function getFaults(deviceId, callback) { faultsCollection - .find({ _id: { $regex: `^${common.escapeRegExp(deviceId)}\\:` } }) + .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } }) .toArray((err, res) => { if (err) return callback(err); @@ -745,7 +747,7 @@ function getFaults(deviceId, callback) { }); } -function saveFault(deviceId, channel, fault, callback) { +export function saveFault(deviceId, channel, fault, callback) { const id = `${deviceId}:${channel}`; fault = Object.assign({}, fault); fault._id = id; @@ -756,11 +758,11 @@ function saveFault(deviceId, channel, fault, callback) { faultsCollection.replaceOne({ _id: id }, fault, { upsert: true }, callback); } -function deleteFault(deviceId, channel, callback) { +export function deleteFault(deviceId, channel, callback) { faultsCollection.deleteOne({ _id: `${deviceId}:${channel}` }, callback); } -function getDueTasks(deviceId, timestamp, callback) { +export function getDueTasks(deviceId, timestamp, callback) { const cur = tasksCollection.find({ device: deviceId }).sort(["timestamp"]); const tasks = []; @@ -782,8 +784,8 @@ function getDueTasks(deviceId, timestamp, callback) { // For API compatibility if (task.name === "download" && task.file) { let q; - if (mongodb.ObjectID.isValid(task.file)) - q = { _id: { $in: [task.file, new mongodb.ObjectID(task.file)] } }; + if (ObjectID.isValid(task.file)) + q = { _id: { $in: [task.file, new ObjectID(task.file)] } }; else q = { _id: task.file }; filesCollection.find(q).toArray((err, res) => { @@ -805,16 +807,16 @@ function getDueTasks(deviceId, timestamp, callback) { ); } -function clearTasks(deviceId, taskIds, callback) { +export function clearTasks(deviceId, taskIds, callback) { tasksCollection.deleteMany( - { _id: { $in: taskIds.map(id => new mongodb.ObjectID(id)) } }, + { _id: { $in: taskIds.map(id => new ObjectID(id)) } }, callback ); } -function getOperations(deviceId, callback) { +export function getOperations(deviceId, callback) { operationsCollection - .find({ _id: { $regex: `^${common.escapeRegExp(deviceId)}\\:` } }) + .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } }) .toArray((err, res) => { if (err) return void callback(err); @@ -833,7 +835,7 @@ function getOperations(deviceId, callback) { }); } -function saveOperation(deviceId, commandKey, operation, callback) { +export function saveOperation(deviceId, commandKey, operation, callback) { const id = `${deviceId}:${commandKey}`; operation = Object.assign({}, operation); operation._id = id; @@ -849,34 +851,34 @@ function saveOperation(deviceId, commandKey, operation, callback) { ); } -function deleteOperation(deviceId, commandKey, callback) { +export function deleteOperation(deviceId, commandKey, callback) { operationsCollection.deleteOne( { _id: `${deviceId}:${commandKey}` }, callback ); } -function getPresets(callback) { +export function getPresets(callback) { presetsCollection.find().toArray(callback); } -function getObjects(callback) { +export function getObjects(callback) { objectsCollection.find().toArray(callback); } -function getProvisions(callback) { +export function getProvisions(callback) { provisionsCollection.find().toArray(callback); } -function getVirtualParameters(callback) { +export function getVirtualParameters(callback) { virtualParametersCollection.find().toArray(callback); } -function getFiles(callback) { +export function getFiles(callback) { filesCollection.find().toArray(callback); } -function getConfig(callback) { +export function getConfig(callback) { configCollection.find().toArray((err, res) => { if (err) return void callback(err); @@ -884,27 +886,8 @@ function getConfig(callback) { null, res.map(c => ({ id: c["_id"], - value: expressionParser.parse(c["value"]) + value: parse(c["value"]) })) ); }); } - -exports.connect = connect; -exports.disconnect = disconnect; -exports.fetchDevice = fetchDevice; -exports.saveDevice = saveDevice; -exports.getFaults = getFaults; -exports.saveFault = saveFault; -exports.deleteFault = deleteFault; -exports.clearTasks = clearTasks; -exports.saveOperation = saveOperation; -exports.deleteOperation = deleteOperation; -exports.getPresets = getPresets; -exports.getObjects = getObjects; -exports.getProvisions = getProvisions; -exports.getVirtualParameters = getVirtualParameters; -exports.getFiles = getFiles; -exports.getDueTasks = getDueTasks; -exports.getOperations = getOperations; -exports.getConfig = getConfig; diff --git a/lib/default-provisions.js b/lib/default-provisions.js index 362be69..1c7f5ef 100644 --- a/lib/default-provisions.js +++ b/lib/default-provisions.js @@ -18,14 +18,14 @@ */ "use strict"; -const config = require("./config"); -const common = require("./common"); -const device = require("./device"); -const scheduling = require("./scheduling"); +import * as config from "./config"; +import * as common from "./common"; +import * as device from "./device"; +import * as scheduling from "./scheduling"; const MAX_DEPTH = config.get("MAX_DEPTH"); -function refresh(sessionContext, provision, declarations) { +export function refresh(sessionContext, provision, declarations) { const path = common.parsePath(provision[1]).slice(); const l = path.length; path.length = MAX_DEPTH; @@ -48,7 +48,7 @@ function refresh(sessionContext, provision, declarations) { return true; } -function value(sessionContext, provision, declarations) { +export function value(sessionContext, provision, declarations) { const path = common.parsePath(provision[1]); declarations.push({ path: path, @@ -62,7 +62,7 @@ function value(sessionContext, provision, declarations) { return true; } -function tag(sessionContext, provision, declarations) { +export function tag(sessionContext, provision, declarations) { const path = ["Tags", provision[1]]; declarations.push({ @@ -77,7 +77,7 @@ function tag(sessionContext, provision, declarations) { return true; } -function reboot(sessionContext, provision, declarations) { +export function reboot(sessionContext, provision, declarations) { declarations.push({ path: ["Reboot"], pathGet: 1, @@ -90,7 +90,7 @@ function reboot(sessionContext, provision, declarations) { return true; } -function reset(sessionContext, provision, declarations) { +export function reset(sessionContext, provision, declarations) { declarations.push({ path: ["FactoryReset"], pathGet: 1, @@ -103,7 +103,7 @@ function reset(sessionContext, provision, declarations) { return true; } -function download(sessionContext, provision, declarations) { +export function download(sessionContext, provision, declarations) { const alias = [ ["FileType"], provision[1] || "", @@ -134,7 +134,7 @@ function download(sessionContext, provision, declarations) { return true; } -function instances( +export function instances( sessionContext, provision, declarations, @@ -178,11 +178,3 @@ function instances( return true; } - -exports.refresh = refresh; -exports.value = value; -exports.tag = tag; -exports.reboot = reboot; -exports.reset = reset; -exports.download = download; -exports.instances = instances; diff --git a/lib/device.js b/lib/device.js index 1f7f562..f19c386 100644 --- a/lib/device.js +++ b/lib/device.js @@ -32,7 +32,7 @@ function parseBool(v) { else return null; } -function sanitizeParameterValue(parameterValue) { +export function sanitizeParameterValue(parameterValue) { if (parameterValue[0] != null) { switch (parameterValue[1]) { case "xsd:boolean": @@ -83,8 +83,8 @@ function sanitizeParameterValue(parameterValue) { return parameterValue; } -function getAliasDeclarations(path, timestamp) { - var decs = []; +export function getAliasDeclarations(path, timestamp) { + const decs = []; function recursive(p, prefix) { const pattern = prefix.concat(p); @@ -113,7 +113,7 @@ function getAliasDeclarations(path, timestamp) { return decs; } -function unpack(deviceData, path, revision) { +export function unpack(deviceData, path, revision) { let allMatches = []; if (!path.alias) { for (const p of deviceData.paths.find(path, false, true)) @@ -194,7 +194,13 @@ function unpack(deviceData, path, revision) { return allMatches; } -function clear(deviceData, path, timestamp, attributes, changeFlags = 0) { +export function clear( + deviceData, + path, + timestamp, + attributes, + changeFlags = 0 +) { const changeTrackers = {}; timestamp = timestamp || 0; @@ -254,7 +260,7 @@ function clear(deviceData, path, timestamp, attributes, changeFlags = 0) { } function compareEquality(a, b) { - var t = typeof a; + const t = typeof a; if ( a === null || a === undefined || @@ -268,7 +274,7 @@ function compareEquality(a, b) { return JSON.stringify(a) === JSON.stringify(b); } -function set(deviceData, path, timestamp, attributes, toClear) { +export function set(deviceData, path, timestamp, attributes, toClear) { path = deviceData.paths.add(path); const currentTimestamp = deviceData.timestamps.get(path); @@ -366,7 +372,7 @@ function set(deviceData, path, timestamp, attributes, toClear) { return toClear; } -function track(deviceData, path, marker, attributes) { +export function track(deviceData, path, marker, attributes) { path = deviceData.paths.add(path); let f = 1; @@ -382,7 +388,7 @@ function track(deviceData, path, marker, attributes) { cur[marker] |= f; } -function clearTrackers(deviceData, tracker) { +export function clearTrackers(deviceData, tracker) { if (Array.isArray(tracker)) { for (const v of deviceData.trackers.values()) for (const t of tracker) delete v[t]; @@ -392,11 +398,3 @@ function clearTrackers(deviceData, tracker) { deviceData.changes.delete(tracker); } } - -exports.sanitizeParameterValue = sanitizeParameterValue; -exports.getAliasDeclarations = getAliasDeclarations; -exports.unpack = unpack; -exports.set = set; -exports.clear = clear; -exports.track = track; -exports.clearTrackers = clearTrackers; diff --git a/lib/extensions.js b/lib/extensions.js index 39b1cf2..f1d3e83 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -18,10 +18,9 @@ */ "use strict"; -const childProcess = require("child_process"); -const crypto = require("crypto"); - -const config = require("./config"); +import { fork } from "child_process"; +import crypto from "crypto"; +import * as config from "./config"; const TIMEOUT = config.get("EXT_TIMEOUT"); @@ -36,11 +35,11 @@ function messageHandler(message) { } } -function run(args, callback) { +export function run(args, callback) { const scriptName = args[0]; if (!processes[scriptName]) { - const p = childProcess.fork("lib/extension-wrapper", [scriptName]); + const p = fork("lib/extension-wrapper", [scriptName]); processes[scriptName] = p; p.on("error", () => { @@ -91,7 +90,7 @@ function kill(process, callback) { }, 100); } -function killAll(callback) { +export function killAll(callback) { let counter = 1; for (const n in processes) { ++counter; @@ -102,6 +101,3 @@ function killAll(callback) { } if (--counter === 0 && callback) callback(); } - -exports.run = run; -exports.killAll = killAll; diff --git a/lib/fs.js b/lib/fs.js index 5d19cd7..9824a82 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -18,12 +18,12 @@ */ "use strict"; -const url = require("url"); -const querystring = require("querystring"); -const mongodb = require("mongodb"); -const db = require("./db"); +import url from "url"; +import querystring from "querystring"; +import { GridFSBucket } from "mongodb"; +import * as db from "./db"; -function listener(request, response) { +export function listener(request, response) { const urlParts = url.parse(request.url, true); if (request.method === "GET") { const filename = querystring.unescape(urlParts.pathname.substring(1)); @@ -45,7 +45,7 @@ function listener(request, response) { "Content-Length": file.length }); - const bucket = new mongodb.GridFSBucket(db.client.db()); + const bucket = new GridFSBucket(db.client.db()); const downloadStream = bucket.openDownloadStream(filename); downloadStream.pipe(response); }); @@ -54,5 +54,3 @@ function listener(request, response) { response.end("405 Method Not Allowed"); } } - -exports.listener = listener; diff --git a/lib/gpn-heuristic.js b/lib/gpn-heuristic.js index 83c26ed..ef985cf 100644 --- a/lib/gpn-heuristic.js +++ b/lib/gpn-heuristic.js @@ -28,7 +28,7 @@ const UNDISCOVERED_DEPTH = 7; // flags is an int where its bits mark the segments in the pattern that // need refreshing. Leading 0s indicate that the pattern up to that // point has been discovered. -function estimateGpnCount(gpnPatterns, depth = 0) { +export function estimateGpnCount(gpnPatterns, depth = 0) { const children = {}; const wildcardChildren = []; let wildcardDiscovered = false; @@ -77,5 +77,3 @@ function estimateGpnCount(gpnPatterns, depth = 0) { return gpnCount; } - -exports.estimateGpnCount = estimateGpnCount; diff --git a/lib/instance-set.js b/lib/instance-set.js index f006b37..4aa2d20 100644 --- a/lib/instance-set.js +++ b/lib/instance-set.js @@ -18,7 +18,7 @@ */ "use strict"; -class InstanceSet { +export default class InstanceSet { constructor() { this.set = new Set(); } @@ -124,5 +124,3 @@ class InstanceSet { return this.set.size; } } - -module.exports = InstanceSet; diff --git a/lib/local-cache.js b/lib/local-cache.js index dd2ce54..4876d54 100644 --- a/lib/local-cache.js +++ b/lib/local-cache.js @@ -18,16 +18,16 @@ */ "use strict"; -const vm = require("vm"); -const crypto = require("crypto"); +import vm from "vm"; +import crypto from "crypto"; -const config = require("./config"); -const db = require("./db"); -const cache = require("./cache"); -const query = require("./query"); -const logger = require("./logger"); -const scheduling = require("./scheduling"); -const expression = require("./common/expression"); +import * as config from "./config"; +import * as db from "./db"; +import * as cache from "./cache"; +import * as query from "./query"; +import * as logger from "./logger"; +import * as scheduling from "./scheduling"; +import * as expression from "./common/expression"; const REFRESH = 3000; const EVICT_TIMEOUT = 60000; @@ -369,7 +369,7 @@ function refresh(callback) { }); } -function getCurrentSnapshot(callback) { +export function getCurrentSnapshot(callback) { if (Date.now() < nextRefresh) { if (callback) return callback(null, currentSnapshot); return Promise.resolve(currentSnapshot); @@ -389,35 +389,35 @@ function getCurrentSnapshot(callback) { }); } -function hasSnapshot(hash) { +export function hasSnapshot(hash) { return snapshots.has(hash); } -function getPresets(snapshotKey) { +export function getPresets(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.presets; } -function getProvisions(snapshotKey) { +export function getProvisions(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.provisions; } -function getVirtualParameters(snapshotKey) { +export function getVirtualParameters(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.virtualParameters; } -function getFiles(snapshotKey) { +export function getFiles(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.files; } -function getConfig(snapshotKey, key, context, now) { +export function getConfig(snapshotKey, key, context, now) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); @@ -445,34 +445,23 @@ function getConfig(snapshotKey, key, context, now) { return expression.evaluate(snapshot.config[key], context, now || Date.now()); } -function getAuthConfig(snapshotKey) { +export function getAuthConfig(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.config.auth; } -function getPermissionsConfig(snapshotKey) { +export function getPermissionsConfig(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.config.permissions; } -function getUiConfig(snapshotKey) { +export function getUiConfig(snapshotKey) { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.config.ui; } - -exports.getCurrentSnapshot = getCurrentSnapshot; -exports.hasSnapshot = hasSnapshot; -exports.getPresets = getPresets; -exports.getProvisions = getProvisions; -exports.getVirtualParameters = getVirtualParameters; -exports.getFiles = getFiles; -exports.getConfig = getConfig; -exports.getAuthConfig = getAuthConfig; -exports.getPermissionsConfig = getPermissionsConfig; -exports.getUiConfig = getUiConfig; diff --git a/lib/logger.js b/lib/logger.js index dd81c4a..9bf66a0 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -18,10 +18,10 @@ */ "use strict"; -const fs = require("fs"); -const os = require("os"); +import fs from "fs"; +import os from "os"; -const config = require("./config"); +import * as config from "./config"; const REOPEN_EVERY = 60000; @@ -90,7 +90,7 @@ function reopen() { setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref(); } -function init(service, version) { +export function init(service, version) { defaultMeta.hostname = os.hostname(); defaultMeta.pid = process.pid; defaultMeta.name = `genieacs-${service}`; @@ -129,12 +129,12 @@ function init(service, version) { setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref(); } -function close() { +export function close() { accessLogStream.end(); logStream.end(); } -function flatten(details) { +export function flatten(details) { if (details.sessionContext) { details.deviceId = details.sessionContext.deviceId; details.remoteAddress = @@ -265,22 +265,22 @@ function log(details) { } } -function info(details) { +export function info(details) { details.severity = "info"; log(details); } -function warn(details) { +export function warn(details) { details.severity = "warn"; log(details); } -function error(details) { +export function error(details) { details.severity = "error"; log(details); } -function accessLog(details) { +export function accessLog(details) { details.timestamp = new Date().toISOString(); if (ACCESS_LOG_FORMAT === "json") { Object.assign(details, defaultMeta); @@ -290,27 +290,17 @@ function accessLog(details) { } } -function accessInfo(details) { +export function accessInfo(details) { details.severity = "info"; accessLog(details); } -function accessWarn(details) { +export function accessWarn(details) { details.severity = "warn"; accessLog(details); } -function accessError(details) { +export function accessError(details) { details.severity = "error"; accessLog(details); } - -exports.init = init; -exports.close = close; -exports.flatten = flatten; -exports.info = info; -exports.warn = warn; -exports.error = error; -exports.accessInfo = accessInfo; -exports.accessWarn = accessWarn; -exports.accessError = accessError; diff --git a/lib/nbi.js b/lib/nbi.js index e63395a..cf76da0 100644 --- a/lib/nbi.js +++ b/lib/nbi.js @@ -18,18 +18,17 @@ */ "use strict"; -const url = require("url"); -const mongodb = require("mongodb"); -const querystring = require("querystring"); -const vm = require("vm"); -const childProcess = require("child_process"); -const config = require("./config"); -const db = require("./db"); -const query = require("./query"); -const apiFunctions = require("./api-functions"); -const cache = require("./cache"); - -const VERSION = require("../package.json").version; +import url from "url"; +import mongodb from "mongodb"; +import querystring from "querystring"; +import vm from "vm"; +import { exec } from "child_process"; +import * as config from "./config"; +import * as db from "./db"; +import * as query from "./query"; +import * as apiFunctions from "./api-functions"; +import * as cache from "./cache"; +import { version as VERSION } from "../package.json"; const DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/tasks\/?$/; const TASKS_REGEX = /^\/tasks\/([a-zA-Z0-9\-_%]+)(\/[a-zA-Z_]*)?$/; @@ -52,7 +51,7 @@ function throwError(err, httpResponse) { throw err; } -function listener(request, response) { +export function listener(request, response) { const chunks = []; let bytes = 0; response.setHeader("GenieACS-Version", VERSION); @@ -520,7 +519,7 @@ function listener(request, response) { } } else if (PING_REGEX.test(urlParts.pathname)) { const host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1]); - childProcess.exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => { + exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => { if (err) { response.writeHead(404, { "Cache-Control": "no-cache" }); response.end(`${err.name}: ${err.message}`); @@ -661,5 +660,3 @@ function listener(request, response) { } }); } - -exports.listener = listener; diff --git a/lib/path-set.js b/lib/path-set.js index aa773be..db92ffb 100644 --- a/lib/path-set.js +++ b/lib/path-set.js @@ -18,7 +18,7 @@ */ "use strict"; -class PathSet { +export default class PathSet { constructor() { this.lengthIndex = []; this.fragmentIndex = []; @@ -161,5 +161,3 @@ class PathSet { return res; } } - -module.exports = PathSet; diff --git a/lib/query.js b/lib/query.js index 366c7a3..0a742da 100644 --- a/lib/query.js +++ b/lib/query.js @@ -18,7 +18,7 @@ */ "use strict"; -const common = require("./common"); +import * as common from "./common"; function stringToRegexp(input, flags) { if (input.indexOf("*") === -1) return false; @@ -53,7 +53,7 @@ function normalize(input) { return input; } -function expandValue(value) { +export function expandValue(value) { if (common.typeOf(value) === common.ARRAY_TYPE) { let a = []; for (const j of value) a = a.conact(expandValue(j)); @@ -104,7 +104,7 @@ function permute(param, val) { return conditions; } -function expand(query) { +export function expand(query) { const newQuery = {}; for (const [k, v] of Object.entries(query)) { if (k[0] === "$") { @@ -157,7 +157,7 @@ function testExpressions(params, expressions, lop) { } } -function test(params, query) { +export function test(params, query) { let res; for (const [k, v] of Object.entries(query)) { if (k.charAt(0) === "$") { @@ -233,7 +233,7 @@ function matchType(src, dst) { return dst; } -function testFilter(obj, filter) { +export function testFilter(obj, filter) { for (const [k, v] of Object.entries(filter)) { const [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2); const val = matchType(obj[param], v); @@ -264,7 +264,7 @@ function testFilter(obj, filter) { return true; } -function convertMongoQueryToFilters(query, filters) { +export function convertMongoQueryToFilters(query, filters) { filters = filters || {}; for (let [k, v] of Object.entries(query)) { if (k[0] === "$") { @@ -346,7 +346,7 @@ function convertMongoQueryToFilters(query, filters) { return filters; } -function sanitizeQueryTypes(query, types) { +export function sanitizeQueryTypes(query, types) { for (const [k, v] of Object.entries(query)) { if (k[0] === "$") { // Logical operator @@ -383,9 +383,3 @@ function sanitizeQueryTypes(query, types) { return query; } - -exports.expand = expand; -exports.test = test; -exports.sanitizeQueryTypes = sanitizeQueryTypes; -exports.convertMongoQueryToFilters = convertMongoQueryToFilters; -exports.testFilter = testFilter; diff --git a/lib/sandbox.js b/lib/sandbox.js index fe796c5..086ae1d 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -18,15 +18,15 @@ */ "use strict"; -const vm = require("vm"); +import vm from "vm"; -const seedrandom = require("seedrandom"); +import seedrandom from "seedrandom"; -const common = require("./common"); -const device = require("./device"); -const extensions = require("./extensions"); -const logger = require("./logger"); -const scheduling = require("./scheduling"); +import * as common from "./common"; +import * as device from "./device"; +import * as extensions from "./extensions"; +import * as logger from "./logger"; +import * as scheduling from "./scheduling"; // Used for throwing to exit user script and commit const COMMIT = Symbol(); @@ -327,7 +327,7 @@ function errorToFault(err) { return fault; } -function run( +export function run( script, globals, sessionContext, @@ -416,5 +416,3 @@ function run( } } } - -exports.run = run; diff --git a/lib/scheduling.js b/lib/scheduling.js index 4329ac0..94b5052 100644 --- a/lib/scheduling.js +++ b/lib/scheduling.js @@ -18,9 +18,9 @@ */ "use strict"; -const crypto = require("crypto"); +import crypto from "crypto"; -const later = require("later"); +import later from "later"; function md532(str) { const digest = crypto @@ -35,22 +35,22 @@ function md532(str) { ); } -function variance(deviceId, vrnc) { +export function variance(deviceId, vrnc) { return (md532(deviceId) >>> 0) % vrnc; } -function interval(timestamp, intrvl, offset = 0) { +export function interval(timestamp, intrvl, offset = 0) { return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset; } -function parseCron(cronExp) { +export function parseCron(cronExp) { const parts = cronExp.trim().split(/\s+/); if (parts.length === 5) parts.unshift("*"); return later.schedule(later.parse.cron(parts.join(" "), true)); } -function cron(timestamp, schedule, offset = 0) { +export function cron(timestamp, schedule, offset = 0) { // TODO later.js doesn't throw erorr if expression is invalid! const ret = [0, 0]; @@ -62,8 +62,3 @@ function cron(timestamp, schedule, offset = 0) { return ret; } - -exports.variance = variance; -exports.interval = interval; -exports.parseCron = parseCron; -exports.cron = cron; diff --git a/lib/server.js b/lib/server.js index 07cb6cc..68ca96e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -18,9 +18,9 @@ */ "use strict"; -const fs = require("fs"); -const http = require("http"); -const https = require("https"); +import fs from "fs"; +import http from "http"; +import https from "https"; let server, listener; @@ -48,7 +48,7 @@ function closeServer(timeout, callback) { }); } -function start(port, networkInterface, ssl, _listener, onConnection) { +export function start(port, networkInterface, ssl, _listener, onConnection) { listener = _listener; if (ssl) { @@ -76,7 +76,7 @@ function start(port, networkInterface, ssl, _listener, onConnection) { server.listen(port, networkInterface); } -function stop() { +export function stop() { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("Could not close server in a timely manner")); @@ -84,6 +84,3 @@ function stop() { closeServer(20000, resolve); }); } - -exports.start = start; -exports.stop = stop; diff --git a/lib/session.js b/lib/session.js index 1d4fa38..ad98a76 100644 --- a/lib/session.js +++ b/lib/session.js @@ -18,17 +18,17 @@ */ "use strict"; -const config = require("./config"); -const common = require("./common"); -const db = require("./db"); -const device = require("./device"); -const sandbox = require("./sandbox"); -const localCache = require("./local-cache"); -const PathSet = require("./path-set"); -const VersionedMap = require("./versioned-map"); -const InstanceSet = require("./instance-set"); -const defaultProvisions = require("./default-provisions"); -const gpnHeuristic = require("./gpn-heuristic"); +import * as config from "./config"; +import * as common from "./common"; +import * as db from "./db"; +import * as device from "./device"; +import * as sandbox from "./sandbox"; +import * as localCache from "./local-cache"; +import * as defaultProvisions from "./default-provisions"; +import { estimateGpnCount } from "./gpn-heuristic"; +import PathSet from "./path-set"; +import VersionedMap from "./versioned-map"; +import InstanceSet from "./instance-set"; function initDeviceData() { return { @@ -41,7 +41,7 @@ function initDeviceData() { }; } -function init(deviceId, cwmpVersion, timeout, callback) { +export function init(deviceId, cwmpVersion, timeout, callback) { const timestamp = Date.now(); const sessionContext = { timestamp: timestamp, @@ -112,7 +112,7 @@ function generateRpcId(sessionContext) { ); } -function inform(sessionContext, rpcReq, callback) { +export function inform(sessionContext, rpcReq, callback) { const timestamp = sessionContext.timestamp + sessionContext.iteration + 1; const params = [ [ @@ -244,7 +244,7 @@ function inform(sessionContext, rpcReq, callback) { }); } -function transferComplete(sessionContext, rpcReq, callback) { +export function transferComplete(sessionContext, rpcReq, callback) { const revision = (sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1; sessionContext.deviceData.timestamps.revision = revision; @@ -410,7 +410,7 @@ function revertDownloadParameters(sessionContext, instance, callback) { }); } -function timeoutOperations(sessionContext, callback) { +export function timeoutOperations(sessionContext, callback) { const revision = (sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1; sessionContext.deviceData.timestamps.revision = revision; @@ -460,7 +460,7 @@ function timeoutOperations(sessionContext, callback) { if ((counter -= 2) === 1) callback(null, faults, operations); } -function addProvisions(sessionContext, channel, provisions) { +export function addProvisions(sessionContext, channel, provisions) { // Multiply by two because every iteration is two // phases: read and update const MAX_ITERATIONS = @@ -513,7 +513,7 @@ function addProvisions(sessionContext, channel, provisions) { } } -function clearProvisions(sessionContext) { +export function clearProvisions(sessionContext) { // Multiply by two because every iteration is two // phases: read and update const MAX_ITERATIONS = @@ -958,7 +958,7 @@ function runDeclarations(sessionContext, declarations) { ); } -function rpcRequest(sessionContext, _declarations, callback) { +export function rpcRequest(sessionContext, _declarations, callback) { if (sessionContext.rpcRequest != null) { return void callback( null, @@ -1560,7 +1560,7 @@ function generateGetRpcRequest(sessionContext) { const v = syncState.gpnPatterns.get(p); if (v) patterns.push([p, (v >> path.length) << path.length]); } - est = gpnHeuristic.estimateGpnCount(patterns); + est = estimateGpnCount(patterns); } if (est < Math.pow(2, Math.max(0, 8 - path.length))) { @@ -2250,7 +2250,7 @@ function clear(sessionContext, toClear, callback) { }); } -function rpcResponse(sessionContext, id, rpcRes, callback) { +export function rpcResponse(sessionContext, id, rpcRes, callback) { if (id !== generateRpcId(sessionContext)) return void callback(new Error("Request ID not recognized")); @@ -2583,7 +2583,7 @@ function rpcResponse(sessionContext, id, rpcRes, callback) { clear(sessionContext, toClear, callback); } -function rpcFault(sessionContext, id, faultResponse, callback) { +export function rpcFault(sessionContext, id, faultResponse, callback) { const rpcReq = sessionContext.rpcRequest; delete sessionContext.syncState; delete sessionContext.rpcRequest; @@ -2627,7 +2627,7 @@ function rpcFault(sessionContext, id, faultResponse, callback) { callback(null, fault); } -function deserialize(sessionContextString, callback) { +export function deserialize(sessionContextString, callback) { const sessionContext = JSON.parse(sessionContextString); for (const decs of sessionContext.declarations) @@ -2654,7 +2654,7 @@ function deserialize(sessionContextString, callback) { }); } -function serialize(sessionContext, callback) { +export function serialize(sessionContext, callback) { const deviceData = []; for (const path of sessionContext.deviceData.paths.find( @@ -2682,15 +2682,3 @@ function serialize(sessionContext, callback) { callback(null, sessionContextString); } - -exports.init = init; -exports.timeoutOperations = timeoutOperations; -exports.inform = inform; -exports.transferComplete = transferComplete; -exports.addProvisions = addProvisions; -exports.clearProvisions = clearProvisions; -exports.rpcRequest = rpcRequest; -exports.rpcResponse = rpcResponse; -exports.rpcFault = rpcFault; -exports.serialize = serialize; -exports.deserialize = deserialize; diff --git a/lib/soap.js b/lib/soap.js index 2c8b0a6..464775d 100644 --- a/lib/soap.js +++ b/lib/soap.js @@ -18,11 +18,11 @@ */ "use strict"; -const libxmljs = require("libxmljs"); +import libxmljs from "libxmljs"; +import * as config from "./config"; +import { version as VERSION } from "../package.json"; -const config = require("./config"); - -const SERVER_NAME = `GenieACS/${require("../package.json").version}`; +const SERVER_NAME = `GenieACS/${VERSION}`; const NAMESPACES = { "1.0": { @@ -518,7 +518,7 @@ function fault(xml) { return { faultCode, faultString, detail }; } -function request(data, cwmpVersion, warn) { +export function request(data, cwmpVersion, warn) { warnings = warn; const rpc = { @@ -642,7 +642,7 @@ function createSoapEnv(cwmpVersion) { return env; } -function response(rpc) { +export function response(rpc) { const headers = { Server: SERVER_NAME, SOAPServer: SERVER_NAME @@ -716,6 +716,3 @@ function response(rpc) { data: env.doc().toString(LIBXMLJS_SAVE_OPTIONS) }; } - -exports.request = request; -exports.response = response; diff --git a/lib/ui.js b/lib/ui.js index 2d65e18..c88ab6d 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -1,19 +1,19 @@ "use strict"; -const Koa = require("koa"); -const Router = require("koa-router"); -const koaStatic = require("koa-static"); -const koaCompress = require("koa-compress"); -const koaBodyParser = require("koa-bodyparser"); -const koaJwt = require("koa-jwt"); -const jwt = require("jsonwebtoken"); - -const config = require("./config"); -const api = require("./ui/api"); -const Authorizer = require("./common/authorizer"); -const expression = require("./common/expression"); -const logger = require("./logger"); -const localCache = require("./local-cache"); +import { Z_SYNC_FLUSH } from "zlib"; +import Koa from "koa"; +import Router from "koa-router"; +import * as jwt from "jsonwebtoken"; +import koaStatic from "koa-static"; +import koaCompress from "koa-compress"; +import koaBodyParser from "koa-bodyparser"; +import koaJwt from "koa-jwt"; +import * as config from "./config"; +import api from "./ui/api"; +import Authorizer from "./common/authorizer"; +import { parse } from "./common/expression-parser"; +import * as logger from "./logger"; +import * as localCache from "./local-cache"; const koa = new Koa(); const router = new Router(); @@ -41,7 +41,7 @@ function getPermissionSets(ctx) { ].includes(k) ) { if (v.filter == null || v.filter === "") v.filter = true; - else v.filter = expression.parse(v.filter); + else v.filter = parse(v.filter); } } return p; @@ -175,11 +175,11 @@ router.get("/", async ctx => { koa.use( koaCompress({ - flush: require("zlib").Z_SYNC_FLUSH + flush: Z_SYNC_FLUSH }) ); koa.use(router.routes()); koa.use(koaStatic("./public")); -exports.listener = koa.callback(); +export const listener = koa.callback(); diff --git a/lib/ui/api-functions.js b/lib/ui/api-functions.js index c98177f..a26da13 100644 --- a/lib/ui/api-functions.js +++ b/lib/ui/api-functions.js @@ -1,14 +1,13 @@ "use strict"; -const http = require("http"); -const https = require("https"); -const url = require("url"); +import http from "http"; +import https from "https"; +import url from "url"; +import * as config from "../config"; +import * as db from "./db"; +import * as mongodbFunctions from "./mongodb-functions"; -const config = require("../config"); -const db = require("./db"); -const mongodbFunctions = require("./mongodb-functions"); - -function filterToMongoQuery(filter, negate = false, res = {}) { +export function filterToMongoQuery(filter, negate = false, res = {}) { const op = filter[0]; if ((!negate && op === "AND") || (negate && op === "OR")) { @@ -66,7 +65,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) { return res; } -function deleteResource(resource, id) { +export function deleteResource(resource, id) { return new Promise((resolve, reject) => { const resourceUrl = resource === "virtualParameters" ? "virtual_parameters" : resource; @@ -142,7 +141,7 @@ function postTask(task, connectionRequest, callback) { .end(JSON.stringify(task)); } -function postTasks(deviceId, tasks) { +export function postTasks(deviceId, tasks) { return new Promise((resolve, reject) => { let connectionRequestStatus; const promises = []; @@ -206,7 +205,7 @@ function postTasks(deviceId, tasks) { }); } -function updateTags(deviceId, tags) { +export function updateTags(deviceId, tags) { return Promise.all( Object.entries(tags).map( ([tag, onOff]) => @@ -235,7 +234,7 @@ function updateTags(deviceId, tags) { ); } -function ping(host) { +export function ping(host) { return new Promise((resolve, reject) => { const options = url.parse( `http://127.0.0.1:${config.get("NBI_PORT")}/ping/${encodeURIComponent( @@ -295,7 +294,7 @@ function ping(host) { }); } -function putResource(resource, id, data) { +export function putResource(resource, id, data) { return new Promise((resolve, reject) => { const resourceUrl = resource === "virtualParameters" ? "virtual_parameters" : resource; @@ -348,7 +347,7 @@ function putResource(resource, id, data) { }); } -function putFile(id, metadata, fileStream) { +export function putFile(id, metadata, fileStream) { return new Promise((resolve, reject) => { const options = url.parse( `http://127.0.0.1:${config.get("NBI_PORT")}/files/${encodeURIComponent( @@ -372,11 +371,3 @@ function putFile(id, metadata, fileStream) { fileStream.pipe(req); }); } - -exports.postTasks = postTasks; -exports.deleteResource = deleteResource; -exports.putResource = putResource; -exports.updateTags = updateTags; -exports.filterToMongoQuery = filterToMongoQuery; -exports.ping = ping; -exports.putFile = putFile; diff --git a/lib/ui/api.js b/lib/ui/api.js index 01cd8c7..f209ac5 100644 --- a/lib/ui/api.js +++ b/lib/ui/api.js @@ -1,13 +1,15 @@ "use strict"; -const stream = require("stream"); -const Router = require("koa-router"); -const db = require("./db"); -const apiFunctions = require("./api-functions"); -const expression = require("../common/expression"); -const logger = require("../logger"); +import stream from "stream"; +import Router from "koa-router"; +import * as db from "./db"; +import * as apiFunctions from "./api-functions"; +import { evaluate, and, extractParams } from "../common/expression"; +import { parse } from "../common/expression-parser"; +import * as logger from "../logger"; const router = new Router(); +export default router; function logUnauthorizedWarning(log) { log.message += " not authorized"; @@ -87,8 +89,7 @@ router.get(`/devices/:id.csv`, async ctx => { for (const [resource, flags] of Object.entries(resources)) { router.head(`/${resource}`, async (ctx, next) => { let filter = true; - if (ctx.request.query.filter) - filter = expression.parse(ctx.request.query.filter); + if (ctx.request.query.filter) filter = parse(ctx.request.query.filter); const log = { message: `Count ${resource}`, @@ -103,7 +104,7 @@ for (const [resource, flags] of Object.entries(resources)) { // Exclude temporary tasks and faults if (resource === "tasks" || resource === "faults") { - filter = expression.and(filter, [ + filter = and(filter, [ "NOT", ["<", ["PARAM", "expiry"], Date.now() + 60000] ]); @@ -121,8 +122,7 @@ for (const [resource, flags] of Object.entries(resources)) { router.get(`/${resource}`, async (ctx, next) => { const options = {}; let filter = true; - if (ctx.request.query.filter) - filter = expression.parse(ctx.request.query.filter); + if (ctx.request.query.filter) filter = parse(ctx.request.query.filter); if (ctx.request.query.limit) options.limit = +ctx.request.query.limit; if (ctx.request.query.skip) options.skip = +ctx.request.query.skip; if (ctx.request.query.sort) @@ -150,7 +150,7 @@ for (const [resource, flags] of Object.entries(resources)) { // Exclude temporary tasks and faults if (resource === "tasks" || resource === "faults") { - filter = expression.and(filter, [ + filter = and(filter, [ "NOT", ["<", ["PARAM", "expiry"], Date.now() + 60000] ]); @@ -173,8 +173,7 @@ for (const [resource, flags] of Object.entries(resources)) { router.get(`/${resource}.csv`, async (ctx, next) => { const options = { projection: {} }; let filter = true; - if (ctx.request.query.filter) - filter = expression.parse(ctx.request.query.filter); + if (ctx.request.query.filter) filter = parse(ctx.request.query.filter); if (ctx.request.query.limit) options.limit = +ctx.request.query.limit; if (ctx.request.query.skip) options.skip = +ctx.request.query.skip; if (ctx.request.query.sort) @@ -198,14 +197,14 @@ for (const [resource, flags] of Object.entries(resources)) { const now = Date.now(); for (const [k, v] of Object.entries(columns)) { - const e = expression.evaluate(expression.parse(v), null, now); + const e = evaluate(parse(v), null, now); columns[k] = e; - for (const p of expression.extractParams(e)) options.projection[p] = 1; + for (const p of extractParams(e)) options.projection[p] = 1; } // Exclude temporary tasks and faults if (resource === "tasks" || resource === "faults") { - filter = expression.and(filter, [ + filter = and(filter, [ "NOT", ["<", ["PARAM", "expiry"], Date.now() + 60000] ]); @@ -222,7 +221,7 @@ for (const [resource, flags] of Object.entries(resources)) { ); await db.query(resource, filter, options, obj => { const arr = Object.values(columns).map(exp => { - const v = expression.evaluate(exp, obj, null, e => { + const v = evaluate(exp, obj, null, e => { if (Array.isArray(e)) { if (e[0] === "PARAM") { if (resource === "devices") { @@ -481,5 +480,3 @@ router.post("/devices/:id/tags", async (ctx, next) => { router.get("/ping/:host", async ctx => { ctx.body = await apiFunctions.ping(ctx.params.host); }); - -module.exports = router; diff --git a/lib/ui/db.js b/lib/ui/db.js index bae8d00..075e1d9 100644 --- a/lib/ui/db.js +++ b/lib/ui/db.js @@ -1,9 +1,9 @@ "use strict"; -const mongodb = require("mongodb"); -const config = require("../config"); -const mongodbFunctions = require("./mongodb-functions"); -const expression = require("../common/expression"); +import { MongoClient } from "mongodb"; +import * as config from "../config"; +import * as mongodbFunctions from "./mongodb-functions"; +import * as expression from "../common/expression"; const CACHE_TTL = 300000; @@ -24,7 +24,7 @@ function getClient() { if (!_clientPromise) { _clientPromise = new Promise((resolve, reject) => { const CONNECTION_URL = config.get("MONGODB_CONNECTION_URL"); - mongodb.MongoClient.connect( + MongoClient.connect( CONNECTION_URL, { useNewUrlParser: true }, (err, client) => { @@ -39,7 +39,7 @@ function getClient() { return _clientPromise; } -function cache(key, valueGetter, ttl) { +export function cache(key, valueGetter, ttl) { return new Promise((resolve, reject) => { getClient() .then(client => { @@ -72,7 +72,7 @@ function cache(key, valueGetter, ttl) { }); } -function query(resource, filter, options, callback) { +export function query(resource, filter, options, callback) { options = options || {}; let q; filter = expression.evaluate(filter, null, Date.now()); @@ -158,7 +158,7 @@ function query(resource, filter, options, callback) { }); } -function count(resource, filter) { +export function count(resource, filter) { let q; filter = expression.evaluate(filter, null, Date.now()); @@ -187,15 +187,10 @@ function count(resource, filter) { }); } -function disconnect() { +export function disconnect() { if (_clientPromise) { _clientPromise.then(client => { client.close(); }); } } - -exports.query = query; -exports.count = count; -exports.cache = cache; -exports.disconnect = disconnect; diff --git a/lib/ui/mongodb-functions.js b/lib/ui/mongodb-functions.js index 741783e..3a72d89 100644 --- a/lib/ui/mongodb-functions.js +++ b/lib/ui/mongodb-functions.js @@ -1,10 +1,10 @@ "use strict"; -const mongodb = require("mongodb"); -const expressionParser = require("../common/expression-parser"); -const expression = require("../common/expression"); +import { ObjectID } from "mongodb"; +import { map, parse, stringify } from "../common/expression-parser"; +import { likePatternToRegExp } from "../common/expression"; -function processDeviceFilter(filter) { - return expressionParser.map(filter, exp => { +export function processDeviceFilter(filter) { + return map(filter, exp => { if (!Array.isArray(exp)) return exp; if (exp[0] === "PARAM") { @@ -32,7 +32,7 @@ function processDeviceFilter(filter) { } else if ( ["=", "<>", ">", ">=", "<", "<=", "LIKE", "NOT LIKE"].includes(exp[0]) ) { - let e = expressionParser.map(exp, ee => { + let e = map(exp, ee => { if ( Array.isArray(ee) && ee[0] === "PARAM" && @@ -54,13 +54,12 @@ function processDeviceFilter(filter) { }); } -function processTasksFilter(filter) { - return expressionParser.map(filter, exp => { +export function processTasksFilter(filter) { + return map(filter, exp => { if (!Array.isArray(exp)) return exp; if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { const e = exp.slice(); - if (e[1][0] === "PARAM" && e[1][1] === "_id") - e[2] = new mongodb.ObjectID(e[2]); + if (e[1][0] === "PARAM" && e[1][1] === "_id") e[2] = new ObjectID(e[2]); else if (e[1][0] === "PARAM" && e[1][1] === "timestamp") e[2] = new Date(e[2]); else if (e[1][0] === "PARAM" && e[1][1] === "expiry") @@ -71,8 +70,8 @@ function processTasksFilter(filter) { }); } -function processFaultsFilter(filter) { - return expressionParser.map(filter, exp => { +export function processFaultsFilter(filter) { + return map(filter, exp => { if (!Array.isArray(exp)) return exp; if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { const e = exp.slice(); @@ -85,7 +84,7 @@ function processFaultsFilter(filter) { }); } -function filterToMongoQuery(filter, negate = false, res = {}) { +export function filterToMongoQuery(filter, negate = false, res = {}) { const op = filter[0]; if ((!negate && op === "AND") || (negate && op === "OR")) { @@ -160,7 +159,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) { param = filter[1][1]; flags = ""; } - const r = expression.likePatternToRegExp(filter[2], filter[3], flags); + const r = likePatternToRegExp(filter[2], filter[3], flags); if (negate) res[param] = { $not: r }; else res[param] = r; } else { @@ -170,7 +169,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) { return res; } -function processDeviceProjection(projection) { +export function processDeviceProjection(projection) { if (!projection) return projection; const p = {}; for (const [k, v] of Object.entries(projection)) { @@ -196,7 +195,7 @@ function processDeviceProjection(projection) { return p; } -function processDeviceSort(sort) { +export function processDeviceSort(sort) { if (!sort) return sort; const s = {}; for (const [k, v] of Object.entries(sort)) { @@ -217,7 +216,7 @@ function parseDate(d) { return isNaN(n) ? "" + d : n; } -function flattenDevice(device) { +export function flattenDevice(device) { function recursive(input, root, output, timestamp) { for (const [name, tree] of Object.entries(input)) { if (!root) { @@ -361,14 +360,14 @@ function flattenDevice(device) { return newDevice; } -function flattenFault(fault) { +export function flattenFault(fault) { const f = Object.assign({}, fault); if (f.timestamp) f.timestamp = +f.timestamp; if (f.expiry) f.expiry = +f.expiry; return f; } -function flattenTask(task) { +export function flattenTask(task) { const t = Object.assign({}, task); t._id = "" + t._id; if (t.timestamp) t.timestamp = +t.timestamp; @@ -443,13 +442,11 @@ function mongoQueryToFilter(query) { return expressions; } -function flattenPreset(preset) { +export function flattenPreset(preset) { const p = Object.assign({}, preset); if (p.precondition) { p.precondition = mongoQueryToFilter(JSON.parse(p.precondition)); - p.precondition = p.precondition.length - ? expression.stringify(p.precondition) - : ""; + p.precondition = p.precondition.length ? stringify(p.precondition) : ""; } if (p.events) { @@ -474,7 +471,7 @@ function flattenPreset(preset) { return p; } -function flattenFile(file) { +export function flattenFile(file) { const f = {}; f["_id"] = file["_id"]; if (file.metadata) { @@ -486,10 +483,10 @@ function flattenFile(file) { return f; } -function preProcessPreset(data) { +export function preProcessPreset(data) { const preset = Object.assign({}, data); preset.precondition = preset.precondition - ? filterToMongoQuery(expression.parse(preset.precondition)) + ? filterToMongoQuery(parse(preset.precondition)) : {}; preset.precondition = JSON.stringify(preset.precondition); @@ -526,25 +523,10 @@ function preProcessPreset(data) { return preset; } -function preProcessProvision(data) { +export function preProcessProvision(data) { return data.script; } -function preProcessVirtualParameters(data) { +export function preProcessVirtualParameters(data) { return data.script; } - -exports.processDeviceFilter = processDeviceFilter; -exports.processTasksFilter = processTasksFilter; -exports.processFaultsFilter = processFaultsFilter; -exports.filterToMongoQuery = filterToMongoQuery; -exports.processDeviceProjection = processDeviceProjection; -exports.processDeviceSort = processDeviceSort; -exports.flattenDevice = flattenDevice; -exports.flattenFault = flattenFault; -exports.flattenTask = flattenTask; -exports.flattenPreset = flattenPreset; -exports.flattenFile = flattenFile; -exports.preProcessPreset = preProcessPreset; -exports.preProcessProvision = preProcessProvision; -exports.preProcessVirtualParameters = preProcessVirtualParameters; diff --git a/lib/versioned-map.js b/lib/versioned-map.js index 30853cd..5f8591b 100644 --- a/lib/versioned-map.js +++ b/lib/versioned-map.js @@ -21,7 +21,7 @@ const NONEXISTENT = new Object(); const UNDEFINED = undefined; -class VersionedMap { +export default class VersionedMap { constructor() { this._sizeDiff = [0]; this._revision = 0; @@ -189,5 +189,3 @@ class VersionedMap { } } } - -module.exports = VersionedMap; diff --git a/package.json b/package.json index 48280d4..a1392d9 100644 --- a/package.json +++ b/package.json @@ -38,12 +38,16 @@ "codemirror": "^5.40.0", "cssnano": "^4.0.5", "eslint": "^5.10.0", + "esm": "^3.1.1", "mithril": "^1.1.6", "postcss-cssnext": "^3.1.0", "postcss-import": "^11.1.0", "prettier": "^1.15.3", + "rollup": "^1.1.2", + "rollup-plugin-json": "^3.1.0", + "rollup-plugin-replace": "^2.1.0", + "rollup-plugin-terser": "^4.0.2", "webpack": "^4.16.3", - "webpack-node-externals": "^1.7.2", "yaml": "^1.0.0" }, "engines": { @@ -53,8 +57,8 @@ "compile": "echo 'Compile step no longer required, skipping.'", "configure": "if [ ! -f ./config/config.json ]; then cp ./config/config-sample.json ./config/config.json; fi", "install": "npm run configure", - "test": "ava --verbose --color", - "lint": "eslint --fix lib ui build test bin/*; prettier --write \"lib/**/*.js\" \"ui/**/*.js\" \"build/**/*.js\" \"test/**/*.js\" bin/*", + "test": "ava -r esm --verbose --color", + "lint": "eslint --fix lib ui build test bin/* tools/*; prettier --parser babylon --write \"lib/**/*.js\" \"ui/**/*.js\" \"build/**/*.js\" \"test/**/*.js\" bin/* tools/*", "build": "node build/build" } } diff --git a/test/common.js b/test/common.js index 91800f2..06f724b 100644 --- a/test/common.js +++ b/test/common.js @@ -1,7 +1,7 @@ "use strict"; -const ava = require("ava"); -const common = require("../lib/common.js"); +import ava from "ava"; +import * as common from "../lib/common.js"; ava("typeOf", t => { const cases = [ diff --git a/tools/configure-ui b/tools/configure-ui index ba23305..a404385 100755 --- a/tools/configure-ui +++ b/tools/configure-ui @@ -1,8 +1,8 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm "use strict"; -const mongodb = require("mongodb"); -const config = require("../lib/config"); +import { MongoClient } from "mongodb"; +import { get } from "../lib/config"; const configData = { "auth.simple.users.admin.password": "'admin'", @@ -158,8 +158,8 @@ const configData = { }; async function run() { - const client = await mongodb.MongoClient.connect( - config.get("MONGODB_CONNECTION_URL"), + const client = await MongoClient.connect( + get("MONGODB_CONNECTION_URL"), { useNewUrlParser: true } ); const collection = client.db().collection("config"); diff --git a/tools/dump-data-model b/tools/dump-data-model index e7d47c1..af0bd1b 100755 --- a/tools/dump-data-model +++ b/tools/dump-data-model @@ -1,4 +1,4 @@ -#!/usr/bin/env node +#!/usr/bin/env -S node -r esm /** * Copyright 2013-2017 Zaid Abdulla * @@ -19,73 +19,66 @@ */ "use strict"; -const fs = require("fs"); -const path = require("path"); +import fs from "fs"; +import path from "path"; +import { connect, devicesCollection } from "../lib/db"; const outputFilename = path.resolve(`data_model_${process.argv[2]}.json`); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); -const db = require("../lib/db"); - const dataModel = {}; +function recursive(object, p) { + if (!p) p = ""; -function recursive(object, path) { - if (!path) - path = ""; + for (const key in object) { + if (key[0] === "_" || !object[key]) continue; - for (let key in object) { - if (key[0] === "_" || !object[key]) - continue; - - let isObject = object[key]["_object"]; - let isValue = "_value" in object[key]; + const isObject = object[key]["_object"]; + const isValue = "_value" in object[key]; if ((isObject && isValue) || (!isObject && !isValue)) throw new Error("Unexpected object"); if (isValue) { let value = object[key]["_value"]; - if (typeof value === "boolean") - value = +value; + if (typeof value === "boolean") value = +value; - let values = [object[key]["_writable"] || false, value.toString()]; - if (object[key]["_type"]) - values.push(object[key]["_type"]); + const values = [object[key]["_writable"] || false, value.toString()]; + if (object[key]["_type"]) values.push(object[key]["_type"]); - dataModel[`${path}${key}`] = values; + dataModel[`${p}${key}`] = values; } if (isObject) { - let values = [object[key]["_writable"] || false]; - dataModel[`${path}${key}.`] = values; - recursive(object[key], `${path}${key}.`); + const values = [object[key]["_writable"] || false]; + dataModel[`${p}${key}.`] = values; + recursive(object[key], `${p}${key}.`); } } } +connect(err => { + if (err) throw err; -db.connect(function(err) { - if (err) - throw err; + return devicesCollection.findOne( + { + _id: process.argv[2] + }, + {}, + (err, device) => { + if (err) throw err; - return db.devicesCollection.findOne({ - _id: process.argv[2] - }, {}, function(err, device) { - if (err) - throw err; + if (!device) throw new Error("Device not found"); - if (!device) - throw new Error("Device not found"); + recursive(device); - recursive(device); + fs.writeFile(outputFilename, JSON.stringify(dataModel, null, 2), () => { + if (err) throw err; - fs.writeFile(outputFilename, JSON.stringify(dataModel, null, 2), function() { - if (err) - throw err; - - console.log(`${outputFilename} saved`); - process.exit(0); - }); - }); + process.stdout.write(`${outputFilename} saved\n`); + process.exit(0); + }); + } + ); }); diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json index 9513ebf..35ee742 100644 --- a/ui/.eslintrc.json +++ b/ui/.eslintrc.json @@ -5,7 +5,6 @@ }, "parser": "babel-eslint", "parserOptions": { - "allowImportExportEverywhere": true, - "sourceType": "module" + "allowImportExportEverywhere": true } } diff --git a/ui/components.js b/ui/components.js index 492fa92..985282e 100644 --- a/ui/components.js +++ b/ui/components.js @@ -13,20 +13,23 @@ import tags from "./components/tags"; import ping from "./components/ping"; import deviceLink from "./components/device-link"; -function get(name) { - if (name === "parameter") return parameter; - else if (name === "parameter-list") return parameterList; - else if (name === "parameter-table") return parameterTable; - else if (name === "overview-dot") return overviewDot; - else if (name === "container") return container; - else if (name === "summon-button") return summonButton; - else if (name === "device-faults") return deviceFaults; - else if (name === "all-parameters") return allParameters; - else if (name === "device-actions") return deviceActions; - else if (name === "tags") return tags; - else if (name === "ping") return ping; - else if (name === "device-link") return deviceLink; - else throw new Error(`No such component '${name}'`); -} +const comps = { + parameter, + "parameter-list": parameterList, + "parameter-table": parameterTable, + "overview-dot": overviewDot, + container, + "summon-button": summonButton, + "device-faults": deviceFaults, + "all-parameters": allParameters, + "device-actions": deviceActions, + tags, + ping, + "device-link": deviceLink +}; -export { get }; +export function get(name) { + const c = comps[name]; + if (!c) throw new Error(`No such component '${name}'`); + return c; +} diff --git a/ui/components/all-parameters.js b/ui/components/all-parameters.js index b679349..72a8a52 100644 --- a/ui/components/all-parameters.js +++ b/ui/components/all-parameters.js @@ -4,10 +4,10 @@ import m from "mithril"; import * as components from "../components"; import * as taskQueue from "../task-queue"; import longTextComponent from "../long-text-component"; -import * as expression from "../../lib/common/expression"; +import { parse } from "../../lib/common/expression-parser"; import memoize from "../../lib/common/memoize"; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); function escapeRegExp(str) { return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); diff --git a/ui/components/parameter-list.js b/ui/components/parameter-list.js index 9f02629..553d77e 100644 --- a/ui/components/parameter-list.js +++ b/ui/components/parameter-list.js @@ -1,20 +1,20 @@ "use strict"; import m from "mithril"; -import * as components from "../components"; +import { get } from "../components"; import memoize from "../../lib/common/memoize"; const getChildAttrs = memoize((attrs, device) => Object.assign({}, attrs, { device: device }) ); -const component = { +export default { view: vnode => { const device = vnode.attrs.device; const rows = Object.values(vnode.attrs.parameters).map(parameter => { const p = m( - components.get(parameter.type || "parameter"), + get(parameter.type || "parameter"), getChildAttrs(parameter, device) ); @@ -33,5 +33,3 @@ const component = { return m("table.parameter-list", rows); } }; - -export default component; diff --git a/ui/devices-page.js b/ui/devices-page.js index b99a798..50c9522 100644 --- a/ui/devices-page.js +++ b/ui/devices-page.js @@ -8,19 +8,20 @@ import * as store from "./store"; import * as components from "./components"; import * as taskQueue from "./task-queue"; import * as notifications from "./notifications"; -import * as expression from "../lib/common/expression"; +import { evaluate, extractParams } from "../lib/common/expression"; +import { parse, stringify } from "../lib/common/expression-parser"; import memoize from "../lib/common/memoize"; import * as smartQuery from "./smart-query"; import * as expressionParser from "../lib/common/expression-parser"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const memoizedGetSortable = memoize(p => { - const expressionParams = expression.extractParams(p); + const expressionParams = extractParams(p); if (expressionParams.length === 1) { - const param = expression.evaluate(expressionParams[0]); + const param = evaluate(expressionParams[0]); if (typeof param === "string") return param; } return null; @@ -28,8 +29,7 @@ const memoizedGetSortable = memoize(p => { const getDownloadUrl = memoize((filter, indexParameters) => { const columns = {}; - for (const p of indexParameters) - columns[p.label] = expression.stringify(p.parameter); + for (const p of indexParameters) columns[p.label] = stringify(p.parameter); return `/api/devices.csv?${m.buildQueryString({ filter: filter, columns: JSON.stringify(columns) diff --git a/ui/faults-page.js b/ui/faults-page.js index cfe6f78..6833d47 100644 --- a/ui/faults-page.js +++ b/ui/faults-page.js @@ -5,16 +5,15 @@ import config from "./config"; import filterComponent from "./filter-component"; import * as store from "./store"; import * as notifications from "./notifications"; -import * as expression from "../lib/common/expression"; import memoize from "../lib/common/memoize"; import * as smartQuery from "./smart-query"; -import * as expressionParser from "../lib/common/expression-parser"; +import { map, parse } from "../lib/common/expression-parser"; import { loadYaml, yaml } from "./dynamic-loader"; import longTextComponent from "./long-text-component"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const attributes = [ @@ -41,7 +40,7 @@ const getDownloadUrl = memoize(filter => { }); const unpackSmartQuery = memoize(query => { - return expressionParser.map(query, e => { + return map(query, e => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") return smartQuery.unpack("faults", e[2], e[3]); return e; diff --git a/ui/files-page.js b/ui/files-page.js index 561410a..020ee47 100644 --- a/ui/files-page.js +++ b/ui/files-page.js @@ -5,17 +5,16 @@ import m from "mithril"; import config from "./config"; import filterComponent from "./filter-component"; import * as store from "./store"; -import * as expression from "../lib/common/expression"; import * as notifications from "./notifications"; import memoize from "../lib/common/memoize"; import putFormComponent from "./put-form-component"; import * as overlay from "./overlay"; import * as smartQuery from "./smart-query"; -import * as expressionParser from "../lib/common/expression-parser"; +import { map, parse } from "../lib/common/expression-parser"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const attributes = [ @@ -45,7 +44,7 @@ const formData = { }; const unpackSmartQuery = memoize(query => { - return expressionParser.map(query, e => { + return map(query, e => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") return smartQuery.unpack("files", e[2], e[3]); return e; diff --git a/ui/filter-component.js b/ui/filter-component.js index b805a8f..e98117d 100644 --- a/ui/filter-component.js +++ b/ui/filter-component.js @@ -1,7 +1,7 @@ "use strict"; import m from "mithril"; -import * as expression from "../lib/common/expression"; +import { parse, stringify } from "../lib/common/expression-parser"; import memoize from "../lib/common/memoize"; import Autocomplete from "./autocomplete-compnent"; import * as smartQuery from "./smart-query"; @@ -21,19 +21,19 @@ function parseFilter(f) { const v = f.slice(k.length + 1).trim(); return ["FUNC", "Q", k.trim(), v]; } - return expression.parse(f); + return parse(f); } function stringifyFilter(f) { if (Array.isArray(f) && f[0] === "FUNC" && f[1] === "Q") return `${f[2]}: ${f[3]}`; - return expression.stringify(f); + return stringify(f); } const splitFilter = memoize(filter => { if (!filter) return [""]; const list = []; - const f = expression.parse(filter); + const f = parse(filter); if (Array.isArray(f) && f[0] === "AND") for (const ff of f.slice(1)) list.push(stringifyFilter(ff)); else list.push(stringifyFilter(f)); @@ -70,7 +70,7 @@ const component = { } else { if (filter.length > 1) filter = ["AND"].concat(filter); else filter = filter[0]; - vnode.attrs.onChange(expression.stringify(filter)); + vnode.attrs.onChange(stringify(filter)); } } } diff --git a/ui/pie-chart-component.js b/ui/pie-chart-component.js index 6d84893..f7942ab 100644 --- a/ui/pie-chart-component.js +++ b/ui/pie-chart-component.js @@ -1,10 +1,10 @@ "use strict"; import m from "mithril"; -import * as expression from "../lib/common/expression"; +import { stringify } from "../lib/common/expression-parser"; import memoize from "../lib/common/memoize"; -const memoizedStringify = memoize(expression.stringify); +const memoizedStringify = memoize(stringify); const drawChart = function(chartData) { const slices = chartData.slices; diff --git a/ui/presets-page.js b/ui/presets-page.js index bc1480c..00d4f35 100644 --- a/ui/presets-page.js +++ b/ui/presets-page.js @@ -6,16 +6,15 @@ import config from "./config"; import filterComponent from "./filter-component"; import * as overlay from "./overlay"; import * as store from "./store"; -import * as expression from "../lib/common/expression"; import * as notifications from "./notifications"; import putFormComponent from "./put-form-component"; import memoize from "../lib/common/memoize"; import * as smartQuery from "./smart-query"; -import * as expressionParser from "../lib/common/expression-parser"; +import { map, parse } from "../lib/common/expression-parser"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const attributes = [ @@ -30,7 +29,7 @@ const attributes = [ ]; const unpackSmartQuery = memoize(query => { - return expressionParser.map(query, e => { + return map(query, e => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") return smartQuery.unpack("presets", e[2], e[3]); return e; diff --git a/ui/provisions-page.js b/ui/provisions-page.js index f95375c..9bb668c 100644 --- a/ui/provisions-page.js +++ b/ui/provisions-page.js @@ -5,18 +5,17 @@ import m from "mithril"; import config from "./config"; import filterComponent from "./filter-component"; import * as store from "./store"; -import * as expression from "../lib/common/expression"; import * as notifications from "./notifications"; import memoize from "../lib/common/memoize"; import putFormComponent from "./put-form-component"; import * as overlay from "./overlay"; import * as smartQuery from "./smart-query"; -import * as expressionParser from "../lib/common/expression-parser"; +import { map, parse } from "../lib/common/expression-parser"; import { loadCodeMirror } from "./dynamic-loader"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const attributes = [ @@ -25,7 +24,7 @@ const attributes = [ ]; const unpackSmartQuery = memoize(query => { - return expressionParser.map(query, e => { + return map(query, e => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") return smartQuery.unpack("provisions", e[2], e[3]); return e; diff --git a/ui/store.js b/ui/store.js index 20df6b9..618d4a3 100644 --- a/ui/store.js +++ b/ui/store.js @@ -1,12 +1,12 @@ "use strict"; import m from "mithril"; -import * as expressionParser from "../lib/common/expression-parser"; -import * as expression from "../lib/common/expression"; +import { map, stringify } from "../lib/common/expression-parser"; +import { or, and, not, evaluate, subset } from "../lib/common/expression"; import memoize from "../lib/common/memoize"; -const memoizedStringify = memoize(expression.stringify); -const memoizedEvaluate = memoize(expression.evaluate); +const memoizedStringify = memoize(stringify); +const memoizedEvaluate = memoize(evaluate); let fulfillTimestamp = 0; @@ -74,43 +74,37 @@ function limitFilter(filter, sort, bookmark) { const arr = Object.entries(sort) .sort(sortSort) .reverse(); - return expression.and( + return and( filter, arr.reduce((cur, kv) => { const [param, asc] = kv; if (asc <= 0) { if (bookmark[param] == null) { - return expression.or( + return or( ["IS NOT NULL", ["PARAM", param]], - expression.and(["IS NULL", ["PARAM", param]], cur) + and(["IS NULL", ["PARAM", param]], cur) ); } let f = null; if (typeof bookmark[param] !== "string") - f = expression.or(f, [">=", ["PARAM", param], ""]); + f = or(f, [">=", ["PARAM", param], ""]); - f = expression.or(f, [">", ["PARAM", param], bookmark[param]]); - return expression.or( - f, - expression.and(["=", ["PARAM", param], bookmark[param]], cur) - ); + f = or(f, [">", ["PARAM", param], bookmark[param]]); + return or(f, and(["=", ["PARAM", param], bookmark[param]], cur)); } else { let f = ["IS NULL", ["PARAM", param]]; - if (bookmark[param] == null) return expression.and(f, cur); + if (bookmark[param] == null) return and(f, cur); if (typeof bookmark[param] !== "number") { - f = expression.or(f, [">=", ["PARAM", param], 0]); - f = expression.or(f, ["<", ["PARAM", param], 0]); + f = or(f, [">=", ["PARAM", param], 0]); + f = or(f, ["<", ["PARAM", param], 0]); } - f = expression.or(f, ["<", ["PARAM", param], bookmark[param]]); + f = or(f, ["<", ["PARAM", param], bookmark[param]]); - return expression.or( - f, - expression.and(["=", ["PARAM", param], bookmark[param]], cur) - ); + return or(f, and(["=", ["PARAM", param], bookmark[param]], cur)); } }, true) ); @@ -157,7 +151,7 @@ function compareFunction(sort) { function findMatches(resourceType, filter, sort, limit) { // Handle "tag =" and "tag <>" special cases if (resourceType === "devices") { - filter = expressionParser.map(filter, e => { + filter = map(filter, e => { if ( Array.isArray(e) && Array.isArray(e[1]) && @@ -173,7 +167,7 @@ function findMatches(resourceType, filter, sort, limit) { let value = []; for (const obj of resources[resourceType].objects.values()) - if (expression.evaluate(filter, obj, fulfillTimestamp)) value.push(obj); + if (evaluate(filter, obj, fulfillTimestamp)) value.push(obj); value = value.sort(compareFunction(sort)); if (limit) value = value.slice(0, limit); @@ -191,7 +185,7 @@ function inferQuery(resourceType, queryResponse) { if (bookmark) filter = limitFilter(filter, sort, bookmark); if ( resources[resourceType].combinedFilter && - expression.subset(filter, resources[resourceType].combinedFilter) + subset(filter, resources[resourceType].combinedFilter) ) queries.fulfilled.set(queryResponse, fulfillTimestamp); } @@ -356,7 +350,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) { if ( resources[resourceType].combinedFilter && - expression.subset(filter, resources[resourceType].combinedFilter) + subset(filter, resources[resourceType].combinedFilter) ) { queries.value.set( queryResponse, @@ -367,7 +361,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) { return false; } - combinedFilter = expression.or(combinedFilter, filter); + combinedFilter = or(combinedFilter, filter); return true; }); @@ -379,12 +373,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) { deleted = new Set(resources[resourceType].objects.keys()); let combinedFilterDiff = combinedFilter; if (resources[resourceType].combinedFilter) { - combinedFilterDiff = expression.and( + combinedFilterDiff = and( combinedFilterDiff, - expression.not(resources[resourceType].combinedFilter) + not(resources[resourceType].combinedFilter) ); } - resources[resourceType].combinedFilter = expression.or( + resources[resourceType].combinedFilter = or( combinedFilter, resources[resourceType].combinedFilter ); @@ -411,13 +405,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) { for (const d of deleted) { const obj = resources[resourceType].objects.get(d); - if ( - expression.evaluate( - combinedFilterDiff, - obj, - fulfillTimestamp - ) - ) + if (evaluate(combinedFilterDiff, obj, fulfillTimestamp)) resources[resourceType].objects.delete(d); } diff --git a/ui/virtual-parameters-page.js b/ui/virtual-parameters-page.js index d3c16ba..81cf278 100644 --- a/ui/virtual-parameters-page.js +++ b/ui/virtual-parameters-page.js @@ -5,18 +5,17 @@ import m from "mithril"; import config from "./config"; import filterComponent from "./filter-component"; import * as store from "./store"; -import * as expression from "../lib/common/expression"; import * as notifications from "./notifications"; import memoize from "../lib/common/memoize"; import putFormComponent from "./put-form-component"; import * as overlay from "./overlay"; import * as smartQuery from "./smart-query"; -import * as expressionParser from "../lib/common/expression-parser"; +import { map, parse } from "../lib/common/expression-parser"; import { loadCodeMirror } from "./dynamic-loader"; const PAGE_SIZE = config.ui.pageSize || 10; -const memoizedParse = memoize(expression.parse); +const memoizedParse = memoize(parse); const memoizedJsonParse = memoize(JSON.parse); const attributes = [ @@ -25,7 +24,7 @@ const attributes = [ ]; const unpackSmartQuery = memoize(query => { - return expressionParser.map(query, e => { + return map(query, e => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") return smartQuery.unpack("virtualParameters", e[2], e[3]); return e;