From ac980116bd3643ce0fb63f956ca7bcf8931eacef Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Mon, 27 Jul 2020 20:30:13 -0700 Subject: [PATCH] Update ESLint and Prettier --- bin/{genieacs-cwmp => genieacs-cwmp.ts} | 41 +- bin/{genieacs-ext => genieacs-ext.ts} | 18 +- bin/{genieacs-fs => genieacs-fs.ts} | 41 +- bin/{genieacs-nbi => genieacs-nbi.ts} | 41 +- bin/{genieacs-ui => genieacs-ui.ts} | 41 +- build/build.ts | 85 +- lib/api-functions.ts | 38 +- lib/auth.ts | 37 +- lib/cache.ts | 25 +- lib/cluster.ts | 14 +- lib/common.ts | 20 +- lib/common/authorizer.ts | 10 +- lib/common/expression-cnf.ts | 24 +- lib/common/expression-parser.ts | 83 +- lib/common/expression.ts | 62 +- lib/common/memoize.ts | 2 +- lib/common/path-set.ts | 14 +- lib/common/path.ts | 6 +- lib/common/sat-solver.ts | 2 +- lib/config.ts | 48 +- lib/connection-request.ts | 22 +- lib/cwmp.ts | 131 +-- lib/db.ts | 137 ++- lib/debug.ts | 14 +- lib/default-provisions.ts | 69 +- lib/device.ts | 39 +- lib/extensions.ts | 14 +- lib/forwarded.ts | 6 +- lib/fs.ts | 4 +- lib/gpn-heuristic.ts | 2 +- lib/init.ts | 99 +- lib/local-cache.ts | 74 +- lib/logger.ts | 119 +- lib/mongodb-functions.ts | 122 +- lib/nbi.ts | 121 +- lib/ping.ts | 10 +- lib/query.ts | 116 +- lib/sandbox.ts | 40 +- lib/scheduling.ts | 25 +- lib/server.ts | 17 +- lib/session.ts | 693 +++++------ lib/soap.ts | 143 ++- lib/types.ts | 16 +- lib/ui.ts | 26 +- lib/ui/api-functions.ts | 47 +- lib/ui/api.ts | 80 +- lib/ui/db.ts | 133 ++- lib/versioned-map.ts | 4 +- lib/xml-parser.ts | 16 +- npm-shrinkwrap.json | 1017 +++++++++-------- package.json | 20 +- test/auth.ts | 4 +- test/common.ts | 49 +- test/device.ts | 38 +- test/mongodb-functions.ts | 16 +- test/path-set.ts | 20 +- test/path.ts | 6 +- test/xml-parser.ts | 26 +- tools/{dump-data-model => dump-data-model.ts} | 6 +- tsconfig.json | 4 +- ui/admin-menu.ts | 4 +- ui/app.ts | 16 +- ui/autocomplete-compnent.ts | 20 +- ui/change-password-component.ts | 28 +- ui/code-editor-component.ts | 14 +- ui/components.ts | 12 +- ui/components/all-parameters.ts | 30 +- ui/components/container.ts | 6 +- ui/components/device-actions.ts | 20 +- ui/components/device-faults.ts | 23 +- ui/components/device-link.ts | 6 +- ui/components/overview-dot.ts | 10 +- ui/components/parameter-list.ts | 12 +- ui/components/parameter-table.ts | 30 +- ui/components/parameter.ts | 18 +- ui/components/ping.ts | 4 +- ui/components/summon-button.ts | 16 +- ui/components/tags.ts | 25 +- ui/config-functions.ts | 11 +- ui/config-page.ts | 60 +- ui/device-page.ts | 10 +- ui/devices-page.ts | 46 +- ui/drawer-component.ts | 72 +- ui/dynamic-loader.ts | 12 +- ui/error-page.ts | 4 +- ui/faults-page.ts | 34 +- ui/files-page.ts | 60 +- ui/filter-component.ts | 28 +- ui/index-table-component.ts | 24 +- ui/layout.ts | 12 +- ui/login-page.ts | 30 +- ui/long-text-component.ts | 16 +- ui/menu.ts | 6 +- ui/notifications.ts | 8 +- ui/overlay.ts | 14 +- ui/overview-page.ts | 8 +- ui/permissions-page.ts | 70 +- ui/pie-chart-component.ts | 28 +- ui/presets-page.ts | 82 +- ui/provisions-page.ts | 66 +- ui/put-form-component.ts | 91 +- ui/smart-query.ts | 51 +- ui/store.ts | 196 ++-- ui/task-queue.ts | 38 +- ui/timeago.ts | 4 +- ui/ui-config-component.ts | 33 +- ui/user-menu.ts | 10 +- ui/users-page.ts | 80 +- ui/virtual-parameters-page.ts | 66 +- ui/wizard-page.ts | 26 +- 110 files changed, 2993 insertions(+), 2794 deletions(-) rename bin/{genieacs-cwmp => genieacs-cwmp.ts} (76%) rename bin/{genieacs-ext => genieacs-ext.ts} (89%) rename bin/{genieacs-fs => genieacs-fs.ts} (76%) rename bin/{genieacs-nbi => genieacs-nbi.ts} (77%) rename bin/{genieacs-ui => genieacs-ui.ts} (78%) rename tools/{dump-data-model => dump-data-model.ts} (95%) diff --git a/bin/genieacs-cwmp b/bin/genieacs-cwmp.ts similarity index 76% rename from bin/genieacs-cwmp rename to bin/genieacs-cwmp.ts index 1d7d881..b74b513 100755 --- a/bin/genieacs-cwmp +++ b/bin/genieacs-cwmp.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only - /** * Copyright 2013-2019 GenieACS Inc. * @@ -31,8 +29,8 @@ import { version as VERSION } from "../package.json"; logger.init("cwmp", VERSION); -const SERVICE_ADDRESS = config.get("CWMP_INTERFACE"); -const SERVICE_PORT = config.get("CWMP_PORT"); +const SERVICE_ADDRESS = config.get("CWMP_INTERFACE") as string; +const SERVICE_PORT = config.get("CWMP_PORT") as number; function exitWorkerGracefully(): void { setTimeout(exitWorkerUngracefully, 5000).unref(); @@ -40,7 +38,7 @@ function exitWorkerGracefully(): void { db.disconnect(), cache.disconnect(), extensions.killAll(), - cluster.worker.disconnect() + cluster.worker.disconnect(), ]).catch(exitWorkerUngracefully); } @@ -51,12 +49,12 @@ function exitWorkerUngracefully(): void { } if (!cluster.worker) { - const WORKER_COUNT = config.get("CWMP_WORKER_PROCESSES"); + const WORKER_COUNT = config.get("CWMP_WORKER_PROCESSES") as number; logger.info({ message: `genieacs-cwmp starting`, pid: process.pid, - version: VERSION + version: VERSION, }); cluster.start(WORKER_COUNT, SERVICE_PORT, SERVICE_ADDRESS); @@ -64,7 +62,7 @@ if (!cluster.worker) { process.on("SIGINT", () => { logger.info({ message: "Received signal SIGINT, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); @@ -73,28 +71,25 @@ if (!cluster.worker) { process.on("SIGTERM", () => { logger.info({ message: "Received signal SIGTERM, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); }); } else { const ssl = { - key: config.get("CWMP_SSL_KEY"), - cert: config.get("CWMP_SSL_CERT") + key: config.get("CWMP_SSL_KEY") as string, + cert: config.get("CWMP_SSL_CERT") as string, }; - process.on("uncaughtException", err => { + process.on("uncaughtException", (err) => { if ((err as NodeJS.ErrnoException).code === "ERR_IPC_DISCONNECTED") return; logger.error({ message: "Uncaught exception", exception: err, - pid: process.pid + pid: process.pid, }); - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); const initPromise = Promise.all([db.connect(), cache.connect()]) @@ -108,7 +103,7 @@ if (!cluster.worker) { 0 ); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throw err; }); @@ -116,19 +111,13 @@ if (!cluster.worker) { process.on("SIGINT", () => { initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); process.on("SIGTERM", () => { initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); } diff --git a/bin/genieacs-ext b/bin/genieacs-ext.ts similarity index 89% rename from bin/genieacs-ext rename to bin/genieacs-ext.ts index 13eb893..864c687 100755 --- a/bin/genieacs-ext +++ b/bin/genieacs-ext.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only - /** * Copyright 2013-2019 GenieACS Inc. * @@ -35,8 +33,8 @@ function errorToFault(err: Error): Fault { message: err.message, detail: { name: err.name, - message: err.message - } + message: err.message, + }, }; if (err.stack) { @@ -56,16 +54,16 @@ function errorToFault(err: Error): Fault { return fault; } -process.on("uncaughtException", err => { +process.on("uncaughtException", (err) => { const fault = errorToFault(err); - jobs.forEach(jobId => { + jobs.forEach((jobId) => { process.send([jobId, fault, null]); }); jobs.clear(); process.disconnect(); }); -process.on("message", message => { +process.on("message", (message) => { jobs.add(message[0]); if (!script) { @@ -79,7 +77,7 @@ process.on("message", message => { if (!script[funcName]) { const fault = { code: "ext", - message: `No such function '${funcName}' in extension '${fileName}'` + message: `No such function '${funcName}' in extension '${fileName}'`, }; process.send([message[0], fault, null]); return; @@ -93,4 +91,6 @@ process.on("message", message => { }); // Ignore SIGINT -process.on("SIGINT", () => {}); +process.on("SIGINT", () => { + // Ignore +}); diff --git a/bin/genieacs-fs b/bin/genieacs-fs.ts similarity index 76% rename from bin/genieacs-fs rename to bin/genieacs-fs.ts index 75244bd..a743523 100755 --- a/bin/genieacs-fs +++ b/bin/genieacs-fs.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only - /** * Copyright 2013-2019 GenieACS Inc. * @@ -30,15 +28,15 @@ import { version as VERSION } from "../package.json"; logger.init("fs", VERSION); -const SERVICE_ADDRESS = config.get("FS_INTERFACE"); -const SERVICE_PORT = config.get("FS_PORT"); +const SERVICE_ADDRESS = config.get("FS_INTERFACE") as string; +const SERVICE_PORT = config.get("FS_PORT") as number; function exitWorkerGracefully(): void { setTimeout(exitWorkerUngracefully, 5000).unref(); Promise.all([ db.disconnect(), cache.disconnect(), - cluster.worker.disconnect() + cluster.worker.disconnect(), ]).catch(exitWorkerUngracefully); } @@ -47,12 +45,12 @@ function exitWorkerUngracefully(): void { } if (!cluster.worker) { - const WORKER_COUNT = config.get("FS_WORKER_PROCESSES"); + const WORKER_COUNT = config.get("FS_WORKER_PROCESSES") as number; logger.info({ message: `genieacs-fs starting`, pid: process.pid, - version: VERSION + version: VERSION, }); cluster.start(WORKER_COUNT, SERVICE_PORT, SERVICE_ADDRESS); @@ -60,7 +58,7 @@ if (!cluster.worker) { process.on("SIGINT", () => { logger.info({ message: "Received signal SIGINT, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); @@ -69,31 +67,28 @@ if (!cluster.worker) { process.on("SIGTERM", () => { logger.info({ message: "Received signal SIGTERM, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); }); } else { const ssl = { - key: config.get("FS_SSL_KEY"), - cert: config.get("FS_SSL_CERT") + key: config.get("FS_SSL_KEY") as string, + cert: config.get("FS_SSL_CERT") as string, }; let stopping = false; - process.on("uncaughtException", err => { + process.on("uncaughtException", (err) => { if ((err as NodeJS.ErrnoException).code === "ERR_IPC_DISCONNECTED") return; logger.error({ message: "Uncaught exception", exception: err, - pid: process.pid + pid: process.pid, }); stopping = true; - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); const _listener = (req, res): void => { @@ -105,7 +100,7 @@ if (!cluster.worker) { .then(() => { server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throw err; }); @@ -114,20 +109,14 @@ if (!cluster.worker) { process.on("SIGINT", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); process.on("SIGTERM", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); } diff --git a/bin/genieacs-nbi b/bin/genieacs-nbi.ts similarity index 77% rename from bin/genieacs-nbi rename to bin/genieacs-nbi.ts index 80a555e..783cc42 100755 --- a/bin/genieacs-nbi +++ b/bin/genieacs-nbi.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only - /** * Copyright 2013-2019 GenieACS Inc. * @@ -31,8 +29,8 @@ import { version as VERSION } from "../package.json"; logger.init("nbi", VERSION); -const SERVICE_ADDRESS = config.get("NBI_INTERFACE"); -const SERVICE_PORT = config.get("NBI_PORT"); +const SERVICE_ADDRESS = config.get("NBI_INTERFACE") as string; +const SERVICE_PORT = config.get("NBI_PORT") as number; function exitWorkerGracefully(): void { setTimeout(exitWorkerUngracefully, 5000).unref(); @@ -40,7 +38,7 @@ function exitWorkerGracefully(): void { db.disconnect(), cache.disconnect(), extensions.killAll(), - cluster.worker.disconnect() + cluster.worker.disconnect(), ]).catch(exitWorkerUngracefully); } @@ -51,12 +49,12 @@ function exitWorkerUngracefully(): void { } if (!cluster.worker) { - const WORKER_COUNT = config.get("NBI_WORKER_PROCESSES"); + const WORKER_COUNT = config.get("NBI_WORKER_PROCESSES") as number; logger.info({ message: `genieacs-nbi starting`, pid: process.pid, - version: VERSION + version: VERSION, }); cluster.start(WORKER_COUNT, SERVICE_PORT, SERVICE_ADDRESS); @@ -64,7 +62,7 @@ if (!cluster.worker) { process.on("SIGINT", () => { logger.info({ message: "Received signal SIGINT, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); @@ -73,31 +71,28 @@ if (!cluster.worker) { process.on("SIGTERM", () => { logger.info({ message: "Received signal SIGTERM, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); }); } else { const ssl = { - key: config.get("NBI_SSL_KEY"), - cert: config.get("NBI_SSL_CERT") + key: config.get("NBI_SSL_KEY") as string, + cert: config.get("NBI_SSL_CERT") as string, }; let stopping = false; - process.on("uncaughtException", err => { + process.on("uncaughtException", (err) => { if ((err as NodeJS.ErrnoException).code === "ERR_IPC_DISCONNECTED") return; logger.error({ message: "Uncaught exception", exception: err, - pid: process.pid + pid: process.pid, }); stopping = true; - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); const _listener = (req, res): void => { @@ -109,7 +104,7 @@ if (!cluster.worker) { .then(() => { server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throw err; }); @@ -118,20 +113,14 @@ if (!cluster.worker) { process.on("SIGINT", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); process.on("SIGTERM", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); } diff --git a/bin/genieacs-ui b/bin/genieacs-ui.ts similarity index 78% rename from bin/genieacs-ui rename to bin/genieacs-ui.ts index e7d2506..e798779 100755 --- a/bin/genieacs-ui +++ b/bin/genieacs-ui.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only - /** * Copyright 2013-2019 GenieACS Inc. * @@ -32,8 +30,8 @@ import { version as VERSION } from "../package.json"; logger.init("ui", VERSION); -const SERVICE_ADDRESS = config.get("UI_INTERFACE"); -const SERVICE_PORT = config.get("UI_PORT"); +const SERVICE_ADDRESS = config.get("UI_INTERFACE") as string; +const SERVICE_PORT = config.get("UI_PORT") as number; function exitWorkerGracefully(): void { setTimeout(exitWorkerUngracefully, 5000).unref(); @@ -42,7 +40,7 @@ function exitWorkerGracefully(): void { db2.disconnect(), cache.disconnect(), extensions.killAll(), - cluster.worker.disconnect() + cluster.worker.disconnect(), ]).catch(exitWorkerUngracefully); } @@ -53,12 +51,12 @@ function exitWorkerUngracefully(): void { } if (!cluster.worker) { - const WORKER_COUNT = config.get("UI_WORKER_PROCESSES"); + const WORKER_COUNT = config.get("UI_WORKER_PROCESSES") as number; logger.info({ message: `genieacs-ui starting`, pid: process.pid, - version: VERSION + version: VERSION, }); cluster.start(WORKER_COUNT, SERVICE_PORT, SERVICE_ADDRESS); @@ -66,7 +64,7 @@ if (!cluster.worker) { process.on("SIGINT", () => { logger.info({ message: "Received signal SIGINT, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); @@ -75,31 +73,28 @@ if (!cluster.worker) { process.on("SIGTERM", () => { logger.info({ message: "Received signal SIGTERM, exiting", - pid: process.pid + pid: process.pid, }); cluster.stop(); }); } else { const ssl = { - key: config.get("UI_SSL_KEY"), - cert: config.get("UI_SSL_CERT") + key: config.get("UI_SSL_KEY") as string, + cert: config.get("UI_SSL_CERT") as string, }; let stopping = false; - process.on("uncaughtException", err => { + process.on("uncaughtException", (err) => { if ((err as NodeJS.ErrnoException).code === "ERR_IPC_DISCONNECTED") return; logger.error({ message: "Uncaught exception", exception: err, - pid: process.pid + pid: process.pid, }); stopping = true; - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); const _listener = (req, res): void => { @@ -111,7 +106,7 @@ if (!cluster.worker) { .then(() => { server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throw err; }); @@ -120,20 +115,14 @@ if (!cluster.worker) { process.on("SIGINT", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); process.on("SIGTERM", () => { stopping = true; initPromise.finally(() => { - server - .stop() - .then(exitWorkerGracefully) - .catch(exitWorkerUngracefully); + server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully); }); }); } diff --git a/build/build.ts b/build/build.ts index d33c940..f94d90c 100644 --- a/build/build.ts +++ b/build/build.ts @@ -21,7 +21,6 @@ import * as path from "path"; import * as fs from "fs"; import { promisify } from "util"; import { rollup, WarningHandler } from "rollup"; -import rollupReplace from "rollup-plugin-replace"; import rollupJson from "rollup-plugin-json"; import typescript from "rollup-plugin-typescript"; import { terser } from "rollup-plugin-terser"; @@ -78,7 +77,7 @@ const externals = [ "codemirror", "codemirror/mode/javascript/javascript", "codemirror/mode/yaml/yaml", - "ipaddr.js" + "ipaddr.js", ]; function rmDirSync(dirPath): void { @@ -123,9 +122,7 @@ function generateSymbol(id: string, svgStr: string): string { } } const symbolBody = xml.children[0].children - .map(c => { - return xmlTostring(c); - }) + .map((c) => xmlTostring(c)) .join(""); return `${symbolBody}`; } @@ -147,7 +144,7 @@ async function init(): Promise { delete packageJson["devDependencies"]; packageJson["scripts"] = { install: packageJson["scripts"].install, - configure: packageJson["scripts"].configure + configure: packageJson["scripts"].configure, }; packageJson["version"] = `${packageJson["version"]}+${BUILD_METADATA}`; fs.writeFileSync( @@ -173,7 +170,7 @@ async function copyStatic(): Promise { "README.md", "CHANGELOG.md", "public/logo.svg", - "public/favicon.png" + "public/favicon.png", ]; for (const file of files) { @@ -194,35 +191,31 @@ async function generateCss(): Promise { stage: 3, features: { "nesting-rules": true, - "color-mod-function": true - } + "color-mod-function": true, + }, }), - cssnano + cssnano, ]).process(cssIn, { from: cssInPath, to: cssOutPath }); fs.writeFileSync(cssOutPath, cssOut.css); } async function generateToolsJs(): Promise { for (const bin of ["dump-data-model"]) { - const inputFile = path.resolve(INPUT_DIR, `tools/${bin}`); + const inputFile = path.resolve(INPUT_DIR, `tools/${bin}.ts`); const outputFile = path.resolve(OUTPUT_DIR, `tools/${bin}`); const bundle = await rollup({ input: inputFile, external: externals, acorn: { - allowHashBang: true + allowHashBang: true, }, plugins: [ - rollupReplace({ - delimiters: ["", ""], - "#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only": "" - }), typescript({ tsconfig: "./tsconfig.json", - include: [`tools/${bin}`, "lib/**/*.ts"] + include: [`tools/${bin}.ts`, "lib/**/*.ts"], }), - MODE === "production" ? terser() : null - ] + MODE === "production" ? terser() : null, + ], }); await bundle.write({ @@ -231,7 +224,7 @@ async function generateToolsJs(): Promise { sourcemap: "inline", sourcemapExcludeSources: true, banner: "#!/usr/bin/env node", - file: outputFile + file: outputFile, }); // Mark as executable @@ -246,25 +239,21 @@ async function generateBackendJs(): Promise { "genieacs-ext", "genieacs-nbi", "genieacs-fs", - "genieacs-ui" + "genieacs-ui", ]) { - const inputFile = path.resolve(INPUT_DIR, `bin/${bin}`); + const inputFile = path.resolve(INPUT_DIR, `bin/${bin}.ts`); const outputFile = path.resolve(OUTPUT_DIR, `bin/${bin}`); const bundle = await rollup({ input: inputFile, external: externals, acorn: { - allowHashBang: true + allowHashBang: true, }, treeshake: { propertyReadSideEffects: false, - pureExternalModules: true + pureExternalModules: true, }, plugins: [ - rollupReplace({ - delimiters: ["", ""], - "#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only": "" - }), rollupJson({ preferConst: true }), { resolveId: (importee, importer) => { @@ -274,14 +263,14 @@ async function generateBackendJs(): Promise { return path.resolve(OUTPUT_DIR, "package.json"); } return null; - } + }, }, typescript({ tsconfig: "./tsconfig.json", - include: [`bin/${bin}`, "lib/**/*.ts"] + include: [`bin/${bin}.ts`, "lib/**/*.ts"], }), - MODE === "production" ? terser() : null - ] + MODE === "production" ? terser() : null, + ], }); await bundle.write({ @@ -290,7 +279,7 @@ async function generateBackendJs(): Promise { sourcemap: "inline", sourcemapExcludeSources: true, banner: "#!/usr/bin/env node", - file: outputFile + file: outputFile, }); // Mark as executable @@ -308,17 +297,17 @@ async function generateFrontendJs(): Promise { external: externals, plugins: [ rollupJson({ preferConst: true }), - typescript({ tsconfig: "./tsconfig.json" }) + typescript({ tsconfig: "./tsconfig.json" }), ], inlineDynamicImports: true, treeshake: { propertyReadSideEffects: false, - pureExternalModules: true + pureExternalModules: true, }, onwarn: ((warning, warn) => { // Ignore circular dependency warnings if (warning.code !== "CIRCULAR_DEPENDENCY") warn(warning); - }) as WarningHandler + }) as WarningHandler, }); await bundle.write({ @@ -326,14 +315,14 @@ async function generateFrontendJs(): Promise { format: "esm", sourcemap: "inline", sourcemapExcludeSources: true, - file: outputFile + file: outputFile, }); const webpackConf = { mode: MODE, entry: outputFile, resolve: { - aliasFields: ["module"] + aliasFields: ["module"], }, devtool: "nosources-source-map", module: { @@ -341,14 +330,14 @@ async function generateFrontendJs(): Promise { { test: /\.js$/, use: ["source-map-loader"], - enforce: "pre" - } - ] + enforce: "pre", + }, + ], }, output: { path: path.resolve(OUTPUT_DIR, "public"), - filename: "app.js" - } + filename: "app.js", + }, }; const stats = await promisify(webpack)(webpackConf); @@ -379,13 +368,15 @@ init() generateIconsSprite(), generateToolsJs(), generateBackendJs(), - generateFrontendJs() + generateFrontendJs(), ]) - .then(() => {}) - .catch(err => { + .then(() => { + // Ignore + }) + .catch((err) => { process.stderr.write(err.stack + "\n"); }); }) - .catch(err => { + .catch((err) => { process.stderr.write(err.stack + "\n"); }); diff --git a/lib/api-functions.ts b/lib/api-functions.ts index 42c0b29..e1a7d68 100644 --- a/lib/api-functions.ts +++ b/lib/api-functions.ts @@ -24,11 +24,11 @@ import * as cache from "./cache"; import { getCurrentSnapshot, getConfig, - getConfigExpression + getConfigExpression, } from "./local-cache"; import { httpConnectionRequest, - udpConnectionRequest + udpConnectionRequest, } from "./connection-request"; import { Expression, Task } from "./types"; import { flattenDevice } from "./mongodb-functions"; @@ -36,7 +36,7 @@ import { evaluate } from "./common/expression"; export async function connectionRequest( deviceId: string, - device?: {} + device?: Record ): Promise { if (!device) { const res = await db.devicesCollection.findOne({ _id: deviceId }); @@ -137,7 +137,7 @@ export async function connectionRequest( "FUNC", "AUTH", ["PARAM", "username"], - ["PARAM", "password"] + ["PARAM", "password"], ] as Expression; } @@ -171,8 +171,12 @@ export async function connectionRequest( } } -export async function watchTask(deviceId, taskId, timeout): Promise { - await new Promise(resolve => setTimeout(resolve, 500)); +export async function watchTask( + deviceId: string, + taskId: string, + timeout: number +): Promise { + await new Promise((resolve) => setTimeout(resolve, 500)); const task = await db.tasksCollection.findOne( { _id: taskId }, @@ -182,7 +186,7 @@ export async function watchTask(deviceId, taskId, timeout): Promise { const q = { _id: `${deviceId}:task_${taskId}` }; const fault = await db.faultsCollection.findOne(q, { - projection: { _id: 1 } + projection: { _id: 1 }, }); if (fault) return "fault"; @@ -194,7 +198,7 @@ export async function watchTask(deviceId, taskId, timeout): Promise { function sanitizeTask(task): void { task.timestamp = new Date(task.timestamp || Date.now()); if (task.expiry) { - if (common.typeOf(task.expiry) === common.DATE_TYPE || isNaN(task.expiry)) + if (task.expiry instanceof Date || isNaN(task.expiry)) task.expiry = new Date(task.expiry); else task.expiry = new Date(task.timestamp.getTime() + +task.expiry * 1000); } @@ -269,8 +273,8 @@ function sanitizeTask(task): void { return task; } -export async function insertTasks(tasks): Promise { - if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks]; +export async function insertTasks(tasks: any[]): Promise { + if (tasks && !Array.isArray(tasks)) tasks = [tasks]; else if (!tasks || tasks.length === 0) return tasks || []; for (const task of tasks) { @@ -278,7 +282,7 @@ export async function insertTasks(tasks): Promise { if (task.uniqueKey) { await db.tasksCollection.deleteOne({ device: task.device, - uniqueKey: task.uniqueKey + uniqueKey: task.uniqueKey, }); } } @@ -286,20 +290,20 @@ export async function insertTasks(tasks): Promise { return tasks; } -export async function deleteDevice(deviceId): Promise { +export async function deleteDevice(deviceId: string): Promise { await Promise.all([ db.tasksCollection.deleteMany({ device: deviceId }), db.devicesCollection.deleteOne({ _id: deviceId }), db.faultsCollection.deleteMany({ _id: { - $regex: `^${common.escapeRegExp(deviceId)}\\:` - } + $regex: `^${common.escapeRegExp(deviceId)}\\:`, + }, }), db.operationsCollection.deleteMany({ _id: { - $regex: `^${common.escapeRegExp(deviceId)}\\:` - } + $regex: `^${common.escapeRegExp(deviceId)}\\:`, + }, }), - cache.del(`${deviceId}_tasks_faults_operations`) + cache.del(`${deviceId}_tasks_faults_operations`), ]); } diff --git a/lib/auth.ts b/lib/auth.ts index 6489303..28fa512 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -19,11 +19,11 @@ import { createHash, randomBytes, pbkdf2 } from "crypto"; -function parseHeaderFeilds(str: string): {} { +function parseHeaderFeilds(str: string): Record { const res = {}; const parts = str.split(","); - let part; + let part: string; while ((part = parts.shift()) != null) { const name = part.split("=", 1)[0]; if (name.length === part.length) { @@ -52,7 +52,9 @@ function parseHeaderFeilds(str: string): {} { return res; } -export function parseAuthorizationHeader(authHeader): { method: string } { +export function parseAuthorizationHeader( + authHeader: string +): { method: string } { authHeader = authHeader.trim(); const method = authHeader.split(" ", 1)[0]; const res = { method: method }; @@ -74,7 +76,9 @@ export function parseAuthorizationHeader(authHeader): { method: string } { return res; } -export function parseWwwAuthenticateHeader(authHeader): {} { +export function parseWwwAuthenticateHeader( + authHeader: string +): Record { authHeader = authHeader.trim(); const method = authHeader.split(" ", 1)[0]; const res = { method: method }; @@ -82,7 +86,7 @@ export function parseWwwAuthenticateHeader(authHeader): {} { return res; } -export function basic(username, password): string { +export function basic(username: string, password: string): string { return "Basic " + Buffer.from(`${username}:${password}`).toString("base64"); } @@ -99,20 +103,12 @@ export function digest( nc?: string | Buffer ): string { const ha1 = createHash("md5"); - ha1 - .update(username) - .update(":") - .update(realm) - .update(":") - .update(password); + ha1.update(username).update(":").update(realm).update(":").update(password); // TODO support "MD5-sess" algorithm directive const ha1d = ha1.digest("hex"); const ha2 = createHash("md5"); - ha2 - .update(httpMethod) - .update(":") - .update(uri); + ha2.update(httpMethod).update(":").update(uri); if (qop === "auth-int") { const bodyHash = createHash("md5") @@ -124,10 +120,7 @@ export function digest( const ha2d = ha2.digest("hex"); const hash = createHash("md5"); - hash - .update(ha1d) - .update(":") - .update(nonce); + hash.update(ha1d).update(":").update(nonce); if (qop) { hash .update(":") @@ -148,7 +141,7 @@ export function solveDigest( uri: string | Buffer, httpMethod: string | Buffer, body: string | Buffer, - authHeader + authHeader: Record ): string { const cnonce = randomBytes(8).toString("hex"); const nc = "00000001"; @@ -185,7 +178,7 @@ export function solveDigest( return authString; } -export function generateSalt(length): Promise { +export function generateSalt(length: number): Promise { return new Promise((resolve, reject) => { randomBytes(length, (err, rand) => { if (err) return void reject(err); @@ -194,7 +187,7 @@ export function generateSalt(length): Promise { }); } -export function hashPassword(pass, salt): Promise { +export function hashPassword(pass: string, salt: string): Promise { return new Promise((resolve, reject) => { pbkdf2(pass, salt, 10000, 128, "sha512", (err, hash) => { if (err) return void reject(err); diff --git a/lib/cache.ts b/lib/cache.ts index 1a01b71..f32318f 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -29,7 +29,7 @@ let mongoTimeOffset = 0; export async function connect(): Promise { const MONGODB_CONNECTION_URL = "" + config.get("MONGODB_CONNECTION_URL"); clientPromise = MongoClient.connect(MONGODB_CONNECTION_URL, { - useNewUrlParser: true + useNewUrlParser: true, }); const db = (await clientPromise).db(); mongoCollection = db.collection("cache"); @@ -43,7 +43,7 @@ export async function disconnect(): Promise { if (clientPromise) await (await clientPromise).close(); } -export async function get(key): Promise { +export async function get(key: string | string[]): Promise { const expire = new Date(Date.now() - mongoTimeOffset); if (Array.isArray(key)) { const res = await mongoCollection.find({ _id: { $in: key } }).toArray(); @@ -54,7 +54,7 @@ export async function get(key): Promise { }); const values = []; - res.forEach(r => { + res.forEach((r) => { if (r["expire"] > expire) values[indices[r["_id"]]] = r["value"]; }); @@ -66,7 +66,7 @@ export async function get(key): Promise { } } -export async function del(key): Promise { +export async function del(key: string | string[]): Promise { if (Array.isArray(key)) await mongoCollection.deleteMany({ _id: { $in: key } }); else await mongoCollection.deleteOne({ _id: key }); @@ -85,7 +85,7 @@ export async function set( ); } -export async function pop(key): Promise { +export async function pop(key: string): Promise { const res = await mongoCollection.findOneAndDelete({ _id: key }); if ( @@ -98,16 +98,17 @@ export async function pop(key): Promise { return null; } -export async function lock(lockName, ttl): Promise { - const token = Math.random() - .toString(36) - .slice(2); +export async function lock( + lockName: string, + ttl: number +): Promise<(extendTtl?: number) => Promise> { + const token = Math.random().toString(36).slice(2); - async function unlockOrExtend(extendTtl): Promise { + async function unlockOrExtend(extendTtl?: number): Promise { if (!extendTtl) { const res = await mongoCollection.deleteOne({ _id: lockName, - value: token + value: token, }); if (res["result"]["n"] !== 1) throw new Error("Lock expired"); } else { @@ -131,7 +132,7 @@ export async function lock(lockName, ttl): Promise { ); } catch (err) { if (err && err.code === 11000) { - await new Promise(resolve => setTimeout(resolve, 200)); + await new Promise((resolve) => setTimeout(resolve, 200)); return lock(lockName, ttl); } } diff --git a/lib/cluster.ts b/lib/cluster.ts index 12f0559..4010af6 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -41,7 +41,7 @@ function restartWorker(worker, code, signal): void { message: "Worker died", pid: worker.process.pid, exitCode: null, - signal: null + signal: null, }; if (code != null) msg.exitCode = code; @@ -57,7 +57,7 @@ function restartWorker(worker, code, signal): void { min2 = 0, min3 = 0; - crashes = crashes.filter(n => { + crashes = crashes.filter((n) => { if (n > now - 60000) ++min1; else if (n > now - 120000) ++min2; else if (n > now - 180000) ++min3; @@ -72,7 +72,7 @@ function restartWorker(worker, code, signal): void { logger.error({ message: "Too many crashes, exiting", - pid: process.pid + pid: process.pid, }); return; } @@ -89,7 +89,11 @@ function restartWorker(worker, code, signal): void { }, respawnTimestamp - now); } -export function start(workerCount, servicePort, serviceAddress): void { +export function start( + workerCount: number, + servicePort: number, + serviceAddress: string +): void { cluster.on("listening", (worker, address) => { if ( (address.addressType === 4 || address.addressType === 6) && @@ -100,7 +104,7 @@ export function start(workerCount, servicePort, serviceAddress): void { message: "Worker listening", pid: worker.process.pid, address: address.address, - port: address.port + port: address.port, }); } }); diff --git a/lib/common.ts b/lib/common.ts index 7fed1af..54454c1 100644 --- a/lib/common.ts +++ b/lib/common.ts @@ -17,22 +17,12 @@ * along with GenieACS. If not, see . */ -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]"; - -export const typeOf = (obj): string => Object.prototype.toString.call(obj); - -export function generateDeviceId(deviceIdStruct): string { +export function generateDeviceId( + deviceIdStruct: Record +): string { // Escapes everything except alphanumerics and underscore function esc(str): string { - return str.replace(/[^A-Za-z0-9_]/g, chr => { + return str.replace(/[^A-Za-z0-9_]/g, (chr) => { const buf = Buffer.from(chr, "utf8"); let rep = ""; for (const b of buf) rep += "%" + b.toString(16).toUpperCase(); @@ -54,6 +44,6 @@ export function generateDeviceId(deviceIdStruct): string { } // Source: http://stackoverflow.com/a/6969486 -export function escapeRegExp(str): string { +export function escapeRegExp(str: string): string { return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); } diff --git a/lib/common/authorizer.ts b/lib/common/authorizer.ts index 793df6b..f4e2550 100644 --- a/lib/common/authorizer.ts +++ b/lib/common/authorizer.ts @@ -23,13 +23,13 @@ import { evaluate, or } from "./expression"; export default class Authorizer { private permissionSets: PermissionSet[]; private validatorCache: WeakMap< - object, + any, (mutationType, mutation, any) => boolean >; private hasAccessCache: Map; private getFilterCache: Map; - public constructor(permissionSets) { + public constructor(permissionSets: PermissionSet[]) { this.permissionSets = permissionSets; this.validatorCache = new WeakMap(); this.hasAccessCache = new Map(); @@ -77,8 +77,8 @@ export default class Authorizer { } public getValidator( - resourceType, - resource + resourceType: string, + resource: unknown ): (mutationType: string, mutation?: any, args?: any) => boolean { if (this.validatorCache.has(resource)) return this.validatorCache.get(resource); @@ -108,7 +108,7 @@ export default class Authorizer { mutation, resourceType, object: resource, - options: any + options: any, }; const valueFunction = (paramName): any => { diff --git a/lib/common/expression-cnf.ts b/lib/common/expression-cnf.ts index c5e44fa..2aacf75 100644 --- a/lib/common/expression-cnf.ts +++ b/lib/common/expression-cnf.ts @@ -65,7 +65,7 @@ function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0): boolean { export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { const ranges = new Map(); const likePatterns = new Map(); - map(exp, e => { + map(exp, (e) => { if (!Array.isArray(e)) return e; if (e[0] === "LIKE" || e[0] === "NOT LIKE") { if ( @@ -151,7 +151,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { if (to == null) vars.push(`${lhs}:${range[range.length - 1]}--`); - return vars.map(key => { + return vars.map((key) => { let f = expressions.get(key); if (!f) { expressions.set(key, (f = expressions.size + 1)); @@ -172,7 +172,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { vars.push(`${lhs}:like:${JSON.stringify(p)}`); } - return vars.map(key => { + return vars.map((key) => { let f = expressions.get(key); if (!f) expressions.set(key, (f = expressions.size + 1)); return f; @@ -184,11 +184,11 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { } const comparisonOperators: ComparisonOperators = { - "=": v => [v, true, v, true], - ">": v => [v, false, null, null], - ">=": v => [v, true, null, null], - "<": v => [null, null, v, false], - "<=": v => [null, null, v, true] + "=": (v) => [v, true, v, true], + ">": (v) => [v, false, null, null], + ">=": (v) => [v, true, null, null], + "<": (v) => [null, null, v, false], + "<=": (v) => [null, null, v, true], }; function recursive(clause, negate): CNF { @@ -211,7 +211,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { JSON.stringify(clause[1]), ...comparisonOperators["="](clause[2]) ); - if (!negate) return vars.map(x => [0 - x]); + if (!negate) return vars.map((x) => [0 - x]); else return [vars]; } else if (op === "IS NULL") { const v = getVariable(JSON.stringify(clause[1]), "null"); @@ -227,7 +227,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { JSON.stringify(clause[1]), ...comparisonOperators[op](clause[2]) ); - if (negate) return vars.map(x => [0 - x]); + if (negate) return vars.map((x) => [0 - x]); else return [vars]; } else if (!Array.isArray(clause[1])) { op = { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[op] || op; @@ -235,7 +235,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { JSON.stringify(clause[2]), ...comparisonOperators[op](clause[1]) ); - if (negate) return vars.map(x => [0 - x]); + if (negate) return vars.map((x) => [0 - x]); else return [vars]; } else { let lhs = JSON.stringify(clause[1]); @@ -264,7 +264,7 @@ export function booleanCnf(exp: Expression): { vars: number; clauses: CNF } { clause[2], clause[3] ); - if (negate) return vars.map(x => [0 - x]); + if (negate) return vars.map((x) => [0 - x]); else return [vars]; } else { const v = getVariable(JSON.stringify(clause), ""); diff --git a/lib/common/expression-parser.ts b/lib/common/expression-parser.ts index b78cf55..79fbbc2 100644 --- a/lib/common/expression-parser.ts +++ b/lib/common/expression-parser.ts @@ -27,7 +27,7 @@ function interpretEscapes(str): string { f: "\f", n: "\n", r: "\r", - t: "\t" + t: "\t", }; return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => { const type = escape.charAt(0); @@ -40,7 +40,10 @@ function interpretEscapes(str): string { }); } -export function map(exp, callback): Expression { +export function map( + exp: Expression, + callback: (exp: Expression) => Expression +): Expression { if (!Array.isArray(exp)) return callback(exp); let clone; @@ -55,7 +58,10 @@ export function map(exp, callback): Expression { return callback(clone || exp); } -export async function mapAsync(exp, callback): Promise { +export async function mapAsync( + exp: Expression, + callback: (exp: Expression) => Promise +): Promise { if (!Array.isArray(exp)) return callback(exp); let clone; @@ -70,7 +76,7 @@ export async function mapAsync(exp, callback): Promise { return callback(clone || exp); } -function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> { +function binaryLeft(operatorsParser, nextParser): parsimmon.Parser { return parsimmon.seqMap( nextParser, parsimmon.seq(operatorsParser, nextParser).many(), @@ -86,7 +92,7 @@ function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> { } const lang = parsimmon.createLanguage({ - ComparisonOperator: function() { + ComparisonOperator: function () { return parsimmon .alt( parsimmon.string(">="), @@ -98,13 +104,10 @@ const lang = parsimmon.createLanguage({ ) .skip(parsimmon.optWhitespace); }, - LikeOperator: function() { + LikeOperator: function () { return parsimmon .alt( - parsimmon - .regexp(/like/i) - .result("LIKE") - .desc("LIKE"), + parsimmon.regexp(/like/i).result("LIKE").desc("LIKE"), parsimmon .regexp(/not\s+like/i) .result("NOT LIKE") @@ -113,7 +116,7 @@ const lang = parsimmon.createLanguage({ .notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/)) .skip(parsimmon.optWhitespace); }, - IsNullOperator: function() { + IsNullOperator: function () { return parsimmon .alt( parsimmon @@ -128,7 +131,7 @@ const lang = parsimmon.createLanguage({ .notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/)) .skip(parsimmon.optWhitespace); }, - NotOperator: function() { + NotOperator: function () { return parsimmon .regexp(/not/i) .result("NOT") @@ -136,7 +139,7 @@ const lang = parsimmon.createLanguage({ .skip(parsimmon.optWhitespace) .desc("NOT"); }, - AndOperator: function() { + AndOperator: function () { return parsimmon .regexp(/and/i) .result("AND") @@ -144,7 +147,7 @@ const lang = parsimmon.createLanguage({ .skip(parsimmon.optWhitespace) .desc("AND"); }, - OrOperator: function() { + OrOperator: function () { return parsimmon .regexp(/or/i) .result("OR") @@ -152,7 +155,7 @@ const lang = parsimmon.createLanguage({ .skip(parsimmon.optWhitespace) .desc("OR"); }, - Parameter: function(r) { + Parameter: function (r) { return parsimmon .alt( parsimmon.regexp(/[a-zA-Z0-9_.*-]+/), @@ -162,48 +165,42 @@ const lang = parsimmon.createLanguage({ ) ) .atLeast(1) - .map(x => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]]) + .map((x) => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]]) .skip(parsimmon.optWhitespace) .desc("parameter"); }, - StringValueSql: function() { + StringValueSql: function () { return parsimmon .regexp(/'([^']*)'/, 1) .atLeast(1) .skip(parsimmon.optWhitespace) - .map(s => s.join("'")) + .map((s) => s.join("'")) .desc("string"); }, - StringValueJs: function() { + StringValueJs: function () { return parsimmon .regexp(/"((?:\\.|.)*?)"/, 1) .skip(parsimmon.optWhitespace) .map(interpretEscapes) .desc("string"); }, - NumberValue: function() { + NumberValue: function () { return parsimmon .regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/) .skip(parsimmon.optWhitespace) .map(Number) .desc("number"); }, - BooleanValue: function() { + BooleanValue: function () { return parsimmon .alt( - parsimmon - .regexp(/true/i) - .result(true) - .desc("TRUE"), - parsimmon - .regexp(/false/i) - .result(false) - .desc("FALSE") + parsimmon.regexp(/true/i).result(true).desc("TRUE"), + parsimmon.regexp(/false/i).result(false).desc("FALSE") ) .notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/)) .skip(parsimmon.optWhitespace); }, - NullValue: function() { + NullValue: function () { return parsimmon .regexp(/null/i) .notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/)) @@ -211,7 +208,7 @@ const lang = parsimmon.createLanguage({ .result(null) .desc("NULL"); }, - FuncValue: function(r) { + FuncValue: function (r) { return parsimmon.seqMap( parsimmon .regexp(/([a-zA-Z0-9_]+)/, 1) @@ -226,7 +223,7 @@ const lang = parsimmon.createLanguage({ (f, args) => ["FUNC", f.toUpperCase()].concat(args) ); }, - Value: function(r) { + Value: function (r) { return parsimmon.alt( r.NullValue, r.BooleanValue, @@ -236,7 +233,7 @@ const lang = parsimmon.createLanguage({ r.FuncValue ); }, - ValueExpression: function(r) { + ValueExpression: function (r) { return binaryLeft( parsimmon.string("||").skip(parsimmon.optWhitespace), binaryLeft( @@ -259,7 +256,7 @@ const lang = parsimmon.createLanguage({ ) ); }, - Comparison: function(r) { + Comparison: function (r) { return parsimmon.alt( parsimmon.seqMap(r.ValueExpression, r.IsNullOperator, (p, o) => [o, p]), parsimmon.seqMap( @@ -289,8 +286,8 @@ const lang = parsimmon.createLanguage({ ) ); }, - Expression: function(r) { - function unary(operatorsParser, nextParser): parsimmon.Parser<{}> { + Expression: function (r) { + function unary(operatorsParser, nextParser): parsimmon.Parser { return parsimmon.seq(operatorsParser, nextParser).or(nextParser); } @@ -301,7 +298,7 @@ const lang = parsimmon.createLanguage({ unary(r.NotOperator, r.Comparison.or(r.ValueExpression)) ) ).trim(parsimmon.optWhitespace); - } + }, }); export function parse(str: string): Expression { @@ -309,7 +306,7 @@ export function parse(str: string): Expression { return lang.Expression.tryParse(str); } -export function stringify(exp, level = 0): string { +export function stringify(exp: Expression, level = 0): string { if (!Array.isArray(exp)) return JSON.stringify(exp); const opLevels = { @@ -330,7 +327,7 @@ export function stringify(exp, level = 0): string { "+": 31, "-": 31, "*": 32, - "/": 32 + "/": 32, }; const op = exp[0].toUpperCase(); @@ -344,7 +341,7 @@ export function stringify(exp, level = 0): string { return wrap( `${exp[1]}(${exp .slice(2) - .map(e => stringify(e)) + .map((e) => stringify(e)) .join(", ")})` ); } else if (op === "PARAM") { @@ -354,7 +351,7 @@ export function stringify(exp, level = 0): string { return wrap( exp[1] .slice(1) - .map(p => { + .map((p) => { if (typeof p === "string") return p; else return `{${stringify(p)}}`; }) @@ -393,7 +390,7 @@ export function stringify(exp, level = 0): string { } } -export function parseLikePattern(pat, esc): string[] { +export function parseLikePattern(pat: string, esc: string): string[] { const chars = pat.split(""); for (let i = 0; i < chars.length; ++i) { @@ -408,5 +405,5 @@ export function parseLikePattern(pat, esc): string[] { while (chars[i + 1] === "%") chars[++i] = ""; } } - return chars.filter(c => c); + return chars.filter((c) => c); } diff --git a/lib/common/expression.ts b/lib/common/expression.ts index 2f30cbd..703310b 100644 --- a/lib/common/expression.ts +++ b/lib/common/expression.ts @@ -24,7 +24,7 @@ import { Expression } from "../types"; const isArray = Array.isArray; -const regExpCache = new WeakMap(); +const regExpCache = new WeakMap(); const REDUCE_SKIP = {}; function reduce(exp, callback): Expression { @@ -44,7 +44,7 @@ function reduce(exp, callback): Expression { return exp; } -export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp { +export function likePatternToRegExp(pat: string, esc = "", flags = ""): RegExp { const convChars = { "-": "\\-", "/": "\\/", @@ -63,11 +63,11 @@ export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp { "{": "\\{", "}": "\\}", "\\%": ".*", - "\\_": "." + "\\_": ".", }; let chars = parseLikePattern(pat, esc); if (!chars.length) return new RegExp("^$", flags); - chars = chars.map(c => convChars[c] || c); + chars = chars.map((c) => convChars[c] || c); chars[0] = chars[0] === ".*" ? "" : "^" + chars[0]; const l = chars.length - 1; chars[l] = [".*", ""].includes(chars[l]) ? "" : chars[l] + "$"; @@ -195,14 +195,24 @@ function evalExp(e: Expression): Expression { } export function evaluate( - exp, - obj, + exp: Expression, + obj: Record | ((exp: string) => Expression), now: number, - cb?: Function + cb?: (exp: Expression) => Expression ): string | number | boolean | null; -export function evaluate(exp, obj?, now?: number, cb?: Function): Expression; -export function evaluate(exp, obj?, now?: number, cb?: Function): Expression { - return map(exp, e => { +export function evaluate( + exp: Expression, + obj?: Record | ((exp: string) => Expression), + now?: number, + cb?: (exp: Expression) => Expression +): Expression; +export function evaluate( + exp: Expression, + obj?: Record | ((exp: string) => Expression), + now?: number, + cb?: (exp: Expression) => Expression +): Expression { + return map(exp, (e) => { if (cb) e = cb(e); if (!isArray(e)) return e; @@ -233,24 +243,24 @@ export function evaluate(exp, obj?, now?: number, cb?: Function): Expression { } export async function evaluateAsync( - exp, - obj, + exp: Expression, + obj: Record, now: number, - cb?: Function + cb?: (exp: Expression) => Promise ): Promise; export async function evaluateAsync( - exp, - obj?, + exp: Expression, + obj?: Record, now?: number, - cb?: Function + cb?: (exp: Expression) => Promise ): Promise; export async function evaluateAsync( - exp, - obj?, + exp: Expression, + obj?: Record, now?: number, - cb?: Function + cb?: (exp: Expression) => Promise ): Promise { - return mapAsync(exp, async e => { + return mapAsync(exp, async (e) => { if (cb) e = await cb(e); if (!isArray(e)) return e; @@ -269,8 +279,8 @@ export async function evaluateAsync( if (obj && !isArray(e[1])) { let v = obj[e[1]]; if (v == null) return null; - if (typeof v === "object") v = v.value ? v.value[0] : null; - return v; + if (typeof v === "object") v = v["value"] ? v["value"][0] : null; + return v as Expression; } } return evalExp(e); @@ -307,21 +317,21 @@ export function or(exp1: Expression, exp2: Expression): Expression { return res; } -export function not(exp): Expression { +export function not(exp: Expression): Expression { if (isArray(exp) && exp[0] === "NOT") return exp[1]; return ["NOT", exp]; } -export function subset(exp1, exp2): boolean { +export function subset(exp1: Expression, exp2: Expression): boolean { const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]); if (!isArray(e)) return !e; const { vars, clauses } = booleanCnf(e); return !naiveDpll(clauses, vars); } -export function extractParams(exp): Expression[] { +export function extractParams(exp: Expression): Expression[] { const params = new Set(); - map(exp, e => { + map(exp, (e) => { if (isArray(e) && e[0] === "PARAM") params.add(e[1]); return e; }); diff --git a/lib/common/memoize.ts b/lib/common/memoize.ts index 236310e..690f1da 100644 --- a/lib/common/memoize.ts +++ b/lib/common/memoize.ts @@ -39,7 +39,7 @@ function getKey(obj): string { return k; } -export default function memoize(func: T): T { +export default function memoize any>(func: T): T { const funcKey = getKey(func); return ((...args) => { const key = JSON.stringify(args.map(getKey)) + funcKey; diff --git a/lib/common/path-set.ts b/lib/common/path-set.ts index c2b7b73..4197536 100644 --- a/lib/common/path-set.ts +++ b/lib/common/path-set.ts @@ -73,8 +73,8 @@ export default class PathSet { public find( path: Path, - superset: boolean = false, - subset: boolean = false, + superset = false, + subset = false, depth: number = path.length ): Path[] { if (path.alias) throw new Error("PathSet does not support aliased paths"); @@ -106,24 +106,24 @@ export default class PathSet { if (!fragmentIndexSet1) { if (!fragmentIndexSet2) return []; if (!res) res = [...fragmentIndexSet2]; - else res = res.filter(r => fragmentIndexSet2.has(r)); + else res = res.filter((r) => fragmentIndexSet2.has(r)); } else if (!fragmentIndexSet2) { if (!res) res = [...fragmentIndexSet1]; - else res = res.filter(r => fragmentIndexSet1.has(r)); + else res = res.filter((r) => fragmentIndexSet1.has(r)); } else { if (!res) { res = [...fragmentIndexSet1, ...fragmentIndexSet2]; } else { res = res.filter( - r => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r) + (r) => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r) ); } } if (!res.length) return res; } - if (!res) res = [].concat(...lengthIndex.map(a => [...a])); - else res = res.filter(r => lengthIndex.some(a => a.has(r))); + if (!res) res = [].concat(...lengthIndex.map((a) => [...a])); + else res = res.filter((r) => lengthIndex.some((a) => a.has(r))); return res; } diff --git a/lib/common/path.ts b/lib/common/path.ts index 97b4289..908ac37 100644 --- a/lib/common/path.ts +++ b/lib/common/path.ts @@ -119,7 +119,7 @@ export default class Path { if (Array.isArray(s)) { alias |= 1 << i; const parts = s.map( - al => `${al[0].toString()}:${JSON.stringify(al[1])}` + (al) => `${al[0].toString()}:${JSON.stringify(al[1])}` ); return `[${parts.join(",")}]`; } else if (s === "*") { @@ -160,7 +160,7 @@ export default class Path { return this._string; } - public slice(start: number = 0, end: number = this.segments.length): Path { + public slice(start = 0, end: number = this.segments.length): Path { if (start < 0) start = Math.max(0, this.segments.length + start); if (end < 0) end = Math.max(0, this.segments.length + end); @@ -212,7 +212,7 @@ export default class Path { public stripAlias(): Path { if (!this.alias) return this; - const segments = this.segments.map(s => (Array.isArray(s) ? "*" : s)); + const segments = this.segments.map((s) => (Array.isArray(s) ? "*" : s)); const str = segments.join("."); let path = cache1.get(str); diff --git a/lib/common/sat-solver.ts b/lib/common/sat-solver.ts index 0e94062..1744c3a 100644 --- a/lib/common/sat-solver.ts +++ b/lib/common/sat-solver.ts @@ -168,7 +168,7 @@ function unitPropagate(clauses, varVals): number[] | 1 | -1 { return derivedVars; } -export function naiveDpll(clauses, maxvarnr): boolean { +export function naiveDpll(clauses: unknown[], maxvarnr: number): boolean { // 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); diff --git a/lib/config.ts b/lib/config.ts index 2ab8765..3186f4e 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -38,7 +38,7 @@ const options = { EXT_DIR: { type: "path", default: resolve(ROOT_DIR, "config/ext") }, MONGODB_CONNECTION_URL: { type: "string", - default: "mongodb://127.0.0.1/genieacs" + default: "mongodb://127.0.0.1/genieacs", }, CWMP_WORKER_PROCESSES: { type: "int", default: 0 }, @@ -100,7 +100,7 @@ const options = { MAX_COMMIT_ITERATIONS: { type: "int", default: 32 }, // Should probably never be changed - DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 } + DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 }, }; const allConfig: { [name: string]: string | number } = {}; @@ -112,37 +112,17 @@ function setConfig(name, value, commandLineArgument = false): boolean { if (name === "CONFIG_DIR" || name === "config-dir") configDir = configDir || resolve(ROOT_DIR, value); - if (name === "CWMP_SSL" || name === "cwmp-ssl") { - cwmpSsl = - cwmpSsl || - String(value) - .toLowerCase() - .trim(); - } + if (name === "CWMP_SSL" || name === "cwmp-ssl") + cwmpSsl = cwmpSsl || String(value).toLowerCase().trim(); - if (name === "NBI_SSL" || name === "nbi-ssl") { - nbiSsl = - nbiSsl || - String(value) - .toLowerCase() - .trim(); - } + if (name === "NBI_SSL" || name === "nbi-ssl") + nbiSsl = nbiSsl || String(value).toLowerCase().trim(); - if (name === "FS_SSL" || name === "fs-ssl") { - fsSsl = - fsSsl || - String(value) - .toLowerCase() - .trim(); - } + if (name === "FS_SSL" || name === "fs-ssl") + fsSsl = fsSsl || String(value).toLowerCase().trim(); - if (name === "UI_SSL" || name === "ui-ssl") { - uiSsl = - uiSsl || - String(value) - .toLowerCase() - .trim(); - } + if (name === "UI_SSL" || name === "ui-ssl") + uiSsl = uiSsl || String(value).toLowerCase().trim(); if (name === "FS_HOSTNAME" || name === "fs-hostname") fsHostname = fsHostname || String(value).trim(); @@ -170,11 +150,7 @@ function setConfig(name, value, commandLineArgument = false): boolean { case "int": return Number(val); case "bool": - return ["true", "1"].includes( - String(val) - .trim() - .toLowerCase() - ); + return ["true", "1"].includes(String(val).trim().toLowerCase()); case "string": return String(val); case "path": @@ -308,7 +284,7 @@ export function get( return null; } -export function getDefault(optionName): string | number | boolean { +export function getDefault(optionName: string): string | number | boolean { const option = options[optionName]; if (!option) return null; diff --git a/lib/connection-request.ts b/lib/connection-request.ts index a7bb666..cd317e2 100644 --- a/lib/connection-request.ts +++ b/lib/connection-request.ts @@ -65,10 +65,10 @@ function httpGet( timeout, _debug: boolean, deviceId: string -): Promise<{ statusCode: number; headers: {} }> { +): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> { return new Promise((resolve, reject) => { const req = http - .get(options, res => { + .get(options, (res) => { res.resume(); resolve({ statusCode: res.statusCode, headers: res.headers }); if (_debug) { @@ -76,12 +76,12 @@ function httpGet( debug.incomingHttpResponse(res, deviceId, null); } }) - .on("error", err => { + .on("error", (err) => { req.abort(); reject(new Error("Device is offline")); if (_debug) debug.outgoingHttpRequestError(req, deviceId, options, err); }) - .on("socket", socket => { + .on("socket", (socket) => { socket.setTimeout(timeout); socket.on("timeout", () => { req.abort(); @@ -104,10 +104,10 @@ export async function httpConnectionRequest( options.agent = new http.Agent({ maxSockets: 1, - keepAlive: true + keepAlive: true, }); - let authHeader: {}; + let authHeader: Record; let username: string; let password: string; @@ -121,8 +121,8 @@ export async function httpConnectionRequest( opts = Object.assign( { headers: { - Authorization: auth.basic(username || "", password || "") - } + Authorization: auth.basic(username || "", password || ""), + }, }, options ); @@ -137,8 +137,8 @@ export async function httpConnectionRequest( "GET", null, authHeader - ) - } + ), + }, }, options ); @@ -172,7 +172,7 @@ export async function httpConnectionRequest( export async function udpConnectionRequest( address: string, authExp: Expression, - sourcePort: number = 0, + sourcePort = 0, _debug: boolean, deviceId: string ): Promise { diff --git a/lib/cwmp.ts b/lib/cwmp.ts index 4176436..0cb189a 100644 --- a/lib/cwmp.ts +++ b/lib/cwmp.ts @@ -43,7 +43,8 @@ import { Expression, Task, SoapMessage, - InformRequest + InformRequest, + AcsResponse, } from "./types"; import { IncomingMessage, ServerResponse } from "http"; import { Readable } from "stream"; @@ -67,7 +68,7 @@ const stats = { concurrentRequests: 0, totalRequests: 0, droppedRequests: 0, - initiatedSessions: 0 + initiatedSessions: 0, }; async function authenticate( @@ -195,14 +196,14 @@ async function writeResponse( if (connection.destroyed) { logger.accessError({ sessionContext: sessionContext, - message: "Connection dropped" + message: "Connection dropped", }); } else if (close) { const isNew = await endSession(sessionContext); if (isNew) { logger.accessInfo({ sessionContext: sessionContext, - message: "New device registered" + message: "New device registered", }); } } else { @@ -265,7 +266,7 @@ function recordFault( message: "Channel has faulted", fault: fault, channel: channel, - retries: sessionContext.retries[channel] + retries: sessionContext.retries[channel], }); } @@ -288,7 +289,7 @@ function recordFault( async function inform( sessionContext: SessionContext, rpc: SoapMessage -): Promise<{ code: number; headers: {}; data: string }> { +): Promise<{ code: number; headers: Record; data: string }> { const acsResponse = await session.inform( sessionContext, rpc.cpeRequest as InformRequest @@ -297,7 +298,7 @@ async function inform( const res = soap.response({ id: rpc.id, acsResponse: acsResponse, - cwmpVersion: sessionContext.cwmpVersion + cwmpVersion: sessionContext.cwmpVersion, }); const cookiesPath = localCache.getConfig( @@ -305,7 +306,7 @@ async function inform( "cwmp.cookiesPath", {}, sessionContext.timestamp, - e => session.configContextCallback(sessionContext, e) + (e) => session.configContextCallback(sessionContext, e) ); if (cookiesPath) { @@ -329,7 +330,7 @@ async function transferComplete(sessionContext, rpc): Promise { logger.accessWarn({ sessionContext: sessionContext, message: "Unrecognized command key", - rpc: rpc + rpc: rpc, }); } @@ -346,7 +347,7 @@ async function transferComplete(sessionContext, rpc): Promise { const res = soap.response({ id: rpc.id, acsResponse: acsResponse, - cwmpVersion: sessionContext.cwmpVersion + cwmpVersion: sessionContext.cwmpVersion, }); return writeResponse(sessionContext, res); @@ -397,7 +398,7 @@ async function applyPresets(sessionContext: SessionContext): Promise { "cwmp.retryDelay", {}, sessionContext.timestamp, - e => session.configContextCallback(sessionContext, e) + (e) => session.configContextCallback(sessionContext, e) ); if (sessionContext.faults) { @@ -466,13 +467,13 @@ async function applyPresets(sessionContext: SessionContext): Promise { } } - const declarations = Object.values(parameters).map(v => ({ + const declarations = Object.values(parameters).map((v) => ({ path: v, pathGet: 1, pathSet: null, attrGet: { value: 1 }, attrSet: null, - defer: true + defer: true, })); const { fault: flt, rpcId: reqId, rpc: acsReq } = await session.rpcRequest( @@ -496,7 +497,7 @@ async function applyPresets(sessionContext: SessionContext): Promise { const appendProvisionsToFaults = {}; for (const p of filteredPresets) { if ( - evaluate(p.precondition, {}, sessionContext.timestamp, e => + evaluate(p.precondition, {}, sessionContext.timestamp, (e) => session.configContextCallback(sessionContext, e) ) ) { @@ -529,7 +530,7 @@ async function applyPresets(sessionContext: SessionContext): Promise { const fault = { code: "preset_loop", message: "The presets are stuck in an endless configuration loop", - timestamp: sessionContext.timestamp + timestamp: sessionContext.timestamp, }; recordFault(sessionContext, fault); // No need to save retryNow @@ -610,13 +611,13 @@ async function nextRpc(sessionContext: SessionContext): Promise { session.clearProvisions(sessionContext); // Clear expired tasks - sessionContext.tasks = sessionContext.tasks.filter(task => { + sessionContext.tasks = sessionContext.tasks.filter((task) => { if (!(task.expiry <= sessionContext.timestamp)) return true; logger.accessInfo({ sessionContext: sessionContext, message: "Task expired", - task: task + task: task, }); if (!sessionContext.doneTasks) sessionContext.doneTasks = []; @@ -633,7 +634,7 @@ async function nextRpc(sessionContext: SessionContext): Promise { }); const task = sessionContext.tasks.find( - t => !sessionContext.faults[`task_${t._id}`] + (t) => !sessionContext.faults[`task_${t._id}`] ); if (!task) return applyPresets(sessionContext); @@ -646,7 +647,7 @@ async function nextRpc(sessionContext: SessionContext): Promise { sessionContext.channels[`task_${task._id}`] = 0; for (const p of task.parameterNames) { session.addProvisions(sessionContext, `task_${task._id}`, [ - ["refresh", p] + ["refresh", p], ]); } @@ -656,14 +657,14 @@ async function nextRpc(sessionContext: SessionContext): Promise { sessionContext.channels[`task_${task._id}`] = 0; for (const p of task.parameterValues) { session.addProvisions(sessionContext, `task_${task._id}`, [ - ["value", p[0], p[1]] + ["value", p[0], p[1]], ]); } break; case "refreshObject": session.addProvisions(sessionContext, `task_${task._id}`, [ - ["refresh", task.objectName] + ["refresh", task.objectName], ]); break; case "reboot": @@ -674,20 +675,20 @@ async function nextRpc(sessionContext: SessionContext): Promise { break; case "download": session.addProvisions(sessionContext, `task_${task._id}`, [ - ["download", task.fileType, task.fileName, task.targetFileName || ""] + ["download", task.fileType, task.fileName, task.targetFileName || ""], ]); break; case "addObject": alias = (task.parameterValues || []) - .map(p => `${p[0]}:${JSON.stringify(p[1])}`) + .map((p) => `${p[0]}:${JSON.stringify(p[1])}`) .join(","); session.addProvisions(sessionContext, `task_${task._id}`, [ - ["instances", `${task.objectName}.[${alias}]`, "+1"] + ["instances", `${task.objectName}.[${alias}]`, "+1"], ]); break; case "deleteObject": session.addProvisions(sessionContext, `task_${task._id}`, [ - ["instances", task.objectName, 0] + ["instances", task.objectName, 0], ]); break; default: @@ -799,13 +800,13 @@ async function sendAcsRequest( const rpc = { id: id, acsRequest: acsRequest, - cwmpVersion: sessionContext.cwmpVersion + cwmpVersion: sessionContext.cwmpVersion, }; logger.accessInfo({ sessionContext: sessionContext, message: "ACS request", - rpc: rpc + rpc: rpc, }); const res = soap.response(rpc); @@ -821,7 +822,7 @@ async function getSession(connection, sessionId): Promise { if (!sessionId) return null; - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const sessionContextString = await cache.pop(`session_${sessionId}`); if (!sessionContextString) return null; @@ -848,7 +849,7 @@ export function onConnection(socket: Socket): void { const timeoutMsg = logger.flatten({ sessionContext: sessionContext, message: "Session timeout", - sessionTimestamp: sessionContext.timestamp + sessionTimestamp: sessionContext.timestamp, }); const timeout = @@ -883,7 +884,7 @@ setInterval(() => { droppedRequests: stats.droppedRequests, totalRequests: stats.totalRequests, initiatedSessions: stats.initiatedSessions, - pid: process.pid + pid: process.pid, }); } @@ -908,20 +909,20 @@ async function getDueTasksAndFaultsAndOperations( tasks: resParsed.tasks || [], faults: resParsed.faults || {}, operations: resParsed.operations || {}, - ttl: 0 + ttl: 0, }; } const res2 = await Promise.all([ db.getDueTasks(deviceId, timestamp), db.getFaults(deviceId), - db.getOperations(deviceId) + db.getOperations(deviceId), ]); return { tasks: res2[0][0], faults: res2[1], operations: res2[2], - ttl: res2[0][1] || 0 + ttl: res2[0][1] || 0, }; } @@ -935,7 +936,7 @@ async function cacheDueTasksAndFaultsAndOperations( const v = { tasks: null, faults: null, - operations: null + operations: null, }; if (tasks.length) v.tasks = tasks; if (Object.keys(faults).length) v.faults = faults; @@ -955,7 +956,7 @@ async function cacheDueTasksAndFaultsAndOperations( async function reportBadState(sessionContext: SessionContext): Promise { logger.accessError({ message: "Bad session state", - sessionContext: sessionContext + sessionContext: sessionContext, }); const httpResponse = sessionContext.httpResponse; currentSessions.delete(httpResponse.connection); @@ -976,7 +977,7 @@ async function responseUnauthorized( // Invalid credentials logger.accessError({ message: "Authentication failure", - sessionContext: sessionContext + sessionContext: sessionContext, }); resHeaders["Connection"] = "close"; @@ -1007,7 +1008,7 @@ async function responseUnauthorized( async function processRequest( sessionContext: SessionContext, rpc: SoapMessage, - parseWarnings: any[], + parseWarnings: Record[], body: string ): Promise { for (const w of parseWarnings) { @@ -1027,7 +1028,7 @@ async function processRequest( "cwmp.debug", {}, sessionContext.timestamp, - e => session.configContextCallback(sessionContext, e) + (e) => session.configContextCallback(sessionContext, e) ); if (!sessionContext.timeout) { @@ -1036,7 +1037,7 @@ async function processRequest( "cwmp.sessionTimeout", {}, sessionContext.timestamp, - e => session.configContextCallback(sessionContext, e) + (e) => session.configContextCallback(sessionContext, e) ); } @@ -1064,7 +1065,7 @@ async function processRequest( logger.accessInfo({ sessionContext: sessionContext, message: "Inform", - rpc: rpc + rpc: rpc, }); return writeResponse(sessionContext, res); @@ -1099,7 +1100,7 @@ async function processRequest( logger.accessInfo({ sessionContext: sessionContext, message: "CPE request", - rpc: rpc + rpc: rpc, }); return transferComplete(sessionContext, rpc); } else if (rpc.cpeRequest.name === "GetRPCMethods") { @@ -1108,15 +1109,15 @@ async function processRequest( logger.accessInfo({ sessionContext: sessionContext, message: "CPE request", - rpc: rpc + rpc: rpc, }); const res = soap.response({ id: rpc.id, acsResponse: { name: "GetRPCMethodsResponse", - methodList: ["Inform", "GetRPCMethods", "TransferComplete"] - }, - cwmpVersion: sessionContext.cwmpVersion + methodList: ["Inform", "GetRPCMethods", "TransferComplete"], + } as AcsResponse, + cwmpVersion: sessionContext.cwmpVersion, }); return writeResponse(sessionContext, res); } else { @@ -1136,7 +1137,7 @@ async function processRequest( logger.accessWarn({ sessionContext: sessionContext, message: "CPE fault", - rpc: rpc + rpc: rpc, }); const fault = await session.rpcFault(sessionContext, rpc.id, rpc.cpeFault); @@ -1179,7 +1180,7 @@ export function listener( .then(() => { stats.concurrentRequests -= 1; }) - .catch(err => { + .catch((err) => { currentSessions.delete(httpResponse.connection); httpResponse.writeHead(500, { Connection: "close" }); httpResponse.end(`${err.name}: ${err.message}`); @@ -1208,7 +1209,7 @@ async function listenerAsync( if (httpRequest.method !== "POST") { httpResponse.writeHead(405, { Allow: "POST", - Connection: "close" + Connection: "close", }); httpResponse.end("405 Method Not Allowed"); return; @@ -1225,7 +1226,7 @@ async function listenerAsync( if (!sessionId && stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) { httpResponse.writeHead(503, { "Retry-after": 60, - Connection: "close" + Connection: "close", }); httpResponse.end("503 Service Unavailable"); stats.droppedRequests += 1; @@ -1252,7 +1253,7 @@ async function listenerAsync( const chunks: Buffer[] = []; let bytes = 0; - stream.on("data", chunk => { + stream.on("data", (chunk) => { chunks.push(chunk); bytes += chunk.length; }); @@ -1290,7 +1291,7 @@ async function listenerAsync( ) { logger.accessError({ message: "Invalid session", - sessionContext: sessionContext + sessionContext: sessionContext, }); const _body = "Invalid session"; @@ -1327,7 +1328,7 @@ async function listenerAsync( if (!charset) { const parse = parseXmlDeclaration(body); - const e = parse ? parse.find(s => s.localName === "encoding") : null; + const e = parse ? parse.find((s) => s.localName === "encoding") : null; charset = e ? e.value.toLowerCase() : "utf8"; } @@ -1340,8 +1341,8 @@ async function listenerAsync( parseError: msg, sessionContext: sessionContext || { httpRequest: httpRequest, - httpResponse: httpResponse - } + httpResponse: httpResponse, + }, }); httpResponse.setHeader("Content-Length", Buffer.byteLength(msg)); httpResponse.writeHead(400, { Connection: "close" }); @@ -1354,10 +1355,10 @@ async function listenerAsync( cacheSnapshot, "cwmp.debug", { - remoteAddress: getRequestOrigin(httpRequest).remoteAddress + remoteAddress: getRequestOrigin(httpRequest).remoteAddress, }, Date.now(), - e => { + (e) => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS") return getRequestOrigin(httpRequest).remoteAddress; return e; @@ -1383,8 +1384,8 @@ async function listenerAsync( parseError: error.message.trim(), sessionContext: sessionContext || { httpRequest: httpRequest, - httpResponse: httpResponse - } + httpResponse: httpResponse, + }, }); httpResponse.setHeader("Content-Length", Buffer.byteLength(error.message)); httpResponse.writeHead(400, { Connection: "close" }); @@ -1402,10 +1403,10 @@ async function listenerAsync( cacheSnapshot, "cwmp.debug", { - remoteAddress: getRequestOrigin(httpRequest).remoteAddress + remoteAddress: getRequestOrigin(httpRequest).remoteAddress, }, Date.now(), - e => { + (e) => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS") return getRequestOrigin(httpRequest).remoteAddress; return e; @@ -1425,8 +1426,8 @@ async function listenerAsync( message: "Invalid session", sessionContext: sessionContext || { httpRequest: httpRequest, - httpResponse: httpResponse - } + httpResponse: httpResponse, + }, }); const _body = "Invalid session"; httpResponse.setHeader("Content-Length", Buffer.byteLength(_body)); @@ -1436,10 +1437,10 @@ async function listenerAsync( cacheSnapshot, "cwmp.debug", { - remoteAddress: getRequestOrigin(httpRequest).remoteAddress + remoteAddress: getRequestOrigin(httpRequest).remoteAddress, }, Date.now(), - e => { + (e) => { if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS") return getRequestOrigin(httpRequest).remoteAddress; return e; @@ -1473,7 +1474,7 @@ async function listenerAsync( tasks: dueTasks, faults, operations, - ttl: cacheUntil + ttl: cacheUntil, } = await getDueTasksAndFaultsAndOperations( deviceId, _sessionContext.timestamp diff --git a/lib/db.ts b/lib/db.ts index b50062d..51aa1cc 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -27,7 +27,7 @@ import { SessionFault, Task, Operation, - Expression + Expression, } from "./types"; import Path from "./common/path"; @@ -49,7 +49,7 @@ export let client: MongoClient; export async function connect(): Promise { clientPromise = MongoClient.connect("" + get("MONGODB_CONNECTION_URL"), { - useNewUrlParser: true + useNewUrlParser: true, }); client = await clientPromise; @@ -77,7 +77,7 @@ export async function disconnect(): Promise { // Optimize projection by removing overlaps // This can modify the object -function optimizeProjection(obj: {}): {} { +function optimizeProjection(obj: { [path: string]: 1 }): { [path: string]: 1 } { if (obj[""]) return { "": obj[""] }; const keys = Object.keys(obj).sort(); @@ -104,13 +104,13 @@ export async function fetchDevice( [ Path.parse("Events"), timestamp, - { object: [timestamp, 1], writable: [timestamp, 0] } + { object: [timestamp, 1], writable: [timestamp, 0] }, ], [ Path.parse("DeviceID"), timestamp, - { object: [timestamp, 1], writable: [timestamp, 0] } - ] + { object: [timestamp, 1], writable: [timestamp, 0] }, + ], ]; const device = await devicesCollection.findOne({ _id: id }); @@ -157,8 +157,8 @@ export async function fetchDevice( { object: [+v, 0], writable: [+v, 0], - value: [+v, [+v, "xsd:dateTime"]] - } + value: [+v, [+v, "xsd:dateTime"]], + }, ]); break; case "_lastBoot": @@ -168,8 +168,8 @@ export async function fetchDevice( { object: [+v, 0], writable: [+v, 0], - value: [+v, [+v, "xsd:dateTime"]] - } + value: [+v, [+v, "xsd:dateTime"]], + }, ]); break; case "_lastBootstrap": @@ -179,8 +179,8 @@ export async function fetchDevice( { object: [+v, 0], writable: [+v, 0], - value: [+v, [+v, "xsd:dateTime"]] - } + value: [+v, [+v, "xsd:dateTime"]], + }, ]); break; case "_registered": @@ -191,8 +191,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [+v, "xsd:dateTime"]] - } + value: [timestamp, [+v, "xsd:dateTime"]], + }, ]); break; case "_id": @@ -202,8 +202,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [v as string, "xsd:string"]] - } + value: [timestamp, [v as string, "xsd:string"]], + }, ]); break; case "_tags": @@ -211,7 +211,7 @@ export async function fetchDevice( res.push([ Path.parse("Tags"), timestamp, - { object: [timestamp, 1], writable: [timestamp, 0] } + { object: [timestamp, 1], writable: [timestamp, 0] }, ]); } @@ -223,8 +223,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 1], - value: [timestamp, [true, "xsd:boolean"]] - } + value: [timestamp, [true, "xsd:boolean"]], + }, ]); } break; @@ -236,8 +236,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [v["_Manufacturer"], "xsd:string"]] - } + value: [timestamp, [v["_Manufacturer"], "xsd:string"]], + }, ]); } @@ -248,8 +248,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [v["_OUI"], "xsd:string"]] - } + value: [timestamp, [v["_OUI"], "xsd:string"]], + }, ]); } @@ -260,8 +260,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [v["_ProductClass"], "xsd:string"]] - } + value: [timestamp, [v["_ProductClass"], "xsd:string"]], + }, ]); } @@ -272,8 +272,8 @@ export async function fetchDevice( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [v["_SerialNumber"], "xsd:string"]] - } + value: [timestamp, [v["_SerialNumber"], "xsd:string"]], + }, ]); } } @@ -398,7 +398,7 @@ export async function saveDevice( } else { if (!update["$pull"]["_tags"]) { update["$pull"]["_tags"] = { - $in: [] + $in: [], }; } update["$pull"]["_tags"]["$in"].push(path.segments[1]); @@ -510,7 +510,7 @@ export async function saveDevice( } const result = await devicesCollection.updateOne({ _id: deviceId }, update, { - upsert: isNew + upsert: isNew, }); if (result.result.n !== 1) @@ -523,7 +523,7 @@ export async function saveDevice( } export async function getFaults( - deviceId + deviceId: string ): Promise<{ [channel: string]: SessionFault }> { const res = await faultsCollection .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } }) @@ -543,24 +543,31 @@ export async function getFaults( return faults; } -export async function saveFault(deviceId, channel, fault): Promise { +export async function saveFault( + deviceId: string, + channel: string, + fault: SessionFault +): Promise { const id = `${deviceId}:${channel}`; - fault = Object.assign({}, fault); - fault._id = id; - fault.device = deviceId; - fault.channel = channel; - fault.timestamp = new Date(fault.timestamp); - fault.provisions = JSON.stringify(fault.provisions); - await faultsCollection.replaceOne({ _id: id }, fault, { upsert: true }); + const f = Object.assign({}, fault) as Record; + f["_id"] = id; + f["device"] = deviceId; + f["channel"] = channel; + f["timestamp"] = new Date(fault.timestamp); + f["provisions"] = JSON.stringify(fault.provisions); + await faultsCollection.replaceOne({ _id: id }, f, { upsert: true }); } -export async function deleteFault(deviceId, channel): Promise { +export async function deleteFault( + deviceId: string, + channel: string +): Promise { await faultsCollection.deleteOne({ _id: `${deviceId}:${channel}` }); } export async function getDueTasks( - deviceId, - timestamp + deviceId: string, + timestamp: number ): Promise<[Task[], number]> { const cur = tasksCollection.find({ device: deviceId }).sort(["timestamp"]); const tasks = [] as Task[]; @@ -593,14 +600,17 @@ export async function getDueTasks( return [tasks, null]; } -export async function clearTasks(deviceId, taskIds): Promise { +export async function clearTasks( + deviceId: string, + taskIds: string[] +): Promise { await tasksCollection.deleteMany({ - _id: { $in: taskIds.map(id => new ObjectID(id)) } + _id: { $in: taskIds.map((id) => new ObjectID(id)) }, }); } export async function getOperations( - deviceId + deviceId: string ): Promise<{ [commandKey: string]: Operation }> { const res = await operationsCollection .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } }) @@ -620,43 +630,46 @@ export async function getOperations( } export async function saveOperation( - deviceId, - commandKey, - operation + deviceId: string, + commandKey: string, + operation: Operation ): Promise { const id = `${deviceId}:${commandKey}`; - operation = Object.assign({}, operation); - operation._id = id; - operation.timestamp = new Date(operation.timestamp); - operation.provisions = JSON.stringify(operation.provisions); - operation.retries = JSON.stringify(operation.retries); - operation.args = JSON.stringify(operation.args); + const o = Object.assign({}, operation) as Record; + o["_id"] = id; + o["timestamp"] = new Date(operation.timestamp); + o["provisions"] = JSON.stringify(operation.provisions); + o["retries"] = JSON.stringify(operation.retries); + o["args"] = JSON.stringify(operation.args); await operationsCollection.replaceOne({ _id: id }, operation, { - upsert: true + upsert: true, }); } -export async function deleteOperation(deviceId, commandKey): Promise { +export async function deleteOperation( + deviceId: string, + commandKey: string +): Promise { await operationsCollection.deleteOne({ _id: `${deviceId}:${commandKey}` }); } -export async function getPresets(): Promise<{}[]> { +export async function getPresets(): Promise[]> { return presetsCollection.find().toArray(); } -export async function getObjects(): Promise<{}[]> { +export async function getObjects(): Promise[]> { return objectsCollection.find().toArray(); } -export async function getProvisions(): Promise<{}[]> { +export async function getProvisions(): Promise[]> { return provisionsCollection.find().toArray(); } -export async function getVirtualParameters(): Promise<{}[]> { +export async function getVirtualParameters(): Promise[]> { return virtualParametersCollection.find().toArray(); } -export function getFiles(): Promise<{}[]> { +export function getFiles(): Promise[]> { return filesCollection.find().toArray(); } @@ -664,9 +677,9 @@ export async function getConfig(): Promise< { id: string; value: Expression }[] > { const res = await configCollection.find().toArray(); - return res.map(c => ({ + return res.map((c) => ({ id: c["_id"], - value: parse(c["value"]) + value: parse(c["value"]), })); } diff --git a/lib/debug.ts b/lib/debug.ts index 5352725..ef6bb98 100644 --- a/lib/debug.ts +++ b/lib/debug.ts @@ -21,7 +21,7 @@ import { IncomingMessage, ServerResponse, ClientRequest, - RequestOptions + RequestOptions, } from "http"; import { Socket } from "net"; import { appendFileSync } from "fs"; @@ -60,7 +60,7 @@ export function incomingHttpRequest( method: httpRequest.method, url: httpRequest.url, headers: httpRequest.headers, - body: body + body: body, }; if (DEBUG_FORMAT === "yaml") @@ -86,7 +86,7 @@ export function outgoingHttpResponse( connection: getConnectionTimestamp(con), statusCode: httpResponse.statusCode, headers: httpResponse.getHeaders(), - body: body + body: body, }; if (DEBUG_FORMAT === "yaml") @@ -115,7 +115,7 @@ export function outgoingHttpRequest( method: options.method || "GET", url: options.path, headers: httpRequest.getHeaders(), - body: body + body: body, }; if (DEBUG_FORMAT === "yaml") @@ -143,7 +143,7 @@ export function outgoingHttpRequestError( method: options.method, url: options.path, headers: httpRequest.getHeaders(), - error: err.message + error: err.message, }; if (DEBUG_FORMAT === "yaml") @@ -169,7 +169,7 @@ export function incomingHttpResponse( connection: getConnectionTimestamp(httpResponse.connection), statusCode: httpResponse.statusCode, headers: httpResponse.headers, - body: body + body: body, }; if (DEBUG_FORMAT === "yaml") @@ -193,7 +193,7 @@ export function outgoingUdpMessage( remoteAddress: remoteAddress, deviceId: deviceId, remotePort: remotePort, - body: body + body: body, }; if (DEBUG_FORMAT === "yaml") diff --git a/lib/default-provisions.ts b/lib/default-provisions.ts index 1da26dd..3d35540 100644 --- a/lib/default-provisions.ts +++ b/lib/default-provisions.ts @@ -21,10 +21,15 @@ import Path from "./common/path"; import * as config from "./config"; import * as device from "./device"; import * as scheduling from "./scheduling"; +import { SessionContext, Declaration } from "./types"; const MAX_DEPTH = +config.get("MAX_DEPTH"); -export function refresh(sessionContext, provision, declarations): boolean { +export function refresh( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if ( (provision.length !== 2 || typeof provision[1] !== "string") && (provision.length !== 3 || @@ -38,7 +43,7 @@ export function refresh(sessionContext, provision, declarations): boolean { segments.length = MAX_DEPTH; segments.fill("*", l); const path = Path.parse(segments.join(".")); - const every = 1000 * (provision[2] || 1); + const every = 1000 * ((provision[2] as number) || 1); const offset = scheduling.variance(sessionContext.deviceId, every); const t = scheduling.interval(sessionContext.timestamp, every, offset); @@ -49,14 +54,18 @@ export function refresh(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { object: 1, writable: 1, value: t }, attrSet: null, - defer: true + defer: true, }); } return true; } -export function value(sessionContext, provision, declarations): boolean { +export function value( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if (provision.length !== 3 || typeof provision[1] !== "string") throw new Error("Invalid arguments"); @@ -66,13 +75,17 @@ export function value(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { value: 1 }, attrSet: { value: [provision[2]] }, - defer: true + defer: true, }); return true; } -export function tag(sessionContext, provision, declarations): boolean { +export function tag( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if ( provision.length !== 3 || typeof provision[1] !== "string" || @@ -86,13 +99,17 @@ export function tag(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { value: 1 }, attrSet: { value: [provision[2]] }, - defer: true + defer: true, }); return true; } -export function reboot(sessionContext, provision, declarations): boolean { +export function reboot( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if (provision.length !== 1) throw new Error("Invalid arguments"); declarations.push({ @@ -101,13 +118,17 @@ export function reboot(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { value: 1 }, attrSet: { value: [sessionContext.timestamp] }, - defer: true + defer: true, }); return true; } -export function reset(sessionContext, provision, declarations): boolean { +export function reset( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if (provision.length !== 1) throw new Error("Invalid arguments"); declarations.push({ @@ -116,13 +137,17 @@ export function reset(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { value: 1 }, attrSet: { value: [sessionContext.timestamp] }, - defer: true + defer: true, }); return true; } -export function download(sessionContext, provision, declarations): boolean { +export function download( + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[] +): boolean { if ( (provision.length !== 3 || typeof provision[1] !== "string" || @@ -137,7 +162,7 @@ export function download(sessionContext, provision, declarations): boolean { const alias = [ `FileType:${JSON.stringify(provision[1] || "")}`, `FileName:${JSON.stringify(provision[2] || "")}`, - `TargetFileName:${JSON.stringify(provision[3] || "")}` + `TargetFileName:${JSON.stringify(provision[3] || "")}`, ].join(","); declarations.push({ @@ -146,7 +171,7 @@ export function download(sessionContext, provision, declarations): boolean { pathSet: 1, attrGet: null, attrSet: null, - defer: true + defer: true, }); declarations.push({ @@ -155,18 +180,18 @@ export function download(sessionContext, provision, declarations): boolean { pathSet: null, attrGet: { value: 1 }, attrSet: { value: [sessionContext.timestamp] }, - defer: true + defer: true, }); return true; } export function instances( - sessionContext, - provision, - declarations, - startRevision, - endRevision + sessionContext: SessionContext, + provision: (string | number | boolean)[], + declarations: Declaration[], + startRevision: number, + endRevision: number ): boolean { if (provision.length !== 3 || typeof provision[1] !== "string") throw new Error("Invalid arguments"); @@ -184,7 +209,7 @@ export function instances( pathSet: null, attrGet: null, attrSet: null, - defer: true + defer: true, }); if (endRevision === startRevision) return false; @@ -203,7 +228,7 @@ export function instances( pathSet: count, attrGet: null, attrSet: null, - defer: true + defer: true, }); return true; diff --git a/lib/device.ts b/lib/device.ts index a42a09b..5089cd9 100644 --- a/lib/device.ts +++ b/lib/device.ts @@ -18,12 +18,18 @@ */ import Path from "./common/path"; -import { DeviceData, Declaration, Clear } from "./types"; +import { + DeviceData, + Declaration, + Clear, + AttributeTimestamps, + Attributes, +} from "./types"; const CHANGE_FLAGS = { object: 2, writable: 4, - value: 8 + value: 8, }; function parseBool(v): boolean { @@ -36,7 +42,7 @@ function parseBool(v): boolean { export function sanitizeParameterValue( parameterValue: [string | number | boolean, string] -): [string | number | boolean, string?] { +): [string | number | boolean, string] { if (parameterValue[0] != null) { switch (parameterValue[1]) { case "xsd:boolean": @@ -96,8 +102,8 @@ export function getAliasDeclarations( pathSet: null, attrGet: attrGet, attrSet: null, - defer: true - } + defer: true, + }, ]; if (path.alias) { @@ -107,7 +113,7 @@ export function getAliasDeclarations( for (const [p] of alias as [Path, string][]) { decs = decs.concat( getAliasDeclarations(parent.concat(p), timestamp, { - value: timestamp + value: timestamp, }) ); } @@ -189,10 +195,10 @@ export function unpack( } export function clear( - deviceData, - path, - timestamp, - attributes, + deviceData: DeviceData, + path: Path, + timestamp: number, + attributes: AttributeTimestamps, changeFlags = 0 ): void { const changeTrackers = {}; @@ -271,8 +277,8 @@ function compareEquality(a, b): boolean { export function set( deviceData: DeviceData, path: Path, - timestamp, - attributes, + timestamp: number, + attributes: Attributes, toClear?: Clear[] ): Clear[] { path = deviceData.paths.add(path); @@ -300,7 +306,7 @@ export function set( attributes.object[1] && attributes.object[0] >= (attributes.value ? attributes.value[0] : 0) ) - attributes.value = [attributes.object[0]]; + attributes.value = [attributes.object[0], null]; const newAttributes = Object.assign({}, currentAttributes, attributes); @@ -376,7 +382,7 @@ export function track( deviceData: DeviceData, path: Path, marker: string, - attributes? + attributes?: string[] ): void { path = deviceData.paths.add(path); let f = 1; @@ -393,7 +399,10 @@ export function track( cur[marker] |= f; } -export function clearTrackers(deviceData: DeviceData, tracker): void { +export function clearTrackers( + deviceData: DeviceData, + tracker: string | string[] +): void { if (Array.isArray(tracker)) { for (const v of deviceData.trackers.values()) for (const t of tracker) delete v[t]; diff --git a/lib/extensions.ts b/lib/extensions.ts index 0f3ec03..52b6b39 100644 --- a/lib/extensions.ts +++ b/lib/extensions.ts @@ -36,8 +36,8 @@ function messageHandler(message): void { } } -export function run(args): Promise<{ fault: Fault; value: any }> { - return new Promise(resolve => { +export function run(args: string[]): Promise<{ fault: Fault; value: any }> { + return new Promise((resolve) => { const scriptName = args[0]; const id = crypto.randomBytes(8).toString("hex"); @@ -45,16 +45,16 @@ export function run(args): Promise<{ fault: Fault; value: any }> { if (!processes[scriptName]) { const p = spawn(ROOT_DIR + "/bin/genieacs-ext", [scriptName], { - stdio: ["inherit", "inherit", "inherit", "ipc"] + stdio: ["inherit", "inherit", "inherit", "ipc"], }); processes[scriptName] = p; - p.on("error", err => { + p.on("error", (err) => { if (processes[scriptName] === p) { if (jobs.delete(id)) { resolve({ fault: { code: err.name, message: err.message }, - value: null + value: null, }); } @@ -74,7 +74,7 @@ export function run(args): Promise<{ fault: Fault; value: any }> { if (jobs.delete(id)) { resolve({ fault: { code: "timeout", message: "Extension timed out" }, - value: null + value: null, }); } }, TIMEOUT); @@ -86,7 +86,7 @@ export function run(args): Promise<{ fault: Fault; value: any }> { } function kill(process: ChildProcess): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const timeToKill = Date.now() + 5000; process.kill(); diff --git a/lib/forwarded.ts b/lib/forwarded.ts index aeadd4e..f8cab26 100644 --- a/lib/forwarded.ts +++ b/lib/forwarded.ts @@ -35,7 +35,7 @@ const FORWARDED_HEADER = "" + config.get("FORWARDED_HEADER"); const cache = new WeakMap(); const cidrs: [IPv4 | IPv6, number][] = []; -for (const str of FORWARDED_HEADER.split(",").map(s => s.trim())) { +for (const str of FORWARDED_HEADER.split(",").map((s) => s.trim())) { try { cidrs.push(parseCIDR(str)); } catch (err) { @@ -104,13 +104,13 @@ export function getRequestOrigin(request: IncomingMessage): RequestOrigin { remoteAddress: soc.remoteAddress, remotePort: soc.remotePort, host: request.headers["host"], - encrypted: !!(request.socket as TLSSocket).encrypted + encrypted: !!(request.socket as TLSSocket).encrypted, }; const header = request.headers["forwarded"]; if (header) { const ip = parse(soc.remoteAddress) as IPv4; - if (cidrs.some(cidr => ip.match(cidr as [IPv4, number]))) { + if (cidrs.some((cidr) => ip.match(cidr as [IPv4, number]))) { const parsed = parseForwardedHeader(header); if (parsed["proto"] === "https") { diff --git a/lib/fs.ts b/lib/fs.ts index 1eed20c..7eec066 100644 --- a/lib/fs.ts +++ b/lib/fs.ts @@ -36,7 +36,7 @@ export function listener( const log = { message: "Fetch file", filename: filename, - remoteAddress: getRequestOrigin(request).remoteAddress + remoteAddress: getRequestOrigin(request).remoteAddress, }; db.filesCollection.findOne({ _id: filename }, (err, file) => { @@ -52,7 +52,7 @@ export function listener( response.writeHead(200, { "Content-Type": file.contentType || "application/octet-stream", - "Content-Length": file.length + "Content-Length": file.length, }); const bucket = new GridFSBucket(db.client.db()); diff --git a/lib/gpn-heuristic.ts b/lib/gpn-heuristic.ts index de99b23..34313b1 100644 --- a/lib/gpn-heuristic.ts +++ b/lib/gpn-heuristic.ts @@ -31,7 +31,7 @@ const UNDISCOVERED_DEPTH = 7; // point has been discovered. export function estimateGpnCount( gpnPatterns: [Path, number][], - depth: number = 0 + depth = 0 ): number { const children: { [segment: string]: [Path, number][] } = {}; const wildcardChildren: [Path, number][] = []; diff --git a/lib/init.ts b/lib/init.ts index 4a67fac..816963a 100644 --- a/lib/init.ts +++ b/lib/init.ts @@ -95,11 +95,11 @@ export async function getStatus(): Promise { filters: !Object.keys(ui["filters"]).length, device: !Object.keys(ui["device"]).length, index: !Object.keys(ui["index"]).length, - overview: !Object.keys(ui["overview"]).length + overview: !Object.keys(ui["overview"]).length, }; } -export async function seed(options): Promise { +export async function seed(options: Record): Promise { const resources = {}; const proms = []; @@ -117,12 +117,12 @@ export async function seed(options): Promise { role: "admin", resource: "virtualParameters", access: 3, - validate: "true" - } + validate: "true", + }, ]; resources["users"] = [ - { username: "admin", password: "admin", roles: ["admin"] } + { username: "admin", password: "admin", roles: ["admin"] }, ]; } @@ -135,7 +135,7 @@ export async function seed(options): Promise { { _id: "ui.filters.1.parameter", value: "DeviceID.ProductClass" }, { _id: "ui.filters.1.type", value: "'string'" }, { _id: "ui.filters.2.label", value: "'Tag'" }, - { _id: "ui.filters.2.type", value: "'tag'" } + { _id: "ui.filters.2.type", value: "'tag'" }, ]); } @@ -149,118 +149,118 @@ export async function seed(options): Promise { { _id: "ui.device.2.parameters.0.label", value: "'Last inform'" }, { _id: "ui.device.2.parameters.0.components.0.type", - value: "'parameter'" + value: "'parameter'", }, { _id: "ui.device.2.parameters.0.components.1.chart", value: "'online'" }, { _id: "ui.device.2.parameters.0.components.1.type", - value: "'overview-dot'" + value: "'overview-dot'", }, { _id: "ui.device.2.parameters.0.components.2.type", - value: "'summon-button'" + value: "'summon-button'", }, { _id: "ui.device.2.parameters.0.components.2.parameters.0", - value: "InternetGatewayDevice.DeviceInfo.HardwareVersion" + value: "InternetGatewayDevice.DeviceInfo.HardwareVersion", }, { _id: "ui.device.2.parameters.0.components.2.parameters.1", - value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion" + value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion", }, { _id: "ui.device.2.parameters.0.components.2.parameters.2", value: - "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.MACAddress" + "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.MACAddress", }, { _id: "ui.device.2.parameters.0.components.2.parameters.3", value: - "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.ExternalIPAddress" + "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.ExternalIPAddress", }, { _id: "ui.device.2.parameters.0.components.2.parameters.4", - value: "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.SSID" + value: "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.SSID", }, { _id: "ui.device.2.parameters.0.components.2.parameters.5", value: - "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.KeyPassphrase" + "InternetGatewayDevice.LANDevice.*.WLANConfiguration.*.KeyPassphrase", }, { _id: "ui.device.2.parameters.0.components.2.parameters.6", - value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.HostName" + value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.HostName", }, { _id: "ui.device.2.parameters.0.components.2.parameters.7", - value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.IPAddress" + value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.IPAddress", }, { _id: "ui.device.2.parameters.0.components.2.parameters.8", - value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.MACAddress" + value: "InternetGatewayDevice.LANDevice.*.Hosts.Host.*.MACAddress", }, { _id: "ui.device.2.parameters.0.parameter", - value: "DATE_STRING(Events.Inform)" + value: "DATE_STRING(Events.Inform)", }, { _id: "ui.device.2.parameters.1.label", value: "'Serial number'" }, { _id: "ui.device.2.parameters.1.parameter", - value: "DeviceID.SerialNumber" + value: "DeviceID.SerialNumber", }, { _id: "ui.device.2.parameters.2.label", value: "'Product class'" }, { _id: "ui.device.2.parameters.2.parameter", - value: "DeviceID.ProductClass" + value: "DeviceID.ProductClass", }, { _id: "ui.device.2.parameters.3.label", value: "'OUI'" }, { _id: "ui.device.2.parameters.3.parameter", - value: "DeviceID.OUI" + value: "DeviceID.OUI", }, { _id: "ui.device.2.parameters.4.label", value: "'Manufacturer'" }, { _id: "ui.device.2.parameters.4.parameter", - value: "DeviceID.Manufacturer" + value: "DeviceID.Manufacturer", }, { _id: "ui.device.2.parameters.5.label", value: "'Hardware version'" }, { _id: "ui.device.2.parameters.5.parameter", - value: "InternetGatewayDevice.DeviceInfo.HardwareVersion" + value: "InternetGatewayDevice.DeviceInfo.HardwareVersion", }, { _id: "ui.device.2.parameters.6.label", value: "'Software version'" }, { _id: "ui.device.2.parameters.6.parameter", - value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion" + value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion", }, { _id: "ui.device.2.parameters.7.label", value: "'MAC'" }, { _id: "ui.device.2.parameters.7.parameter", value: - "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress" + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress", }, { _id: "ui.device.2.parameters.8.label", value: "'IP'" }, { _id: "ui.device.2.parameters.8.parameter", value: - "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress" + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", }, { _id: "ui.device.2.parameters.9.label", value: "'WLAN SSID'" }, { _id: "ui.device.2.parameters.9.parameter", - value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID" + value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", }, { _id: "ui.device.2.parameters.10.label", value: "'WLAN passphrase'" }, { _id: "ui.device.2.parameters.10.parameter", value: - "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase" + "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.KeyPassphrase", }, { _id: "ui.device.3.type", value: "'parameter-table'" }, { _id: "ui.device.3.label", value: "'WLAN hosts'" }, { _id: "ui.device.3.parameter", - value: "InternetGatewayDevice.LANDevice.1.Hosts.Host" + value: "InternetGatewayDevice.LANDevice.1.Hosts.Host", }, { _id: "ui.device.3.childParameters.0.label", value: "'Host name'" }, { _id: "ui.device.3.childParameters.0.parameter", value: "HostName" }, @@ -278,12 +278,12 @@ export async function seed(options): Promise { { _id: "ui.device.5.element", value: "'div.container-full-width'" }, { _id: "ui.device.5.components.0.components.0", - value: "'All parameters'" + value: "'All parameters'", }, { _id: "ui.device.5.components.0.element", value: "'h3'" }, { _id: "ui.device.5.components.0.type", value: "'container'" }, { _id: "ui.device.5.components.1.type", value: "'all-parameters'" }, - { _id: "ui.device.6.type", value: "'device-actions'" } + { _id: "ui.device.6.type", value: "'device-actions'" }, ]); } @@ -298,18 +298,18 @@ export async function seed(options): Promise { { _id: "ui.index.2.label", value: "'Software version'" }, { _id: "ui.index.2.parameter", - value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion" + value: "InternetGatewayDevice.DeviceInfo.SoftwareVersion", }, { _id: "ui.index.3.label", value: "'IP'" }, { _id: "ui.index.3.parameter", value: - "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress" + "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress", }, { _id: "ui.index.4.label", value: "'SSID'" }, { _id: "ui.index.4.parameter", - value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID" + value: "InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", }, { _id: "ui.index.5.type", value: "'container'" }, { _id: "ui.index.5.label", value: "'Last inform'" }, @@ -322,7 +322,7 @@ export async function seed(options): Promise { { _id: "ui.index.6.label", value: "'Tags'" }, { _id: "ui.index.6.parameter", value: "Tags" }, { _id: "ui.index.6.unsortable", value: "true" }, - { _id: "ui.index.6.writable", value: "false" } + { _id: "ui.index.6.writable", value: "false" }, ]); } @@ -331,43 +331,44 @@ export async function seed(options): Promise { { _id: "ui.overview.charts.online.label", value: "'Online status'" }, { _id: "ui.overview.charts.online.slices.1_onlineNow.color", - value: "'#31a354'" + value: "'#31a354'", }, { _id: "ui.overview.charts.online.slices.1_onlineNow.filter", - value: "Events.Inform > NOW() - 5 * 60 * 1000" + value: "Events.Inform > NOW() - 5 * 60 * 1000", }, { _id: "ui.overview.charts.online.slices.1_onlineNow.label", - value: "'Online now'" + value: "'Online now'", }, { _id: "ui.overview.charts.online.slices.2_past24.color", - value: "'#a1d99b'" + value: "'#a1d99b'", }, { _id: "ui.overview.charts.online.slices.2_past24.filter", value: - "Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)" + "Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)", }, { _id: "ui.overview.charts.online.slices.2_past24.label", - value: "'Past 24 hours'" + value: "'Past 24 hours'", }, { _id: "ui.overview.charts.online.slices.3_others.color", - value: "'#e5f5e0'" + value: "'#e5f5e0'", }, { _id: "ui.overview.charts.online.slices.3_others.filter", - value: "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)" + value: + "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)", }, { _id: "ui.overview.charts.online.slices.3_others.label", - value: "'Others'" + value: "'Others'", }, { _id: "ui.overview.groups.online.label", value: "''" }, - { _id: "ui.overview.groups.online.charts.0", value: "'online'" } + { _id: "ui.overview.groups.online.charts.0", value: "'online'" }, ]); } @@ -378,16 +379,16 @@ export async function seed(options): Promise { weight: 0, channel: "bootstrap", events: "0 BOOTSTRAP", - provision: "bootstrap" + provision: "bootstrap", }, { _id: "default", weight: 0, channel: "default", provision: "default" }, - { _id: "inform", weight: 0, channel: "inform", provision: "inform" } + { _id: "inform", weight: 0, channel: "inform", provision: "inform" }, ]; resources["provisions"] = [ { _id: "bootstrap", script: BOOTSTRAP_SCRIPT }, { _id: "default", script: DEFAULT_SCRIPT }, - { _id: "inform", script: INFORM_SCRIPT } + { _id: "inform", script: INFORM_SCRIPT }, ]; } diff --git a/lib/local-cache.ts b/lib/local-cache.ts index 29ff665..63e3206 100644 --- a/lib/local-cache.ts +++ b/lib/local-cache.ts @@ -36,7 +36,7 @@ import { Users, Permissions, Config, - UiConfig + UiConfig, } from "./types"; interface Snapshot { @@ -107,11 +107,15 @@ function computeHash(snapshot): string { return h.digest("hex"); } -function flattenObject(src, prefix = "", dst = {}): {} { +function flattenObject( + src: Record, + prefix = "", + dst = {} +): Record { for (const k of Object.keys(src)) { const v = src[k]; if (typeof v === "object" && !Array.isArray(v)) - flattenObject(v, `${prefix}${k}.`, dst); + flattenObject(v as Record, `${prefix}${k}.`, dst); else dst[`${prefix}${k}`] = v; } return dst; @@ -121,14 +125,14 @@ async function fetchPresets(): Promise { const res = await db.getPresets(); let objects = await db.getObjects(); - objects = objects.map(obj => { + objects = objects.map((obj) => { // Flatten object obj = flattenObject(obj); // If no keys are defined, consider all parameters as keys to keep the // same behavior from v1.0 if (!obj["_keys"] || !obj["_keys"].length) - obj["_keys"] = Object.keys(obj).filter(k => !k.startsWith("_")); + obj["_keys"] = Object.keys(obj).filter((k) => !k.startsWith("_")); return obj; }); @@ -145,12 +149,9 @@ async function fetchPresets(): Promise { if (preset["schedule"]) { const parts = preset["schedule"].trim().split(/\s+/); schedule = { - md5: crypto - .createHash("md5") - .update(preset["schedule"]) - .digest("hex"), + md5: crypto.createHash("md5").update(preset["schedule"]).digest("hex"), duration: null, - schedule: null + schedule: null, }; try { @@ -160,7 +161,7 @@ async function fetchPresets(): Promise { logger.warn({ message: "Invalid preset schedule", preset: preset["_id"], - schedule: preset["schedule"] + schedule: preset["schedule"], }); schedule.schedule = false; } @@ -208,7 +209,7 @@ async function fetchPresets(): Promise { for (const obj of objects) { if (obj["_id"] === c.object) { const alias = obj["_keys"] - .map(k => `${k}:${JSON.stringify(obj[k])}`) + .map((k) => `${k}:${JSON.stringify(obj[k])}`) .join(","); const p = `${c.name}.[${alias}]`; _provisions.push(["instances", p, 1]); @@ -226,7 +227,7 @@ async function fetchPresets(): Promise { for (const obj of objects) { if (obj["_id"] === c.object) { const alias = obj["_keys"] - .map(k => `${k}:${JSON.stringify(obj[k])}`) + .map((k) => `${k}:${JSON.stringify(obj[k])}`) .join(","); const p = `${c.name}.[${alias}]`; _provisions.push(["instances", p, 0]); @@ -246,7 +247,7 @@ async function fetchPresets(): Promise { schedule: schedule, events: events, precondition: precondition, - provisions: _provisions + provisions: _provisions, }); } @@ -263,7 +264,9 @@ async function fetchProvisions(): Promise { .createHash("md5") .update(r["script"]) .digest("hex"); - provisions[r["_id"]].script = new vm.Script( + provisions[ + r["_id"] + ].script = new vm.Script( `"use strict";(function(){\n${r["script"]}\n})();`, { filename: r["_id"], lineOffset: -1, timeout: 50 } ); @@ -282,7 +285,9 @@ async function fetchVirtualParameters(): Promise { .createHash("md5") .update(r["script"]) .digest("hex"); - virtualParameters[r["_id"]].script = new vm.Script( + virtualParameters[ + r["_id"] + ].script = new vm.Script( `"use strict";(function(){\n${r["script"]}\n})();`, { filename: r["_id"], lineOffset: -1, timeout: 50 } ); @@ -301,7 +306,7 @@ async function fetchPermissions(): Promise { permissions[p.role][p.access][p.resource] = { access: p.access, - filter: parse(p.filter || "true") + filter: parse(p.filter || "true"), }; if (p.validate) permissions[p.role][p.access][p.resource].validate = parse(p.validate); @@ -333,7 +338,7 @@ async function fetchUsers(): Promise { users[user._id] = { password: user.password, salt: user.salt, - roles: user.roles.split(",").map(s => s.trim()) + roles: user.roles.split(",").map((s) => s.trim()), }; } @@ -349,7 +354,7 @@ async function fetchConfig(): Promise<[Config, UiConfig]> { filters: {}, device: {}, index: {}, - overview: {} + overview: {}, }; const _config = {}; @@ -376,7 +381,7 @@ async function fetchConfig(): Promise<[Config, UiConfig]> { async function refresh(): Promise { if (!nextRefresh) { - await new Promise(resolve => setTimeout(resolve, 20)); + await new Promise((resolve) => setTimeout(resolve, 20)); await refresh(); return; } @@ -400,7 +405,7 @@ async function refresh(): Promise { fetchFiles(), fetchPermissions(), fetchUsers(), - fetchConfig() + fetchConfig(), ]); const snapshot = { @@ -411,7 +416,7 @@ async function refresh(): Promise { permissions: res[4], users: res[5], config: res[6][0], - ui: res[6][1] + ui: res[6][1], }; if (currentSnapshot) { @@ -435,29 +440,29 @@ export async function getCurrentSnapshot(): Promise { return currentSnapshot; } -export function hasSnapshot(hash): boolean { +export function hasSnapshot(hash: string): boolean { return snapshots.has(hash); } -export function getPresets(snapshotKey): Preset[] { +export function getPresets(snapshotKey: string): Preset[] { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.presets; } -export function getProvisions(snapshotKey): Provisions { +export function getProvisions(snapshotKey: string): Provisions { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.provisions; } -export function getVirtualParameters(snapshotKey): VirtualParameters { +export function getVirtualParameters(snapshotKey: string): VirtualParameters { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.virtualParameters; } -export function getFiles(snapshotKey): Files { +export function getFiles(snapshotKey: string): Files { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.files; @@ -466,7 +471,7 @@ export function getFiles(snapshotKey): Files { export function getConfig( snapshotKey: string, key: string, - context: {}, + context: Record, now: number, cb?: (Expression) => Expression ): string | number | boolean | null { @@ -488,7 +493,7 @@ export function getConfig( "CONNECTION_REQUEST_ALLOW_BASIC_AUTH", "cwmp.maxCommitIterations": "MAX_COMMIT_ITERATIONS", "cwmp.deviceOnlineThreshold": "DEVICE_ONLINE_THRESHOLD", - "cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT" + "cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT", }; if (!(key in snapshot.config)) { @@ -509,28 +514,31 @@ export function getConfig( return Array.isArray(v) ? null : v; } -export function getConfigExpression(snapshotKey, key): Expression { +export function getConfigExpression( + snapshotKey: string, + key: string +): Expression { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.config[key]; } -export function getUsers(snapshotKey): {} { +export function getUsers(snapshotKey: string): Users { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.users; } -export function getPermissions(snapshotKey): Permissions { +export function getPermissions(snapshotKey: string): Permissions { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); return snapshot.permissions; } -export function getUiConfig(snapshotKey): UiConfig { +export function getUiConfig(snapshotKey: string): UiConfig { const snapshot = snapshots.get(snapshotKey); if (!snapshot) throw new Error("Cache snapshot does not exist"); diff --git a/lib/logger.ts b/lib/logger.ts index 0a42874..57bfabc 100644 --- a/lib/logger.ts +++ b/lib/logger.ts @@ -22,6 +22,14 @@ import * as os from "os"; import * as config from "./config"; import { getRequestOrigin } from "./forwarded"; +import { + SessionContext, + AcsRequest, + CpeRequest, + CpeFault, + InformRequest, + Fault, +} from "./types"; const REOPEN_EVERY = 60000; @@ -67,7 +75,7 @@ function reopen(): void { if (!(stat && stat.dev === logStat.dev && stat.ino === logStat.ino)) { logStream.end(); logStream = fs.createWriteStream(null, { - fd: fs.openSync(LOG_FILE, "a") + fd: fs.openSync(LOG_FILE, "a"), }); logStat = fs.fstatSync(logStream.fd); } @@ -91,7 +99,7 @@ function reopen(): void { ) { accessLogStream.end(); accessLogStream = fs.createWriteStream(null, { - fd: fs.openSync(ACCESS_LOG_FILE, "a") + fd: fs.openSync(ACCESS_LOG_FILE, "a"), }); accessLogStat = fs.fstatSync(accessLogStream.fd); } @@ -105,7 +113,7 @@ function reopen(): void { setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref(); } -export function init(service, version): void { +export function init(service: string, version: string): void { defaultMeta.hostname = os.hostname(); defaultMeta.pid = process.pid; defaultMeta.name = `genieacs-${service}`; @@ -121,7 +129,7 @@ export function init(service, version): void { if (ACCESS_LOG_FILE) { accessLogStream = fs.createWriteStream(null, { - fd: fs.openSync(ACCESS_LOG_FILE, "a") + fd: fs.openSync(ACCESS_LOG_FILE, "a"), }); accessLogStat = fs.fstatSync(accessLogStream.fd); } @@ -147,62 +155,75 @@ export function close(): void { logStream.end(); } -export function flatten(details): {} { +export function flatten( + details: Record +): Record { if (details.sessionContext) { - details.deviceId = details.sessionContext.deviceId; + const sessionContext = details.sessionContext as SessionContext; + details.deviceId = sessionContext.deviceId; details.remoteAddress = getRequestOrigin( - details.sessionContext.httpRequest + sessionContext.httpRequest ).remoteAddress; delete details.sessionContext; } if (details.exception) { - details.exceptionName = details.exception.name; - details.exceptionMessage = details.exception.message; - details.exceptionStack = details.exception.stack; + const err = details.exception as Error; + details.exceptionName = err.name; + details.exceptionMessage = err.message; + details.exceptionStack = err.stack; delete details.exception; } if (details.task) { - details.taskId = details.task._id; + details.taskId = details.task["_id"]; delete details.task; } if (details.rpc) { - if (details.rpc.acsRequest) { - details.acsRequestId = details.rpc.id; - details.acsRequestName = details.rpc.acsRequest.name; - if (details.rpc.acsRequest.commandKey) - details.acsRequestCommandKey = details.rpc.acsRequest.commandKey; - } else if (details.rpc.cpeRequest) { - details.cpeRequestId = details.rpc.id; - if (details.rpc.cpeRequest.name === "Inform") { - details.informEvent = details.rpc.cpeRequest.event.join(","); - details.informRetryCount = details.rpc.cpeRequest.retryCount; + const rpc = details.rpc as { + id: string; + acsRequest?: AcsRequest; + cpeRequest?: CpeRequest; + cpeFault?: CpeFault; + }; + if (rpc.acsRequest) { + details.acsRequestId = rpc.id; + details.acsRequestName = rpc.acsRequest.name; + if (rpc.acsRequest["commandKey"]) + details.acsRequestCommandKey = rpc.acsRequest["commandKey"]; + } else if (rpc.cpeRequest) { + details.cpeRequestId = rpc.id; + if (rpc.cpeRequest.name === "Inform") { + details.informEvent = (rpc.cpeRequest as InformRequest).event.join(","); + details.informRetryCount = (rpc.cpeRequest as InformRequest).retryCount; } else { - details.cpeRequestName = details.rpc.cpeRequest.name; - if (details.rpc.cpeRequest.commandKey) - details.cpeRequestCommandKey = details.rpc.cpeRequest.commandKey; + details.cpeRequestName = rpc.cpeRequest.name; + if (rpc.cpeRequest["commandKey"]) + details.cpeRequestCommandKey = rpc.cpeRequest["commandKey"]; } - } else if (details.rpc.cpeFault) { - details.acsRequestId = details.rpc.id; - details.cpeFaultCode = details.rpc.cpeFault.detail.faultCode; - details.cpeFaultString = details.rpc.cpeFault.detail.faultString; + } else if (rpc.cpeFault) { + details.acsRequestId = rpc.id; + details.cpeFaultCode = rpc.cpeFault.detail.faultCode; + details.cpeFaultString = rpc.cpeFault.detail.faultString; } delete details.rpc; } if (details.fault) { - details.faultCode = details.fault.code; - details.faultMessage = details.fault.message; + const fault = details.fault as Fault; + details.faultCode = fault.code; + details.faultMessage = fault.message; delete details.fault; } // For genieacs-ui if (details.context) { - details.remoteAddress = getRequestOrigin(details.context.req).remoteAddress; - if (details.context.state.user) - details.user = details.context.state.user.username; + details.remoteAddress = getRequestOrigin( + details.context["req"] + ).remoteAddress; + if (details.context["state"].user) + details.user = details.context["state"].user.username; delete details.context; } @@ -212,7 +233,10 @@ export function flatten(details): {} { return details; } -function formatJson(details, systemd): string { +function formatJson( + details: Record, + systemd: boolean +): string { if (systemd) { let severity = ""; if (details.severity === "info") severity = "<6>"; @@ -225,14 +249,17 @@ function formatJson(details, systemd): string { return `${JSON.stringify(flatten(details))}${os.EOL}`; } -function formatSimple(details, systemd): string { +function formatSimple( + details: Record, + systemd: boolean +): string { const skip = { user: true, remoteAddress: true, severity: true, timestamp: true, message: true, - deviceId: !!details.sessionContext + deviceId: !!details.sessionContext, }; flatten(details); @@ -265,12 +292,14 @@ function formatSimple(details, systemd): string { return `${severity}${remote}${details.message}${meta}${os.EOL}`; } - return `${details.timestamp} [${details.severity.toUpperCase()}] ${remote}${ + return `${ + details.timestamp + } [${(details.severity as string).toUpperCase()}] ${remote}${ details.message }${meta}${os.EOL}`; } -function log(details): void { +function log(details: Record): void { details.timestamp = new Date().toISOString(); if (LOG_FORMAT === "json") { details = Object.assign({}, defaultMeta, details); @@ -280,22 +309,22 @@ function log(details): void { } } -export function info(details): void { +export function info(details: Record): void { details.severity = "info"; log(details); } -export function warn(details): void { +export function warn(details: Record): void { details.severity = "warn"; log(details); } -export function error(details): void { +export function error(details: Record): void { details.severity = "error"; log(details); } -export function accessLog(details): void { +export function accessLog(details: Record): void { details.timestamp = new Date().toISOString(); if (ACCESS_LOG_FORMAT === "json") { Object.assign(details, defaultMeta); @@ -305,17 +334,17 @@ export function accessLog(details): void { } } -export function accessInfo(details): void { +export function accessInfo(details: Record): void { details.severity = "info"; accessLog(details); } -export function accessWarn(details): void { +export function accessWarn(details: Record): void { details.severity = "warn"; accessLog(details); } -export function accessError(details): void { +export function accessError(details: Record): void { details.severity = "error"; accessLog(details); } diff --git a/lib/mongodb-functions.ts b/lib/mongodb-functions.ts index 788c0a3..bdd0a09 100644 --- a/lib/mongodb-functions.ts +++ b/lib/mongodb-functions.ts @@ -24,8 +24,8 @@ import { Expression, Fault, Task } from "./types"; const isArray = Array.isArray; -export function processDeviceFilter(filter): Expression { - return map(filter, exp => { +export function processDeviceFilter(filter: Expression): Expression { + return map(filter, (exp) => { if (!isArray(exp)) return exp; if (exp[0] === "PARAM") { @@ -61,10 +61,10 @@ export function processDeviceFilter(filter): Expression { "LIKE", "NOT LIKE", "IS NULL", - "IS NOT NULL" + "IS NOT NULL", ].includes(exp[0]) ) { - let e = map(exp, ee => { + let e = map(exp, (ee) => { if ( isArray(ee) && ee[0] === "PARAM" && @@ -86,8 +86,8 @@ export function processDeviceFilter(filter): Expression { }); } -export function processTasksFilter(filter): Expression { - return map(filter, exp => { +export function processTasksFilter(filter: Expression): Expression { + return map(filter, (exp) => { if (!isArray(exp)) return exp; if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { const e = exp.slice(); @@ -102,8 +102,8 @@ export function processTasksFilter(filter): Expression { }); } -export function processFaultsFilter(filter): Expression { - return map(filter, exp => { +export function processFaultsFilter(filter: Expression): Expression { + return map(filter, (exp) => { if (!isArray(exp)) return exp; if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { const e = exp.slice(); @@ -116,7 +116,7 @@ export function processFaultsFilter(filter): Expression { }); } -export function filterToMongoQuery(exp: Expression): {} { +export function filterToMongoQuery(exp: Expression): Record { const ops = { OR: 0, AND: 0, @@ -130,10 +130,10 @@ export function filterToMongoQuery(exp: Expression): {} { LIKE: 1, "NOT LIKE": 1, "IS NULL": 1, - "IS NOT NULL": 1 + "IS NOT NULL": 1, }; - function recursive(filter, negate, res = {}): {} { + function recursive(filter, negate, res = {}): Record { const op = filter[0]; if (ops[op] === 0) { @@ -249,7 +249,9 @@ export function filterToMongoQuery(exp: Expression): {} { return recursive(_exp, false); } -export function processDeviceProjection(projection: {}): {} { +export function processDeviceProjection( + projection: Record +): Record { if (!projection) return projection; const p = {}; for (const [k, v] of Object.entries(projection)) { @@ -275,7 +277,9 @@ export function processDeviceProjection(projection: {}): {} { return p; } -export function processDeviceSort(sort: {}): {} { +export function processDeviceSort( + sort: Record +): Record { if (!sort) return sort; const s = {}; for (const [k, v] of Object.entries(sort)) { @@ -291,7 +295,7 @@ export function processDeviceSort(sort: {}): {} { return s; } -function parseDate(d): number | string { +function parseDate(d: Date): number | string { const n = +d; return isNaN(n) ? "" + d : n; } @@ -307,45 +311,45 @@ interface FlatDevice { }; } -export function flattenDevice(device): FlatDevice { +export function flattenDevice(device: Record): FlatDevice { function recursive(input, root, output, timestamp): void { for (const [name, tree] of Object.entries(input)) { if (!root) { if (name === "_lastInform") { output["Events.Inform"] = { - value: [parseDate(tree), "xsd:dateTime"], + value: [parseDate(tree as Date), "xsd:dateTime"], valueTimestamp: timestamp, writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_registered") { output["Events.Registered"] = { - value: [parseDate(tree), "xsd:dateTime"], + value: [parseDate(tree as Date), "xsd:dateTime"], valueTimestamp: timestamp, writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_lastBoot") { output["Events.1_BOOT"] = { - value: [parseDate(tree), "xsd:dateTime"], + value: [parseDate(tree as Date), "xsd:dateTime"], valueTimestamp: timestamp, writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_lastBootstrap") { output["Events.0_BOOTSTRAP"] = { - value: [parseDate(tree), "xsd:dateTime"], + value: [parseDate(tree as Date), "xsd:dateTime"], valueTimestamp: timestamp, writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_id") { output["DeviceID.ID"] = { @@ -354,7 +358,7 @@ export function flattenDevice(device): FlatDevice { writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_deviceId") { output["DeviceID.Manufacturer"] = { @@ -363,7 +367,7 @@ export function flattenDevice(device): FlatDevice { writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; output["DeviceID.OUI"] = { value: [tree["_OUI"], "xsd:string"], @@ -371,7 +375,7 @@ export function flattenDevice(device): FlatDevice { writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; output["DeviceID.ProductClass"] = { value: [tree["_ProductClass"], "xsd:string"], @@ -379,7 +383,7 @@ export function flattenDevice(device): FlatDevice { writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; output["DeviceID.SerialNumber"] = { value: [tree["_SerialNumber"], "xsd:string"], @@ -387,14 +391,14 @@ export function flattenDevice(device): FlatDevice { writable: false, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } else if (name === "_tags") { output["Tags"] = { writable: false, writableTimestamp: timestamp, object: true, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; for (const t of tree as string[]) { @@ -404,7 +408,7 @@ export function flattenDevice(device): FlatDevice { writable: true, writableTimestamp: timestamp, object: false, - objectTimestamp: timestamp + objectTimestamp: timestamp, }; } } @@ -422,7 +426,7 @@ export function flattenDevice(device): FlatDevice { if (tree["_value"] != null) { attrs["value"] = [ tree["_value"] instanceof Date ? +tree["_value"] : tree["_value"], - tree["_type"] + tree["_type"], ]; attrs["valueTimestamp"] = +(tree["_timestamp"] || childrenTimestamp); attrs["object"] = false; @@ -446,27 +450,27 @@ export function flattenDevice(device): FlatDevice { } const newDevice = {}; - const timestamp = new Date(device["_lastInform"] || 1).getTime(); + const timestamp = new Date((device["_lastInform"] as Date) || 1).getTime(); recursive(device, "", newDevice, timestamp); return newDevice; } -export function flattenFault(fault): Fault { - const f = Object.assign({}, fault); +export function flattenFault(fault: unknown): Fault { + const f = Object.assign({}, fault) as Fault; if (f.timestamp) f.timestamp = +f.timestamp; - if (f.expiry) f.expiry = +f.expiry; - return f; + if (f["expiry"]) f["expiry"] = +f["expiry"]; + return f as Fault; } -export function flattenTask(task): Task { - const t = Object.assign({}, task); +export function flattenTask(task: unknown): Task { + const t = Object.assign({}, task) as Task; t._id = "" + t._id; - if (t.timestamp) t.timestamp = +t.timestamp; + if (t["timestamp"]) t["timestamp"] = +t["timestamp"]; if (t.expiry) t.expiry = +t.expiry; return t; } -export function mongoQueryToFilter(query): Expression { +export function mongoQueryToFilter(query: Record): Expression { function recursive(_query): Expression { const expressions: Expression[] = []; for (const [k, v] of Object.entries(_query)) { @@ -554,15 +558,19 @@ export function mongoQueryToFilter(query): Expression { return recursive(query); } -export function flattenPreset(preset): {} { +export function flattenPreset( + preset: Record +): Record { const p = Object.assign({}, preset); if (p.precondition) { try { // Try parse to check expression validity - parse(p.precondition); + parse(p.precondition as string); } catch (error) { - p.precondition = mongoQueryToFilter(JSON.parse(p.precondition)); - p.precondition = p.precondition.length ? stringify(p.precondition) : ""; + p.precondition = mongoQueryToFilter(JSON.parse(p.precondition as string)); + p.precondition = (p.precondition as string).length + ? stringify(p.precondition as Expression) + : ""; } } @@ -574,7 +582,7 @@ export function flattenPreset(preset): {} { const provision = p.configurations[0]; if ( - p.configurations.length === 1 && + (p.configurations as any[]).length === 1 && provision.type === "provision" && provision.name && provision.name.length @@ -589,30 +597,34 @@ export function flattenPreset(preset): {} { return p; } -export function flattenFile(file): {} { +export function flattenFile( + file: Record +): Record { const f = {}; f["_id"] = file["_id"]; if (file.metadata) { - f["metadata.fileType"] = file.metadata.fileType || ""; - f["metadata.oui"] = file.metadata.oui || ""; - f["metadata.productClass"] = file.metadata.productClass || ""; - f["metadata.version"] = file.metadata.version || ""; + f["metadata.fileType"] = file["metadata"]["fileType"] || ""; + f["metadata.oui"] = file["metadata"]["oui"] || ""; + f["metadata.productClass"] = file["metadata"]["productClass"] || ""; + f["metadata.version"] = file["metadata"]["version"] || ""; } return f; } -export function preProcessPreset(data): {} { +export function preProcessPreset( + data: Record +): Record { const preset = Object.assign({}, data); if (!preset.precondition) preset.precondition = ""; // Try parse to check expression validity - parse(preset.precondition); + parse(preset.precondition as string); - preset.weight = parseInt(preset.weight) || 0; + preset.weight = parseInt(preset.weight as string) || 0; const events = {}; if (preset.events) { - for (let e of preset.events.split(",")) { + for (let e of (preset.events as string).split(",")) { let v = true; e = e.trim(); if (e.startsWith("-")) { @@ -630,7 +642,7 @@ export function preProcessPreset(data): {} { const configuration = { type: "provision", name: preset.provision, - args: null + args: null, }; if (preset.provisionArgs) diff --git a/lib/nbi.ts b/lib/nbi.ts index e604999..2a86d31 100644 --- a/lib/nbi.ts +++ b/lib/nbi.ts @@ -25,6 +25,7 @@ import * as config from "./config"; import * as db from "./db"; import * as query from "./query"; import * as apiFunctions from "./api-functions"; +import { IncomingMessage, ServerResponse } from "http"; import * as cache from "./cache"; import { version as VERSION } from "../package.json"; import { ping } from "./ping"; @@ -42,7 +43,7 @@ const PROVISIONS_REGEX = /^\/provisions\/([a-zA-Z0-9\-_%]+)\/?$/; const VIRTUAL_PARAMETERS_REGEX = /^\/virtual_parameters\/([a-zA-Z0-9\-_%]+)\/?$/; const FAULTS_REGEX = /^\/faults\/([a-zA-Z0-9\-_%:]+)\/?$/; -function throwError(err, httpResponse?): never { +function throwError(err: Error, httpResponse?: ServerResponse): never { if (httpResponse) { httpResponse.writeHead(500, { Connection: "close" }); httpResponse.end(`${err.name}: ${err.message}`); @@ -50,7 +51,10 @@ function throwError(err, httpResponse?): never { throw err; } -export function listener(request, response): void { +export function listener( + request: IncomingMessage, + response: ServerResponse +): void { const chunks = []; let bytes = 0; response.setHeader("GenieACS-Version", VERSION); @@ -85,7 +89,7 @@ export function listener(request, response): void { { _id: presetName }, preset, { upsert: true }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -94,7 +98,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -102,7 +106,7 @@ export function listener(request, response): void { } ); } else if (request.method === "DELETE") { - db.presetsCollection.deleteOne({ _id: presetName }, err => { + db.presetsCollection.deleteOne({ _id: presetName }, (err) => { if (err) return void throwError(err, response); cache @@ -111,7 +115,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -132,7 +136,7 @@ export function listener(request, response): void { { _id: objectName }, object, { upsert: true }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -141,7 +145,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -149,7 +153,7 @@ export function listener(request, response): void { } ); } else if (request.method === "DELETE") { - db.objectsCollection.deleteOne({ _id: objectName }, err => { + db.objectsCollection.deleteOne({ _id: objectName }, (err) => { if (err) return void throwError(err, response); cache @@ -158,7 +162,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -175,7 +179,7 @@ export function listener(request, response): void { if (request.method === "PUT") { const object = { _id: provisionName, - script: body.toString() + script: body.toString(), }; try { @@ -190,7 +194,7 @@ export function listener(request, response): void { { _id: provisionName }, object, { upsert: true }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -199,7 +203,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -207,7 +211,7 @@ export function listener(request, response): void { } ); } else if (request.method === "DELETE") { - db.provisionsCollection.deleteOne({ _id: provisionName }, err => { + db.provisionsCollection.deleteOne({ _id: provisionName }, (err) => { if (err) return void throwError(err, response); cache @@ -216,7 +220,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -233,7 +237,7 @@ export function listener(request, response): void { if (request.method === "PUT") { const object = { _id: virtualParameterName, - script: body.toString() + script: body.toString(), }; try { @@ -248,7 +252,7 @@ export function listener(request, response): void { { _id: virtualParameterName }, object, { upsert: true }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -257,7 +261,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -267,7 +271,7 @@ export function listener(request, response): void { } else if (request.method === "DELETE") { db.virtualParametersCollection.deleteOne( { _id: virtualParameterName }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -276,7 +280,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -295,7 +299,7 @@ export function listener(request, response): void { db.devicesCollection.updateOne( { _id: deviceId }, { $addToSet: { _tags: tag } }, - err => { + (err) => { if (err) return void throwError(err, response); response.writeHead(200); response.end(); @@ -305,7 +309,7 @@ export function listener(request, response): void { db.devicesCollection.updateOne( { _id: deviceId }, { $pull: { _tags: tag } }, - err => { + (err) => { if (err) return void throwError(err, response); response.writeHead(200); @@ -323,12 +327,12 @@ export function listener(request, response): void { ); const deviceId = faultId.split(":", 1)[0]; const channel = faultId.slice(deviceId.length + 1); - db.faultsCollection.deleteOne({ _id: faultId }, err => { + db.faultsCollection.deleteOne({ _id: faultId }, (err) => { if (err) return void throwError(err, response); if (channel.startsWith("task_")) { const objId = new mongodb.ObjectID(channel.slice(5)); - return void db.tasksCollection.deleteOne({ _id: objId }, err => { + return void db.tasksCollection.deleteOne({ _id: objId }, (err) => { if (err) return void throwError(err, response); cache @@ -337,7 +341,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -351,7 +355,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -382,17 +386,20 @@ export function listener(request, response): void { const taskTimeout = (urlParts.query.timeout && parseInt(urlParts.query.timeout as string)) || - config.get("DEVICE_ONLINE_THRESHOLD", deviceId); + (config.get( + "DEVICE_ONLINE_THRESHOLD", + deviceId + ) as number); apiFunctions .watchTask(deviceId, task._id, taskTimeout) - .then(status => { + .then((status) => { if (status === "timeout") { response.writeHead( 202, "Task queued but not processed", { - "Content-Type": "application/json" + "Content-Type": "application/json", } ); response.end(JSON.stringify(task)); @@ -404,44 +411,44 @@ export function listener(request, response): void { return void throwError(err, response); response.writeHead(202, "Task faulted", { - "Content-Type": "application/json" + "Content-Type": "application/json", }); response.end(JSON.stringify(task2)); } ); } else { response.writeHead(200, { - "Content-Type": "application/json" + "Content-Type": "application/json", }); response.end(JSON.stringify(task)); } }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); }); }) - .catch(err => { + .catch((err) => { response.writeHead(202, err.message, { - "Content-Type": "application/json" + "Content-Type": "application/json", }); response.end(JSON.stringify(task)); }); } else { response.writeHead(202, { - "Content-Type": "application/json" + "Content-Type": "application/json", }); response.end(JSON.stringify(task)); } }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); }); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -454,7 +461,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { response.writeHead(504); response.end(`${err.name}: ${err.message}`); }); @@ -487,12 +494,12 @@ export function listener(request, response): void { const deviceId = task.device; db.tasksCollection.deleteOne( { _id: new mongodb.ObjectID(taskId) }, - err => { + (err) => { if (err) return void throwError(err, response); db.faultsCollection.deleteOne( { _id: `${deviceId}:task_${taskId}` }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -501,7 +508,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -527,7 +534,7 @@ export function listener(request, response): void { const deviceId = task.device; db.faultsCollection.deleteOne( { _id: `${deviceId}:task_${taskId}` }, - err => { + (err) => { if (err) return void throwError(err, response); cache @@ -536,7 +543,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -562,7 +569,7 @@ export function listener(request, response): void { fileType: request.headers.filetype, oui: request.headers.oui, productClass: request.headers.productclass, - version: request.headers.version + version: request.headers.version, }; const bucket = new mongodb.GridFSBucket(db.client.db()); bucket.delete((filename as unknown) as mongodb.ObjectId, () => { @@ -570,11 +577,11 @@ export function listener(request, response): void { filename, filename, { - metadata: metadata + metadata: metadata, } ); - uploadStream.on("error", err => { + uploadStream.on("error", (err) => { throwError(err, response); }); @@ -585,7 +592,7 @@ export function listener(request, response): void { }); } else if (request.method === "DELETE") { const bucket = new mongodb.GridFSBucket(db.client.db()); - bucket.delete((filename as unknown) as mongodb.ObjectId, err => { + bucket.delete((filename as unknown) as mongodb.ObjectId, (err) => { if (err) { if (err.message.startsWith("FileNotFound")) { response.writeHead(404); @@ -618,7 +625,7 @@ export function listener(request, response): void { response.writeHead(200, { "Content-Type": "text/plain", - "Cache-Control": "no-cache" + "Cache-Control": "no-cache", }); response.end(stdout); }); @@ -638,7 +645,7 @@ export function listener(request, response): void { response.writeHead(200); response.end(); }) - .catch(err => { + .catch((err) => { setTimeout(() => { throwError(err, response); }); @@ -686,15 +693,15 @@ export function listener(request, response): void { break; case "tasks": q = query.sanitizeQueryTypes(q, { - _id: v => new mongodb.ObjectID(v), - timestamp: v => new Date(v), - retries: Number + _id: (v) => new mongodb.ObjectID(v as string), + timestamp: (v) => new Date(v as number), + retries: Number, }); break; case "faults": q = query.sanitizeQueryTypes(q, { - timestamp: v => new Date(v), - retries: Number + timestamp: (v) => new Date(v as number), + retries: Number, }); } @@ -730,7 +737,7 @@ export function listener(request, response): void { response.writeHead(200, { "Content-Type": "application/json", - total: total + total: total, }); if (request.method === "HEAD") { @@ -741,11 +748,11 @@ export function listener(request, response): void { response.write("[\n"); i = 0; cur.forEach( - item => { + (item) => { if (i++) response.write(",\n"); response.write(JSON.stringify(item)); }, - err => { + (err) => { if (err) return void throwError(err); response.end("\n]"); } diff --git a/lib/ping.ts b/lib/ping.ts index 216cecc..e34a265 100644 --- a/lib/ping.ts +++ b/lib/ping.ts @@ -20,7 +20,7 @@ import { platform } from "os"; import { exec } from "child_process"; -interface Ping { +export interface PingResult { packetsTransmitted: number; packetsReceived: number; packetLoss: number; @@ -32,7 +32,7 @@ interface Ping { export function ping( host: string, - callback: (err, res?, stdout?) => void + callback: (err: Error, res?: PingResult, stdout?: string) => void ): void { let cmd: string, parseRegExp1: RegExp, parseRegExp2: RegExp; switch (platform()) { @@ -55,7 +55,7 @@ export function ping( } exec(cmd, (err, stdout) => { - let parsed: Ping; + let parsed: PingResult; if (stdout) { const m1 = stdout.match(parseRegExp1); if (m1) { @@ -66,7 +66,7 @@ export function ping( min: +m1[4], avg: +m1[5], max: +m1[6], - mdev: +m1[7] + mdev: +m1[7], }; } else { const m2 = stdout.match(parseRegExp2); @@ -78,7 +78,7 @@ export function ping( min: null, avg: null, max: null, - mdev: null + mdev: null, }; } } diff --git a/lib/query.ts b/lib/query.ts index 558a87c..8e3eda4 100644 --- a/lib/query.ts +++ b/lib/query.ts @@ -17,7 +17,9 @@ * along with GenieACS. If not, see . */ -import * as common from "./common"; +function isObject(obj: any): boolean { + return Object.prototype.toString.call(obj) === "[object Object]"; +} function stringToRegexp(input, flags?): RegExp | false { if (input.indexOf("*") === -1) return false; @@ -34,8 +36,8 @@ function stringToRegexp(input, flags?): RegExp | false { } function normalize(input): any { - if (common.typeOf(input) === common.STRING_TYPE) { - const vals = [input]; + if (typeof input === "string") { + const vals: any = [input]; const m = /^\/(.*?)\/(g?i?m?y?)$/.exec(input); if (m) vals.push({ $regex: new RegExp(m[1], m[2]) }); @@ -52,14 +54,14 @@ function normalize(input): any { return input; } -export function expandValue(value): any[] { - if (common.typeOf(value) === common.ARRAY_TYPE) { +function expandValue(value: unknown): unknown[] { + if (Array.isArray(value)) { let a = []; for (const j of value) a = a.concat(expandValue(j)); return [a]; - } else if (common.typeOf(value) !== common.OBJECT_TYPE) { + } else if (!isObject(value)) { const n = normalize(value); - if (common.typeOf(n) !== common.ARRAY_TYPE) return [n]; + if (!Array.isArray(n)) return [n]; else return n; } @@ -103,12 +105,14 @@ function permute(param, val): any[] { return conditions; } -export function expand(query): {} { +export function expand( + query: Record +): Record { const newQuery = {}; for (const [k, v] of Object.entries(query)) { if (k[0] === "$") { // Operator - newQuery[k] = (v as any[]).map(e => expand(e)); + newQuery[k] = (v as any[]).map((e) => expand(e)); } else { const conditions = permute(k, v); if (conditions.length > 1) { @@ -129,100 +133,16 @@ export function expand(query): {} { return newQuery; } -function testExpressions(params, expressions, lop): boolean { - for (const f of expressions) { - const res = test(params, f); - switch (lop) { - case "$and": - if (!res) return false; - break; - case "$or": - if (res) return true; - break; - case "$nor": - if (res) return false; - } - } - - switch (lop) { - case "$and": - return true; - case "$or": - return false; - case "$nor": - return true; - default: - throw new Error("Unknown logical operator"); - } -} - -export function test(params, query): boolean { - let res; - for (const [k, v] of Object.entries(query)) { - if (k.charAt(0) === "$") { - // Logical operator - res = testExpressions(params, v, k); - } else { - const value = params[k]; - if (common.typeOf(v) !== common.OBJECT_TYPE) { - if (common.typeOf(value) === common.ARRAY_TYPE) { - // TODO comparing array to regex, array to array, and object to object - res = value.indexOf(v) !== -1; - } else { - if (common.typeOf(v) === common.REGEXP_TYPE) - res = (v as RegExp).test(value); - else res = v === value; - } - } else { - for (const [k2, v2] of Object.entries(v)) { - switch (k2) { - case "$ne": - if (common.typeOf(value) === common.ARRAY_TYPE) - res = value.indexOf(v2) === -1; - else res = value !== v2; - break; - case "$lt": - res = value < v2; - break; - case "$lte": - res = value <= v2; - break; - case "$gt": - res = value > v2; - break; - case "$gte": - res = value >= v2; - break; - case "$regex": - res = v2.test(value); - break; - case "$in": - throw new Error("Operator not supported"); - case "$nin": - throw new Error("Operator not supported"); - case "$all": - throw new Error("Operator not supported"); - case "$exists": - throw new Error("Operator not supported"); - default: - throw new Error("Operator not supported"); - } - } - } - } - - if (!res) return false; - } - return true; -} - -export function sanitizeQueryTypes(query, types): {} { +export function sanitizeQueryTypes( + query: Record, + types: Record unknown> +): Record { for (const [k, v] of Object.entries(query)) { if (k[0] === "$") { // Logical operator for (const vv of v as any[]) sanitizeQueryTypes(vv, types); } else if (k in types) { - if (common.typeOf(v) === common.OBJECT_TYPE) { + if (isObject(v)) { for (const [kk, vv] of Object.entries(v)) { switch (kk) { case "$in": diff --git a/lib/sandbox.ts b/lib/sandbox.ts index 40f0a59..c197301 100644 --- a/lib/sandbox.ts +++ b/lib/sandbox.ts @@ -127,7 +127,7 @@ function random(): number { return state.rng(); } -random.seed = function(s) { +random.seed = function (s) { state.rng = seedrandom(s); }; @@ -135,7 +135,7 @@ class ParameterWrapper { public constructor(path: Path, attributes, unpacked?, unpackedRevision?) { for (const attrName of attributes) { Object.defineProperty(this, attrName, { - get: function() { + get: function () { if (state.uncommitted) commit(); if (state.revision !== unpackedRevision) { @@ -157,12 +157,12 @@ class ParameterWrapper { if (!attr) return UNDEFINED; return attr[1]; - } + }, }); } Object.defineProperty(this, "path", { - get: function() { + get: function () { if (state.uncommitted) commit(); if (state.revision !== unpackedRevision) { @@ -177,11 +177,11 @@ class ParameterWrapper { if (!unpacked.length) return UNDEFINED; return unpacked[0].toString(); - } + }, }); Object.defineProperty(this, "size", { - get: function() { + get: function () { if (state.uncommitted) commit(); if (state.revision !== unpackedRevision) { @@ -196,10 +196,10 @@ class ParameterWrapper { if (!unpacked.length) return UNDEFINED; return unpacked.length; - } + }, }); - this[Symbol.iterator] = function*() { + this[Symbol.iterator] = function* () { if (state.uncommitted) commit(); if (state.revision !== unpackedRevision) { @@ -235,7 +235,7 @@ function declare( pathSet: null, attrGet: null, attrSet: null, - defer: true + defer: true, }; const attrs = new Set(); @@ -290,9 +290,9 @@ function commit(): void { } } -function ext(): any { +function ext(...args: unknown[]): any { ++state.extCounter; - const extCall = Array.from(arguments).map(String); + const extCall = args.map(String); const key = `${state.revision}: ${JSON.stringify(extCall)}`; if (key in state.sessionContext.extensionsCache) @@ -302,11 +302,11 @@ function ext(): any { throw EXT; } -function log(msg: string, meta: {}): void { +function log(msg: string, meta: Record): void { if (state.revision === state.maxRevision && state.extCounter >= 0) { const details = Object.assign({}, meta, { sessionContext: state.sessionContext, - message: `Script: ${msg}` + message: `Script: ${msg}`, }); delete details["hostname"]; @@ -342,8 +342,8 @@ function errorToFault(err: Error): Fault { message: err.message, detail: { name: err.name, - message: err.message - } + message: err.message, + }, }; if (err.stack) { @@ -365,7 +365,7 @@ function errorToFault(err: Error): Fault { export async function run( script: vm.Script, - globals: {}, + globals: Record, sessionContext: SessionContext, startRevision: number, maxRevision: number, @@ -380,7 +380,7 @@ export async function run( extensions: {}, clear: [], rng: null, - extCounter: extCounter + extCounter: extCounter, }; for (const n of Object.keys(context)) delete context[n]; @@ -403,7 +403,7 @@ export async function run( clear: null, declare: null, done: false, - returnValue: null + returnValue: null, }; } } @@ -423,7 +423,7 @@ export async function run( clear: null, declare: null, done: false, - returnValue: null + returnValue: null, }; } @@ -443,6 +443,6 @@ export async function run( clear: _state.clear, declare: _state.declarations, done: status === 0, - returnValue: ret + returnValue: ret, }; } diff --git a/lib/scheduling.ts b/lib/scheduling.ts index 68a63a5..d63e800 100644 --- a/lib/scheduling.ts +++ b/lib/scheduling.ts @@ -21,10 +21,7 @@ import * as crypto from "crypto"; import * as later from "later"; function md532(str): number { - const digest = crypto - .createHash("md5") - .update(str) - .digest(); + const digest = crypto.createHash("md5").update(str).digest(); return ( digest.readUInt32LE(0) ^ digest.readUInt32LE(4) ^ @@ -33,29 +30,37 @@ function md532(str): number { ); } -export function variance(deviceId, vrnc): number { +export function variance(deviceId: string, vrnc: number): number { return (md532(deviceId) >>> 0) % vrnc; } -export function interval(timestamp, intrvl, offset = 0): number { +export function interval( + timestamp: number, + intrvl: number, + offset = 0 +): number { return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset; } -export function parseCron(cronExp): any { +export function parseCron(cronExp: string): any { const parts = cronExp.trim().split(/\s+/); if (parts.length === 5) parts.unshift("*"); return later.schedule(later.parse.cron(parts.join(" "), true)); } -export function cron(timestamp, schedule, offset = 0): number[] { +export function cron( + timestamp: number, + schedule: unknown, + offset = 0 +): number[] { // TODO later.js doesn't throw erorr if expression is invalid! const ret = [0, 0]; - const prev = schedule.prev(1, new Date(timestamp + offset)); + const prev = (schedule as any).prev(1, new Date(timestamp + offset)); if (prev) ret[0] = prev.setMilliseconds(0) - offset; - const next = schedule.next(1, new Date(timestamp + offset + 1000)); + const next = (schedule as any).next(1, new Date(timestamp + offset + 1000)); if (next) ret[1] = next.setMilliseconds(0) - offset; return ret; diff --git a/lib/server.ts b/lib/server.ts index d5aae82..f8a662e 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -20,6 +20,7 @@ import * as fs from "fs"; import * as http from "http"; import * as https from "https"; +import { Socket } from "net"; import * as path from "path"; import { ROOT_DIR } from "./config"; @@ -52,12 +53,12 @@ function closeServer(timeout, callback): void { } export function start( - port, - networkInterface, - ssl, - _listener, - onConnection?, - keepAliveTimeout: number = -1 + port: number, + networkInterface: string, + ssl: { key: string; cert: string }, + _listener: http.RequestListener, + onConnection?: (socket: Socket) => void, + keepAliveTimeout = -1 ): void { listener = _listener; @@ -65,10 +66,10 @@ export function start( const options = { key: ssl.key .split(":") - .map(f => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))), + .map((f) => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))), cert: ssl.cert .split(":") - .map(f => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))) + .map((f) => fs.readFileSync(path.resolve(ROOT_DIR, f.trim()))), }; server = https.createServer(options, listener); diff --git a/lib/session.ts b/lib/session.ts index 50312ee..e9ba5f6 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -48,7 +48,7 @@ import { TransferCompleteRequest, Operation, ScriptResult, - Expression + Expression, } from "./types"; import { getRequestOrigin } from "./forwarded"; @@ -59,7 +59,7 @@ const VALID_PARAM_TYPES = new Set([ "xsd:string", "xsd:dateTime", "xsd:base64", - "xsd:hexBinary" + "xsd:hexBinary", ]); function initDeviceData(): DeviceData { @@ -68,7 +68,7 @@ function initDeviceData(): DeviceData { timestamps: new VersionedMap(), attributes: new VersionedMap(), trackers: new Map(), - changes: new Set() + changes: new Set(), }; } @@ -94,7 +94,7 @@ export function init( extensionsCache: {}, declarations: [], state: 0, - authState: 0 + authState: 0, }; return sessionContext; @@ -148,8 +148,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [rpcReq.deviceId.Manufacturer, "xsd:string"]] - } + value: [timestamp, [rpcReq.deviceId.Manufacturer, "xsd:string"]], + }, ], [ @@ -158,8 +158,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [rpcReq.deviceId.OUI, "xsd:string"]] - } + value: [timestamp, [rpcReq.deviceId.OUI, "xsd:string"]], + }, ], [ @@ -168,8 +168,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [rpcReq.deviceId.ProductClass, "xsd:string"]] - } + value: [timestamp, [rpcReq.deviceId.ProductClass, "xsd:string"]], + }, ], [ @@ -178,9 +178,9 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [rpcReq.deviceId.SerialNumber, "xsd:string"]] - } - ] + value: [timestamp, [rpcReq.deviceId.SerialNumber, "xsd:string"]], + }, + ], ]; for (const p of rpcReq.parameterList) { @@ -190,8 +190,8 @@ export async function inform( timestamp, { object: [timestamp, 0], - value: [timestamp, p.slice(1) as [string | number | boolean, string]] - } + value: [timestamp, p.slice(1) as [string | number | boolean, string]], + }, ]); } @@ -201,8 +201,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]] - } + value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]], + }, ]); for (const e of rpcReq.event) { @@ -212,8 +212,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]] - } + value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]], + }, ]); } @@ -224,8 +224,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [sessionContext.deviceId, "xsd:string"]] - } + value: [timestamp, [sessionContext.deviceId, "xsd:string"]], + }, ]); params.push([ Path.parse("Events.Registered"), @@ -233,8 +233,8 @@ export async function inform( { object: [timestamp, 0], writable: [timestamp, 0], - value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]] - } + value: [timestamp, [sessionContext.timestamp, "xsd:dateTime"]], + }, ]); } @@ -280,7 +280,7 @@ export async function transferComplete( return { acsResponse: { name: "TransferCompleteResponse" }, operation: null, - fault: null + fault: null, }; } @@ -297,13 +297,13 @@ export async function transferComplete( code: `cwmp.${rpcReq.faultStruct.faultCode}`, message: rpcReq.faultStruct.faultString, detail: rpcReq.faultStruct, - timestamp: operation.timestamp + timestamp: operation.timestamp, }; return { acsResponse: { name: "TransferCompleteResponse" }, operation: operation, - fault: fault + fault: fault, }; } @@ -386,7 +386,7 @@ export async function transferComplete( return { acsResponse: { name: "TransferCompleteResponse" }, operation: operation, - fault: null + fault: null, }; } @@ -413,9 +413,9 @@ async function revertDownloadParameters( timestamp, [ lastDownload && lastDownload.value[1] ? lastDownload.value[1][0] : 0, - "xsd:dateTime" - ] - ] + "xsd:dateTime", + ], + ], }); if (toClear) { @@ -446,7 +446,7 @@ export async function timeoutOperations( "cwmp.downloadTimeout", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ) * 1000; if (sessionContext.timestamp > operation.timestamp + DOWNLOAD_TIMEOUT) { @@ -458,7 +458,7 @@ export async function timeoutOperations( faults.push({ code: "timeout", message: "Download operation timed out", - timestamp: operation.timestamp + timestamp: operation.timestamp, }); operations.push(operation); @@ -472,8 +472,8 @@ export async function timeoutOperations( export function addProvisions( sessionContext: SessionContext, - channel, - provisions + channel: string, + provisions: (string | number | boolean)[][] ): void { // Multiply by two because every iteration is two // phases: read and update @@ -483,7 +483,7 @@ export function addProvisions( "cwmp.maxCommitIterations", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ) * 2; delete sessionContext.syncState; @@ -538,7 +538,7 @@ export function clearProvisions(sessionContext: SessionContext): void { "cwmp.maxCommitIterations", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ) * 2; if (sessionContext.revisions[sessionContext.revisions.length - 1] > 0) { @@ -572,7 +572,7 @@ async function runProvisions( const allProvisions = localCache.getProvisions(sessionContext.cacheSnapshot); const res = await Promise.all( - provisions.map(async provision => { + provisions.map(async (provision) => { if (!allProvisions[provision[0]]) { if (defaultProvisions[provision[0]]) { const dec = []; @@ -593,8 +593,8 @@ async function runProvisions( detail: { name: err.name, message: err.message, - stack: `${err.name}: ${err.message}\n at ${provision[0]}` - } + stack: `${err.name}: ${err.message}\n at ${provision[0]}`, + }, }; } return { @@ -602,7 +602,7 @@ async function runProvisions( clear: null, declare: dec, done: done, - returnValue: null + returnValue: null, }; } return null; @@ -638,7 +638,7 @@ async function runProvisions( clear: allClear, declare: allDeclarations, done: done, - returnValue: null + returnValue: null, }; } @@ -653,9 +653,9 @@ async function runVirtualParameters( ); const res = await Promise.all( - provisions.map(async provision => { + provisions.map(async (provision) => { const globals = { - args: provision.slice(1) + args: provision.slice(1), }; const r = await sandbox.run( @@ -670,7 +670,7 @@ async function runVirtualParameters( if (!r.returnValue) { r.fault = { code: "script", - message: "Invalid virtual parameter return value" + message: "Invalid virtual parameter return value", }; return r; } @@ -688,7 +688,7 @@ async function runVirtualParameters( ) { r.fault = { code: "script", - message: `Virtual parameter '${provision[0]}' must provide 'writable' attribute` + message: `Virtual parameter '${provision[0]}' must provide 'writable' attribute`, }; return r; } @@ -702,15 +702,14 @@ async function runVirtualParameters( if (!t) { if (typeof v === "number") t = "xsd:int"; else if (typeof v === "boolean") t = "xsd:boolean"; - else if (((v as unknown) as object) instanceof Date) - t = "xsd:datetime"; + else if ((v as any) instanceof Date) t = "xsd:datetime"; else t = "xsd:string"; } if (v == null || !VALID_PARAM_TYPES.has(t)) { r.fault = { code: "script", - message: "Invalid virtual parameter value attribute" + message: "Invalid virtual parameter value attribute", }; return r; } @@ -719,7 +718,7 @@ async function runVirtualParameters( } else if (provision[1].value != null || provision[2].value != null) { r.fault = { code: "script", - message: `Virtual parameter '${provision[0]}' must provide 'value' attribute` + message: `Virtual parameter '${provision[0]}' must provide 'value' attribute`, }; return r; } @@ -756,7 +755,7 @@ async function runVirtualParameters( clear: allClear, declare: allDeclarations, done: done, - returnValue: done ? virtualParameterUpdates : null + returnValue: done ? virtualParameterUpdates : null, }; } @@ -770,7 +769,7 @@ function runDeclarations( exist: new Set(), object: new Set(), writable: new Set(), - value: new Set() + value: new Set(), }, spv: new Map(), gpn: new Set(), @@ -784,7 +783,7 @@ function runDeclarations( downloadsValues: new Map(), downloadsDownload: new Map(), reboot: 0, - factoryReset: 0 + factoryReset: 0, }; } @@ -983,7 +982,7 @@ export async function rpcRequest( return { fault: null, rpcId: generateRpcId(sessionContext), - rpc: sessionContext.rpcRequest + rpc: sessionContext.rpcRequest, }; } @@ -1018,7 +1017,7 @@ export async function rpcRequest( clear: toClear, declare: decs, done: done, - returnValue: ret + returnValue: ret, } = await run( sessionContext, provisions, @@ -1079,10 +1078,10 @@ export async function rpcRequest( fault: { code: "too_many_rpcs", message: "Too many RPC requests", - timestamp: sessionContext.timestamp + timestamp: sessionContext.timestamp, }, rpcId: null, - rpc: null + rpc: null, }; } @@ -1092,10 +1091,10 @@ export async function rpcRequest( code: "deeply_nested_vparams", message: "Virtual parameters are referencing other virtual parameters in a deeply nested manner", - timestamp: sessionContext.timestamp + timestamp: sessionContext.timestamp, }, rpcId: null, - rpc: null + rpc: null, }; } @@ -1104,10 +1103,10 @@ export async function rpcRequest( fault: { code: "too_many_cycles", message: "Too many provision cycles", - timestamp: sessionContext.timestamp + timestamp: sessionContext.timestamp, }, rpcId: null, - rpc: null + rpc: null, }; } @@ -1119,7 +1118,7 @@ export async function rpcRequest( "cwmp.maxCommitIterations", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ) * 2; if (sessionContext.iteration >= MAX_ITERATIONS * (sessionContext.cycle + 1)) { @@ -1127,10 +1126,10 @@ export async function rpcRequest( fault: { code: "too_many_commits", message: "Too many commit iterations", - timestamp: sessionContext.timestamp + timestamp: sessionContext.timestamp, }, rpcId: null, - rpc: null + rpc: null, }; } @@ -1164,7 +1163,7 @@ export async function rpcRequest( sessionContext.cacheSnapshot ); - vpd = vpd.filter(declaration => { + vpd = vpd.filter((declaration) => { if (Object.keys(allVirtualParameters).length) { if (declaration[0].length === 1) { // Avoid setting on every inform as "exist" timestamp @@ -1188,7 +1187,7 @@ export async function rpcRequest( Path.parse(`VirtualParameters.${k}`), timestamp, { - object: [timestamp, 0] + object: [timestamp, 0], }, toClear ); @@ -1276,9 +1275,9 @@ export async function rpcRequest( p, timestamp, { - object: [timestamp, false], - writable: [timestamp, true], - value: [timestamp, [true, "xsd:boolean"]] + object: [timestamp, 0], + writable: [timestamp, 1], + value: [timestamp, [true, "xsd:boolean"]], }, toClear ); @@ -1332,26 +1331,26 @@ export async function rpcRequest( const params = { FileType: { writable: 1, - value: [instance.FileType || "", "xsd:string"] + value: [instance.FileType || "", "xsd:string"], }, FileName: { writable: 1, - value: [instance.FileName || "", "xsd:string"] + value: [instance.FileName || "", "xsd:string"], }, TargetFileName: { writable: 1, - value: [instance.TargetFileName || "", "xsd:string"] + value: [instance.TargetFileName || "", "xsd:string"], }, Download: { writable: 1, - value: [instance.Download || 0, "xsd:dateTime"] + value: [instance.Download || 0, "xsd:dateTime"], }, LastFileType: { writable: 0, value: ["", "xsd:string"] }, LastFileName: { writable: 0, value: ["", "xsd:string"] }, LastTargetFileName: { writable: 0, value: ["", "xsd:string"] }, LastDownload: { writable: 0, value: [0, "xsd:dateTime"] }, StartTime: { writable: 0, value: [0, "xsd:dateTime"] }, - CompleteTime: { writable: 0, value: [0, "xsd:dateTime"] } + CompleteTime: { writable: 0, value: [0, "xsd:dateTime"] }, }; for (const [k, v] of Object.entries(params)) { @@ -1361,8 +1360,11 @@ export async function rpcRequest( timestamp, { object: [timestamp, 0], - writable: [timestamp, v.writable], - value: [timestamp, v.value] + writable: [timestamp, v.writable as 0 | 1], + value: [ + timestamp, + v.value as [string | number | boolean, string], + ], }, toClear ); @@ -1440,7 +1442,7 @@ export async function rpcRequest( return { fault: null, rpcId: generateRpcId(sessionContext), - rpc: sessionContext.rpcRequest + rpc: sessionContext.rpcRequest, }; } @@ -1564,7 +1566,7 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest { "cwmp.gpnNextLevel", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const paths = Array.from(syncState.gpn.keys()).sort( @@ -1614,7 +1616,7 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest { return { name: "GetParameterNames", parameterPath: path.length ? path.toString() + "." : "", - nextLevel: nextLevel + nextLevel: nextLevel, }; } } @@ -1625,7 +1627,7 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest { "cwmp.gpvBatchSize", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const parameterNames: string[] = []; @@ -1642,7 +1644,7 @@ function generateGetRpcRequest(sessionContext: SessionContext): GetAcsRequest { if (parameterNames.length) { return { name: "GetParameterValues", - parameterNames: parameterNames + parameterNames: parameterNames, }; } } @@ -1661,7 +1663,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { if (instance && sessionContext.deviceData.attributes.has(instance)) { return { name: "DeleteObject", - objectName: instance.toString() + "." + objectName: instance.toString() + ".", }; } } @@ -1676,7 +1678,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { name: "AddObject", objectName: param.toString() + ".", instanceValues: instance, - next: "getInstanceKeys" + next: "getInstanceKeys", }; } } @@ -1688,7 +1690,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { "cwmp.gpvBatchSize", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const DATETIME_MILLISECONDS = !!localCache.getConfig( @@ -1696,7 +1698,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { "cwmp.datetimeMilliseconds", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const BOOLEAN_LITERAL = !!localCache.getConfig( @@ -1704,7 +1706,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { "cwmp.booleanLiteral", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const parameterValues: [Path, string | number | boolean, string][] = []; @@ -1736,7 +1738,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { return { name: "SetParameterValues", parameterList: parameterValues.map( - p => + (p) => [p[0].toString(), p[1], p[2]] as [ string, string | number | boolean, @@ -1744,7 +1746,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { ] ), DATETIME_MILLISECONDS: DATETIME_MILLISECONDS, - BOOLEAN_LITERAL: BOOLEAN_LITERAL + BOOLEAN_LITERAL: BOOLEAN_LITERAL, }; } @@ -1782,7 +1784,7 @@ function generateSetRpcRequest(sessionContext: SessionContext): SetAcsRequest { ? targetFileNameAttrs.value ? (targetFileNameAttrs.value[1][0] as string) : null - : null + : null, }; } } @@ -1849,7 +1851,7 @@ function generateGetVirtualParameterProvisions( dec, {}, currentTimestamps, - currentValues + currentValues, ]); } } @@ -1902,7 +1904,7 @@ function generateSetVirtualParameterProvisions( {}, { value: val }, currentTimestamps, - currentValues + currentValues, ]); } } @@ -2034,10 +2036,13 @@ function processDeclarations( declareAttributeValues && declareAttributeValues.value ) { - syncState.tags.set(currentPath, device.sanitizeParameterValue([ - declareAttributeValues.value[0], - "xsd:boolean" - ])[0] as boolean); + syncState.tags.set( + currentPath, + device.sanitizeParameterValue([ + declareAttributeValues.value[0], + "xsd:boolean", + ])[0] as boolean + ); } break; @@ -2198,7 +2203,7 @@ function processInstances( sessionContext: SessionContext, parent: Path, parameters: Path[], - keys: {}, + keys: Record, minInstances: number, maxInstances: number, defer: boolean @@ -2265,16 +2270,18 @@ export async function rpcResponse( ++sessionContext.rpcCount; - const rpcRes = _rpcRes as CpeGetResponse & CpeSetResponse; - const rpcReq = sessionContext.rpcRequest as GetAcsRequest & SetAcsRequest; + const rpcRes = _rpcRes; + const rpcReq = sessionContext.rpcRequest; if (!rpcReq.next) { sessionContext.rpcRequest = null; } else if (rpcReq.next === "getInstanceKeys") { const parameterNames = []; const instanceValues = {}; - for (const [k, v] of Object.entries(rpcReq.instanceValues)) { - const n = `${rpcReq.objectName}${rpcRes.instanceNumber}.${k}`; + const req = rpcReq as GetAcsRequest; + const res = rpcRes as CpeSetResponse; + for (const [k, v] of Object.entries(req.instanceValues)) { + const n = `${req.objectName}${res.instanceNumber}.${k}`; parameterNames.push(n); instanceValues[n] = v; } @@ -2286,17 +2293,19 @@ export async function rpcResponse( name: "GetParameterValues", parameterNames: parameterNames, next: "setInstanceKeys", - instanceValues: instanceValues + instanceValues: instanceValues, }; sessionContext.rpcRequest = r; } } else if (rpcReq.next === "setInstanceKeys") { + const req = rpcReq as GetAcsRequest; + const res = rpcRes as CpeGetResponse; const parameterList: [string, string | number | boolean, string][] = []; - for (const p of rpcRes.parameterList) { - if (p[1] !== rpcReq.instanceValues[p[0]]) { + for (const p of res.parameterList) { + if (p[1] !== req.instanceValues[p[0]]) { const v = device.sanitizeParameterValue([ - rpcReq.instanceValues[p[0]], - p[2] + req.instanceValues[p[0]], + p[2], ]); parameterList.push([p[0], v[0], v[1]]); } @@ -2310,7 +2319,7 @@ export async function rpcResponse( "cwmp.datetimeMilliseconds", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const BOOLEAN_LITERAL = !!localCache.getConfig( @@ -2318,14 +2327,14 @@ export async function rpcResponse( "cwmp.booleanLiteral", {}, sessionContext.timestamp, - e => configContextCallback(sessionContext, e) + (e) => configContextCallback(sessionContext, e) ); const r: SetAcsRequest = { name: "SetParameterValues", parameterList: parameterList, DATETIME_MILLISECONDS: DATETIME_MILLISECONDS, - BOOLEAN_LITERAL: BOOLEAN_LITERAL + BOOLEAN_LITERAL: BOOLEAN_LITERAL, }; sessionContext.rpcRequest = r; } @@ -2344,261 +2353,261 @@ export async function rpcResponse( params: [Path, number, Attributes?][], wildcardPath: Path; - switch (rpcRes.name) { - case "GetParameterValuesResponse": - if (rpcReq.name !== "GetParameterValues") - throw new Error("Response name does not match request name"); + if (rpcRes.name === "GetParameterValuesResponse") { + const req = rpcReq as GetAcsRequest; + const res = rpcRes as CpeGetResponse; + if (req.name !== "GetParameterValues") + throw new Error("Response name does not match request name"); - for (const p of rpcRes.parameterList) { - toClear = device.set( - sessionContext.deviceData, + for (const p of res.parameterList) { + toClear = device.set( + sessionContext.deviceData, + Path.parse(p[0]), + timestamp, + { + object: [timestamp, 0], + value: [timestamp, p.slice(1) as [string | number | boolean, string]], + }, + toClear + ); + } + } else if (rpcRes.name === "GetParameterNamesResponse") { + const req = rpcReq as GetAcsRequest; + const res = rpcRes as CpeGetResponse; + + if (req.name !== "GetParameterNames") + throw new Error("Response name does not match request name"); + + if (req.parameterPath.endsWith(".")) + root = Path.parse(req.parameterPath.slice(0, -1)); + else root = Path.parse(req.parameterPath); + + wildcardPath = Path.parse("*"); + params = [[root.concat(wildcardPath), timestamp]]; + + // Some clients don't report all ancestors explicitly + missing = {}; + + for (const p of res.parameterList) { + let i = p[0].length - 1; + while ((i = p[0].lastIndexOf(".", i - 1)) > req.parameterPath.length) + missing[p[0].slice(0, i)] |= 0; + + if (p[0].endsWith(".")) { + missing[p[0].slice(0, -1)] |= 1; + const path = Path.parse(p[0].slice(0, -1)); + if (!req.nextLevel) params.push([path.concat(wildcardPath), timestamp]); + + params.push([ + path, + timestamp, + { object: [timestamp, 1], writable: [timestamp, p[1] ? 1 : 0] }, + ]); + } else { + missing[p[0]] |= 1; + params.push([ Path.parse(p[0]), timestamp, - { object: [timestamp, 0], value: [timestamp, p.slice(1)] }, - toClear - ); + { object: [timestamp, 0], writable: [timestamp, p[1] ? 1 : 0] }, + ]); } + } - break; - case "GetParameterNamesResponse": - if (rpcReq.name !== "GetParameterNames") - throw new Error("Response name does not match request name"); - - if (rpcReq.parameterPath.endsWith(".")) - root = Path.parse(rpcReq.parameterPath.slice(0, -1)); - else root = Path.parse(rpcReq.parameterPath); - - wildcardPath = Path.parse("*"); - params = [[root.concat(wildcardPath), timestamp]]; - - // Some clients don't report all ancestors explicitly - missing = {}; - - for (const p of rpcRes.parameterList) { - let i = p[0].length - 1; - while ((i = p[0].lastIndexOf(".", i - 1)) > rpcReq.parameterPath.length) - missing[p[0].slice(0, i)] |= 0; - - if (p[0].endsWith(".")) { - missing[p[0].slice(0, -1)] |= 1; - const path = Path.parse(p[0].slice(0, -1)); - if (!rpcReq.nextLevel) - params.push([path.concat(wildcardPath), timestamp]); - - params.push([ - path, - timestamp, - { object: [timestamp, 1], writable: [timestamp, p[1] ? 1 : 0] } - ]); - } else { - missing[p[0]] |= 1; - params.push([ - Path.parse(p[0]), - timestamp, - { object: [timestamp, 0], writable: [timestamp, p[1] ? 1 : 0] } - ]); - } + for (const [k, v] of Object.entries(missing)) { + if (v === 0) { + // TODO consider showing a warning + const path = Path.parse(k); + params.push([ + path, + timestamp, + { object: [timestamp, 1], writable: [timestamp, 0] }, + ]); + params.push([path.concat(wildcardPath), timestamp]); } + } - for (const [k, v] of Object.entries(missing)) { - if (v === 0) { - // TODO consider showing a warning - const path = Path.parse(k); - params.push([ - path, - timestamp, - { object: [timestamp, 1], writable: [timestamp, 0] } - ]); - params.push([path.concat(wildcardPath), timestamp]); - } + // Sort such that: + // - Longer params come first in order to work around client issue + // where object paths can have no trailing dot. + // - Parameters come before wildcard paths. + params.sort((a, b) => { + let al = a[0].length; + let bl = b[0].length; + if (b[0].segments[bl - 1] === "*") bl *= -1; + if (a[0].segments[al - 1] === "*") al *= -1; + return bl - al; + }); + + if (!root.length) { + for (const n of [ + "DeviceID", + "Events", + "Tags", + "Reboot", + "FactoryReset", + "VirtualParameters", + "Downloads", + ]) { + const p = sessionContext.deviceData.paths.get(Path.parse(n)); + if (p && sessionContext.deviceData.attributes.has(p)) + sessionContext.deviceData.timestamps.set(p, timestamp); } + } - // Sort such that: - // - Longer params come first in order to work around client issue - // where object paths can have no trailing dot. - // - Parameters come before wildcard paths. - params.sort((a, b) => { - let al = a[0].length; - let bl = b[0].length; - if (b[0].segments[bl - 1] === "*") bl *= -1; - if (a[0].segments[al - 1] === "*") al *= -1; - return bl - al; - }); - - if (!root.length) { - for (const n of [ - "DeviceID", - "Events", - "Tags", - "Reboot", - "FactoryReset", - "VirtualParameters", - "Downloads" - ]) { - const p = sessionContext.deviceData.paths.get(Path.parse(n)); - if (p && sessionContext.deviceData.attributes.has(p)) - sessionContext.deviceData.timestamps.set(p, timestamp); - } - } - - for (const p of params) { - toClear = device.set( - sessionContext.deviceData, - p[0], - p[1], - p[2], - toClear - ); - } - - if (toClear) { - for (const c of toClear) - device.clear(sessionContext.deviceData, c[0], c[1], c[2], c[3]); - } - - return; - case "SetParameterValuesResponse": - if (rpcReq.name !== "SetParameterValues") - throw new Error("Response name does not match request name"); - - for (const p of rpcReq.parameterList) { - toClear = device.set( - sessionContext.deviceData, - Path.parse(p[0]), - timestamp + 1, - { - object: [timestamp + 1, 0], - writable: [timestamp + 1, 1], - value: [timestamp + 1, p.slice(1)] - }, - toClear - ); - } - - break; - case "AddObjectResponse": + for (const p of params) { toClear = device.set( sessionContext.deviceData, - Path.parse(rpcReq.objectName + rpcRes.instanceNumber), - timestamp + 1, - { object: [timestamp + 1, 1] }, + p[0], + p[1], + p[2], toClear ); - break; - case "DeleteObjectResponse": + } + } else if (rpcRes.name === "SetParameterValuesResponse") { + const req = rpcReq as SetAcsRequest; + + if (req.name !== "SetParameterValues") + throw new Error("Response name does not match request name"); + + for (const p of req.parameterList) { toClear = device.set( sessionContext.deviceData, - Path.parse(rpcReq.objectName.slice(0, -1)), + Path.parse(p[0]), timestamp + 1, - null, + { + object: [timestamp + 1, 0], + writable: [timestamp + 1, 1], + value: [ + timestamp + 1, + p.slice(1) as [string | number | boolean, string], + ], + }, toClear ); - break; - case "RebootResponse": + } + } else if (rpcRes.name === "AddObjectResponse") { + const req = rpcReq as SetAcsRequest; + const res = rpcRes as CpeSetResponse; + toClear = device.set( + sessionContext.deviceData, + Path.parse(req.objectName + res.instanceNumber), + timestamp + 1, + { object: [timestamp + 1, 1] }, + toClear + ); + } else if (rpcRes.name === "DeleteObjectResponse") { + const req = rpcReq as SetAcsRequest; + toClear = device.set( + sessionContext.deviceData, + Path.parse(req.objectName.slice(0, -1)), + timestamp + 1, + null, + toClear + ); + } else if (rpcRes.name === "RebootResponse") { + toClear = device.set( + sessionContext.deviceData, + Path.parse("Reboot"), + timestamp + 1, + { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, + toClear + ); + } else if (rpcRes.name === "FactoryResetResponse") { + toClear = device.set( + sessionContext.deviceData, + Path.parse("FactoryReset"), + timestamp + 1, + { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, + toClear + ); + } else if (rpcRes.name === "DownloadResponse") { + const req = rpcReq as SetAcsRequest; + const res = rpcRes as CpeSetResponse; + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.Download`), + timestamp + 1, + { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, + toClear + ); + + if (res.status === 0) { toClear = device.set( sessionContext.deviceData, - Path.parse("Reboot"), + Path.parse(`Downloads.${req.instance}.LastDownload`), timestamp + 1, - { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, - toClear - ); - break; - case "FactoryResetResponse": - toClear = device.set( - sessionContext.deviceData, - Path.parse("FactoryReset"), - timestamp + 1, - { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, - toClear - ); - break; - case "DownloadResponse": - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.Download`), - timestamp + 1, - { value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] }, + { + value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]], + }, toClear ); - if (rpcRes.status === 0) { - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.LastDownload`), - timestamp + 1, - { - value: [timestamp + 1, [sessionContext.timestamp, "xsd:dateTime"]] - }, - toClear - ); + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.LastFileType`), + timestamp + 1, + { value: [timestamp + 1, [req.fileType, "xsd:string"]] }, + toClear + ); - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.LastFileType`), - timestamp + 1, - { value: [timestamp + 1, [rpcReq.fileType, "xsd:string"]] }, - toClear - ); + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.LastFileName`), + timestamp + 1, + { value: [timestamp + 1, [req.fileType, "xsd:string"]] }, + toClear + ); - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.LastFileName`), - timestamp + 1, - { value: [timestamp + 1, [rpcReq.fileType, "xsd:string"]] }, - toClear - ); + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.LastTargetFileName`), + timestamp + 1, + { value: [timestamp + 1, [req.fileType, "xsd:string"]] }, + toClear + ); - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.LastTargetFileName`), - timestamp + 1, - { value: [timestamp + 1, [rpcReq.fileType, "xsd:string"]] }, - toClear - ); + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.StartTime`), + timestamp + 1, + { value: [timestamp + 1, [+res.startTime, "xsd:dateTime"]] }, + toClear + ); - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.StartTime`), - timestamp + 1, - { value: [timestamp + 1, [+rpcRes.startTime, "xsd:dateTime"]] }, - toClear - ); + toClear = device.set( + sessionContext.deviceData, + Path.parse(`Downloads.${req.instance}.CompleteTime`), + timestamp + 1, + { value: [timestamp + 1, [+res.completeTime, "xsd:dateTime"]] }, + toClear + ); + } else { + const operation = { + name: "Download", + timestamp: sessionContext.timestamp, + provisions: sessionContext.provisions, + channels: sessionContext.channels, + retries: {}, + args: { + instance: req.instance, + fileType: req.fileType, + fileName: req.fileName, + targetFileName: req.targetFileName, + }, + }; - toClear = device.set( - sessionContext.deviceData, - Path.parse(`Downloads.${rpcReq.instance}.CompleteTime`), - timestamp + 1, - { value: [timestamp + 1, [+rpcRes.completeTime, "xsd:dateTime"]] }, - toClear - ); - } else { - const operation = { - name: "Download", - timestamp: sessionContext.timestamp, - provisions: sessionContext.provisions, - channels: sessionContext.channels, - retries: {}, - args: { - instance: rpcReq.instance, - fileType: rpcReq.fileType, - fileName: rpcReq.fileName, - targetFileName: rpcReq.targetFileName - } - }; - - for (const channel of Object.keys(sessionContext.channels)) { - if (sessionContext.retries[channel] != null) - operation.retries[channel] = sessionContext.retries[channel]; - } - - sessionContext.operations[rpcReq.commandKey] = operation; - if (!sessionContext.operationsTouched) - sessionContext.operationsTouched = {}; - sessionContext.operationsTouched[rpcReq.commandKey] = 1; + for (const channel of Object.keys(sessionContext.channels)) { + if (sessionContext.retries[channel] != null) + operation.retries[channel] = sessionContext.retries[channel]; } - break; - default: - throw new Error("Response name not recognized"); + + sessionContext.operations[req.commandKey] = operation; + if (!sessionContext.operationsTouched) + sessionContext.operationsTouched = {}; + sessionContext.operationsTouched[req.commandKey] = 1; + } + } else { + throw new Error("Response name not recognized"); } if (toClear) { @@ -2612,7 +2621,7 @@ export async function rpcFault( id: string, faultResponse: CpeFault ): Promise { - const rpcReq = sessionContext.rpcRequest as GetAcsRequest & SetAcsRequest; + const rpcReq = sessionContext.rpcRequest as GetAcsRequest | SetAcsRequest; delete sessionContext.syncState; delete sessionContext.rpcRequest; ++sessionContext.rpcCount; @@ -2629,16 +2638,16 @@ export async function rpcFault( if (rpcReq.name === "GetParameterNames") { if (rpcReq.parameterPath) { toClear = [ - [Path.parse(rpcReq.parameterPath.replace(/\.$/, "")), timestamp] + [Path.parse(rpcReq.parameterPath.replace(/\.$/, "")), timestamp], ]; } } else if (rpcReq.name === "GetParameterValues") { toClear = rpcReq.parameterNames.map( - p => [Path.parse(p.replace(/\.$/, "")), timestamp] as Clear + (p) => [Path.parse(p.replace(/\.$/, "")), timestamp] as Clear ); } else if (rpcReq.name === "SetParameterValues") { toClear = rpcReq.parameterList.map( - p => [Path.parse(p[0].replace(/\.$/, "")), timestamp] as Clear + (p) => [Path.parse(p[0].replace(/\.$/, "")), timestamp] as Clear ); } else if (rpcReq.name === "AddObject") { toClear = [[Path.parse(rpcReq.objectName.replace(/\.$/, "")), timestamp]]; @@ -2656,14 +2665,14 @@ export async function rpcFault( const fault: Fault = { code: `cwmp.${faultResponse.detail.faultCode}`, message: faultResponse.detail.faultString, - detail: faultResponse.detail + detail: faultResponse.detail, }; return fault; } export async function deserialize( - sessionContextString + sessionContextString: string ): Promise { const sessionContext = JSON.parse(sessionContextString); @@ -2704,13 +2713,13 @@ export async function serialize( path.toString(), sessionContext.deviceData.trackers.get(path) || null, sessionContext.deviceData.timestamps.getRevisions(path) || null, - sessionContext.deviceData.attributes.getRevisions(path) || null + sessionContext.deviceData.attributes.getRevisions(path) || null, ]; deviceData.push(e); } - const declarations = sessionContext.declarations.map(decs => { - return decs.map(d => Object.assign({}, d, { path: d.path.toString() })); + const declarations = sessionContext.declarations.map((decs) => { + return decs.map((d) => Object.assign({}, d, { path: d.path.toString() })); }); const jsonSessionContext = Object.assign({}, sessionContext, { @@ -2719,7 +2728,7 @@ export async function serialize( syncState: null, toLoad: null, httpRequest: null, - httpResponse: null + httpResponse: null, }); const sessionContextString = JSON.stringify(jsonSessionContext); diff --git a/lib/soap.ts b/lib/soap.ts index f7c4389..14ccddc 100644 --- a/lib/soap.ts +++ b/lib/soap.ts @@ -22,7 +22,7 @@ import { Element, parseAttrs, encodeEntities, - decodeEntities + decodeEntities, } from "./xml-parser"; import memoize from "./common/memoize"; import { version as VERSION } from "../package.json"; @@ -36,7 +36,8 @@ import { SpvFault, CpeFault, SoapMessage, - TransferCompleteRequest + TransferCompleteRequest, + AcsRequest, } from "./types"; const SERVER_NAME = `GenieACS/${VERSION}`; @@ -47,39 +48,39 @@ const NAMESPACES = { "soap-env": "http://schemas.xmlsoap.org/soap/envelope/", xsd: "http://www.w3.org/2001/XMLSchema", xsi: "http://www.w3.org/2001/XMLSchema-instance", - cwmp: "urn:dslforum-org:cwmp-1-0" + cwmp: "urn:dslforum-org:cwmp-1-0", }, "1.1": { "soap-enc": "http://schemas.xmlsoap.org/soap/encoding/", "soap-env": "http://schemas.xmlsoap.org/soap/envelope/", xsd: "http://www.w3.org/2001/XMLSchema", xsi: "http://www.w3.org/2001/XMLSchema-instance", - cwmp: "urn:dslforum-org:cwmp-1-1" + cwmp: "urn:dslforum-org:cwmp-1-1", }, "1.2": { "soap-enc": "http://schemas.xmlsoap.org/soap/encoding/", "soap-env": "http://schemas.xmlsoap.org/soap/envelope/", xsd: "http://www.w3.org/2001/XMLSchema", xsi: "http://www.w3.org/2001/XMLSchema-instance", - cwmp: "urn:dslforum-org:cwmp-1-2" + cwmp: "urn:dslforum-org:cwmp-1-2", }, "1.3": { "soap-enc": "http://schemas.xmlsoap.org/soap/encoding/", "soap-env": "http://schemas.xmlsoap.org/soap/envelope/", xsd: "http://www.w3.org/2001/XMLSchema", xsi: "http://www.w3.org/2001/XMLSchema-instance", - cwmp: "urn:dslforum-org:cwmp-1-2" + cwmp: "urn:dslforum-org:cwmp-1-2", }, "1.4": { "soap-enc": "http://schemas.xmlsoap.org/soap/encoding/", "soap-env": "http://schemas.xmlsoap.org/soap/envelope/", xsd: "http://www.w3.org/2001/XMLSchema", xsi: "http://www.w3.org/2001/XMLSchema-instance", - cwmp: "urn:dslforum-org:cwmp-1-3" - } + cwmp: "urn:dslforum-org:cwmp-1-3", + }, }; -let warnings; +let warnings: Record[]; const memoizedParseAttrs = memoize(parseAttrs); @@ -93,14 +94,16 @@ function parseBool(v): boolean { function event(xml: Element): string[] { return xml.children - .filter(n => n.localName === "EventStruct") - .map(c => c.children.find(n => n.localName === "EventCode").text.trim()); + .filter((n) => n.localName === "EventStruct") + .map((c) => + c.children.find((n) => n.localName === "EventCode").text.trim() + ); } function parameterInfoList(xml: Element): [string, boolean][] { return xml.children - .filter(e => e.localName === "ParameterInfoStruct") - .map<[string, boolean]>(e => { + .filter((e) => e.localName === "ParameterInfoStruct") + .map<[string, boolean]>((e) => { let param: string, value: string; for (const c of e.children) { switch (c.localName) { @@ -118,7 +121,7 @@ function parameterInfoList(xml: Element): [string, boolean][] { if (parsed === null) { warnings.push({ message: "Invalid writable attribute", - parameter: param + parameter: param, }); parsed = false; } @@ -129,7 +132,7 @@ function parameterInfoList(xml: Element): [string, boolean][] { const getValueType = memoize((str: string) => parseAttrs(str) - .find(s => s.localName === "type") + .find((s) => s.localName === "type") .value.trim() ); @@ -137,8 +140,8 @@ function parameterValueList( xml: Element ): [string, string | number | boolean, string][] { return xml.children - .filter(e => e.localName === "ParameterValueStruct") - .map<[string, string | number | boolean, string]>(e => { + .filter((e) => e.localName === "ParameterValueStruct") + .map<[string, string | number | boolean, string]>((e) => { let valueElement: Element, param: string; for (const c of e.children) { switch (c.localName) { @@ -160,7 +163,7 @@ function parameterValueList( if (parsed === null) { warnings.push({ message: "Invalid value attribute", - parameter: param + parameter: param, }); parsed = value; } @@ -169,7 +172,7 @@ function parameterValueList( if (isNaN(parsed)) { warnings.push({ message: "Invalid value attribute", - parameter: param + parameter: param, }); parsed = value; } @@ -178,7 +181,7 @@ function parameterValueList( if (isNaN(parsed)) { warnings.push({ message: "Invalid value attribute", - parameter: param + parameter: param, }); parsed = value; } @@ -198,8 +201,8 @@ function GetParameterNamesResponse(xml): CpeGetResponse { return { name: "GetParameterNamesResponse", parameterList: parameterInfoList( - xml.children.find(n => n.localName === "ParameterList") - ) + xml.children.find((n) => n.localName === "ParameterList") + ), }; } @@ -207,7 +210,7 @@ function GetParameterValues(methodRequest): string { return `${methodRequest.parameterNames - .map(p => `${p}`) + .map((p) => `${p}`) .join("")}`; } @@ -215,13 +218,13 @@ function GetParameterValuesResponse(xml: Element): CpeGetResponse { return { name: "GetParameterValuesResponse", parameterList: parameterValueList( - xml.children.find(n => n.localName === "ParameterList") - ) + xml.children.find((n) => n.localName === "ParameterList") + ), }; } function SetParameterValues(methodRequest): string { - const params = methodRequest.parameterList.map(p => { + const params = methodRequest.parameterList.map((p) => { let val = p[1]; if (p[2] === "xsd:dateTime" && typeof val === "number") { val = new Date(val).toISOString(); @@ -237,24 +240,24 @@ function SetParameterValues(methodRequest): string { return `${params.join( - "" - )}${methodRequest.parameterKey || - ""}`; + }]">${params.join("")}${ + methodRequest.parameterKey || "" + }`; } function SetParameterValuesResponse(xml: Element): CpeSetResponse { return { name: "SetParameterValuesResponse", - status: parseInt(xml.children.find(n => n.localName === "Status").text) + status: parseInt(xml.children.find((n) => n.localName === "Status").text), }; } function AddObject(methodRequest): string { return `${ methodRequest.objectName - }${methodRequest.parameterKey || - ""}`; + }${ + methodRequest.parameterKey || "" + }`; } function AddObjectResponse(xml: Element): CpeSetResponse { @@ -273,32 +276,34 @@ function AddObjectResponse(xml: Element): CpeSetResponse { return { name: "AddObjectResponse", instanceNumber: instanceNumber, - status: status + status: status, }; } function DeleteObject(methodRequest): string { return `${ methodRequest.objectName - }${methodRequest.parameterKey || - ""}`; + }${ + methodRequest.parameterKey || "" + }`; } function DeleteObjectResponse(xml: Element): CpeSetResponse { return { name: "DeleteObjectResponse", - status: parseInt(xml.children.find(n => n.localName === "Status").text) + status: parseInt(xml.children.find((n) => n.localName === "Status").text), }; } function Reboot(methodRequest): string { - return `${methodRequest.commandKey || - ""}`; + return `${ + methodRequest.commandKey || "" + }`; } function RebootResponse(): CpeSetResponse { return { - name: "RebootResponse" + name: "RebootResponse", }; } @@ -308,23 +313,26 @@ function FactoryReset(): string { function FactoryResetResponse(): CpeSetResponse { return { - name: "FactoryResetResponse" + name: "FactoryResetResponse", }; } function Download(methodRequest): string { - return `${methodRequest.commandKey || - ""}${methodRequest.fileType}${ + return `${ + methodRequest.commandKey || "" + }${methodRequest.fileType}${ methodRequest.url }${encodeEntities( methodRequest.username || "" )}${encodeEntities( methodRequest.password || "" - )}${methodRequest.fileSize || - "0"}${encodeEntities( + )}${ + methodRequest.fileSize || "0" + }${encodeEntities( methodRequest.targetFileName || "" - )}${methodRequest.delaySeconds || - "0"}${encodeEntities( + )}${ + methodRequest.delaySeconds || "0" + }${encodeEntities( methodRequest.successUrl || "" )}${encodeEntities( methodRequest.failureUrl || "" @@ -351,7 +359,7 @@ function DownloadResponse(xml: Element): CpeSetResponse { name: "DownloadResponse", status: status, startTime: startTime, - completeTime: completeTime + completeTime: completeTime, }; } @@ -361,7 +369,7 @@ function Inform(xml: Element): InformRequest { Manufacturer: null, OUI: null, ProductClass: null, - SerialNumber: null + SerialNumber: null, }; for (const c of xml.children) { @@ -389,7 +397,7 @@ function Inform(xml: Element): InformRequest { parameterList: parameterList, deviceId: deviceId, event: evnt, - retryCount: retryCount + retryCount: retryCount, }; } @@ -405,7 +413,7 @@ function GetRPCMethodsResponse(methodResponse): string { return `${methodResponse.methodList - .map(m => `${m}`) + .map((m) => `${m}`) .join("")}`; } @@ -433,7 +441,7 @@ function TransferComplete(xml: Element): TransferCompleteRequest { commandKey: commandKey, faultStruct: _faultStruct, startTime: startTime, - completeTime: completeTime + completeTime: completeTime, }; } @@ -444,7 +452,7 @@ function TransferCompleteResponse(): string { function RequestDownload(xml: Element): CpeRequest { return { name: "RequestDownload", - fileType: xml.children.find(n => n.localName === "FileType").text + fileType: xml.children.find((n) => n.localName === "FileType").text, }; } @@ -480,7 +488,7 @@ function faultStruct(xml: Element): FaultStruct { setParameterValuesFault.push({ parameterName: pn, faultCode: fc, - faultString: fs + faultString: fs, }); } } @@ -499,7 +507,7 @@ function fault(xml: Element): CpeFault { faultString = decodeEntities(c.text); break; case "detail": - detail = faultStruct(c.children.find(n => n.localName === "Fault")); + detail = faultStruct(c.children.find((n) => n.localName === "Fault")); break; } } @@ -507,7 +515,11 @@ function fault(xml: Element): CpeFault { return { faultCode, faultString, detail }; } -export function request(body: string, cwmpVersion, warn): SoapMessage { +export function request( + body: string, + cwmpVersion: string, + warn: Record[] +): SoapMessage { warnings = warn; const rpc = { @@ -516,7 +528,7 @@ export function request(body: string, cwmpVersion, warn): SoapMessage { sessionTimeout: null, cpeRequest: null, cpeFault: null, - cpeResponse: null + cpeResponse: null, }; if (!body.length) return rpc; @@ -563,9 +575,9 @@ export function request(body: string, cwmpVersion, warn): SoapMessage { const attrs = memoizedParseAttrs(e.attrs); const attr = namespace ? attrs.find( - s => s.namespace === "xmlns" && s.localName === namespace + (s) => s.namespace === "xmlns" && s.localName === namespace ) - : attrs.find(s => s.name === "xmlns"); + : attrs.find((s) => s.name === "xmlns"); if (attr) namespaceHref = attr.value; } @@ -653,13 +665,18 @@ const namespacesAttrs = { .join(" "), "1.4": Object.entries(NAMESPACES["1.4"]) .map(([k, v]) => `xmlns:${k}="${v}"`) - .join(" ") + .join(" "), }; -export function response(rpc): { code: number; headers: {}; data: string } { +export function response(rpc: { + id: string; + acsRequest?: AcsRequest; + acsResponse?: AcsResponse; + cwmpVersion?: string; +}): { code: number; headers: Record; data: string } { const headers = { Server: SERVER_NAME, - SOAPServer: SERVER_NAME + SOAPServer: SERVER_NAME, }; if (!rpc) return { code: 204, headers: headers, data: "" }; @@ -721,6 +738,6 @@ export function response(rpc): { code: number; headers: {}; data: string } { namespacesAttrs[rpc.cwmpVersion] }>${ rpc.id - }${body}` + }${body}`, }; } diff --git a/lib/types.ts b/lib/types.ts index 02e200f..4dd4a11 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -152,7 +152,7 @@ export interface SessionContext { } export interface Task { - _id: string; + _id?: string; name: string; parameterNames?: string[]; parameterValues?: [string, string | number | boolean, string?][]; @@ -300,7 +300,7 @@ export interface QueryOptions { export interface Declaration { path: Path; pathGet: number; - pathSet?: [number, number]; + pathSet?: number | [number, number]; attrGet?: { object?: number; writable?: number; value?: number }; attrSet?: { object?: boolean; @@ -322,7 +322,7 @@ export interface Preset { channel: string; schedule?: { md5: string; duration: number; schedule: any }; events?: { [event: string]: boolean }; - precondition?: {}; + precondition?: Expression; provisions: string[][]; } @@ -367,12 +367,12 @@ export interface Config { } export interface UiConfig { - filters: {}; - device: {}; - index: {}; + filters: Record; + device: Record; + index: Record; overview: { - charts?: {}; - groups?: {}; + charts?: Record; + groups?: Record; }; pageSize?: Expression; } diff --git a/lib/ui.ts b/lib/ui.ts index 8a4cd1c..f4aad07 100644 --- a/lib/ui.ts +++ b/lib/ui.ts @@ -52,14 +52,14 @@ const getAuthorizer = memoize( (snapshot: string, rolesStr: string): Authorizer => { const roles: string[] = JSON.parse(rolesStr); const allPermissions = localCache.getPermissions(snapshot); - const permissionSets: PermissionSet[] = roles.map(r => + const permissionSets: PermissionSet[] = roles.map((r) => Object.values(allPermissions[r] || {}) ); return new Authorizer(permissionSets); } ); -koa.on("error", async err => { +koa.on("error", async (err) => { throw err; }); @@ -84,7 +84,7 @@ koa.use( } return true; - } + }, }) ); @@ -111,7 +111,7 @@ koa.use(async (ctx, next) => { return next(); }); -router.post("/login", async ctx => { +router.post("/login", async (ctx) => { if (!JWT_SECRET) { ctx.status = 500; ctx.body = "UI_JWT_SECRET is not set"; @@ -126,7 +126,7 @@ router.post("/login", async ctx => { message: "Log in", context: ctx, username: username, - method: null + method: null, }; function success(authMethod): void { @@ -150,13 +150,13 @@ router.post("/login", async ctx => { failure(); }); -router.post("/logout", async ctx => { +router.post("/logout", async (ctx) => { ctx.cookies.set(JWT_COOKIE); // Delete cookie ctx.body = ""; logger.accessInfo({ message: "Log out", - context: ctx + context: ctx, }); }); @@ -170,11 +170,11 @@ koa.use(async (ctx, next) => { koa.use(koaBodyParser()); router.use("/api", api.routes(), api.allowedMethods()); -router.get("/status", ctx => { +router.get("/status", (ctx) => { ctx.body = "OK"; }); -router.get("/init", async ctx => { +router.get("/init", async (ctx) => { const status = await init.getStatus(); if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) { if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false; @@ -195,7 +195,7 @@ router.get("/init", async ctx => { ctx.body = status; }); -router.post("/init", async ctx => { +router.post("/init", async (ctx) => { const status = ctx.request.body; if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) { if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false; @@ -216,7 +216,7 @@ router.post("/init", async ctx => { ctx.body = ""; }); -router.get("/", async ctx => { +router.get("/", async (ctx) => { const permissionSets: PermissionSet[] = ctx.state.authorizer.getPermissionSets(); let wizard = ""; @@ -234,7 +234,7 @@ router.get("/", async ctx => {