Update ESLint and Prettier
This commit is contained in:
1 parent
5a9e03db2c
commit
ac980116bd
110 files changed
+2760
-2561
No files matched your search
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
+38
-47
@@ -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 `<symbol id="icon-${id}" ${viewBox}>${symbolBody}</symbol>`;
|
||||
}
|
||||
@@ -147,7 +144,7 @@ async function init(): Promise<void> {
|
||||
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<void> {
|
||||
"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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
sourcemap: "inline",
|
||||
sourcemapExcludeSources: true,
|
||||
banner: "#!/usr/bin/env node",
|
||||
file: outputFile
|
||||
file: outputFile,
|
||||
});
|
||||
|
||||
// Mark as executable
|
||||
@@ -246,25 +239,21 @@ async function generateBackendJs(): Promise<void> {
|
||||
"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<void> {
|
||||
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<void> {
|
||||
sourcemap: "inline",
|
||||
sourcemapExcludeSources: true,
|
||||
banner: "#!/usr/bin/env node",
|
||||
file: outputFile
|
||||
file: outputFile,
|
||||
});
|
||||
|
||||
// Mark as executable
|
||||
@@ -308,17 +297,17 @@ async function generateFrontendJs(): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
{
|
||||
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");
|
||||
});
|
||||
+21
-17
@@ -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<string, { value?: [boolean | number | string, string] }>
|
||||
): Promise<void> {
|
||||
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<string> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
export async function watchTask(
|
||||
deviceId: string,
|
||||
taskId: string,
|
||||
timeout: number
|
||||
): Promise<string> {
|
||||
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<string> {
|
||||
|
||||
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<string> {
|
||||
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<Task[]> {
|
||||
if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks];
|
||||
export async function insertTasks(tasks: any[]): Promise<Task[]> {
|
||||
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<Task[]> {
|
||||
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<Task[]> {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
export async function deleteDevice(deviceId): Promise<void> {
|
||||
export async function deleteDevice(deviceId: string): Promise<void> {
|
||||
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`),
|
||||
]);
|
||||
}
|
||||
+15
-22
@@ -19,11 +19,11 @@
|
||||
|
||||
import { createHash, randomBytes, pbkdf2 } from "crypto";
|
||||
|
||||
function parseHeaderFeilds(str: string): {} {
|
||||
function parseHeaderFeilds(str: string): Record<string, string> {
|
||||
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<string, string> {
|
||||
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, string>
|
||||
): string {
|
||||
const cnonce = randomBytes(8).toString("hex");
|
||||
const nc = "00000001";
|
||||
@@ -185,7 +178,7 @@ export function solveDigest(
|
||||
return authString;
|
||||
}
|
||||
|
||||
export function generateSalt(length): Promise<string> {
|
||||
export function generateSalt(length: number): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
randomBytes(length, (err, rand) => {
|
||||
if (err) return void reject(err);
|
||||
@@ -194,7 +187,7 @@ export function generateSalt(length): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
export function hashPassword(pass, salt): Promise<string> {
|
||||
export function hashPassword(pass: string, salt: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
pbkdf2(pass, salt, 10000, 128, "sha512", (err, hash) => {
|
||||
if (err) return void reject(err);
|
||||
|
||||
+13
-12
@@ -29,7 +29,7 @@ let mongoTimeOffset = 0;
|
||||
export async function connect(): Promise<void> {
|
||||
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<void> {
|
||||
if (clientPromise) await (await clientPromise).close();
|
||||
}
|
||||
|
||||
export async function get(key): Promise<any> {
|
||||
export async function get(key: string | string[]): Promise<any> {
|
||||
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<any> {
|
||||
});
|
||||
|
||||
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<any> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function del(key): Promise<void> {
|
||||
export async function del(key: string | string[]): Promise<void> {
|
||||
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<any> {
|
||||
export async function pop(key: string): Promise<any> {
|
||||
const res = await mongoCollection.findOneAndDelete({ _id: key });
|
||||
|
||||
if (
|
||||
@@ -98,16 +98,17 @@ export async function pop(key): Promise<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function lock(lockName, ttl): Promise<Function> {
|
||||
const token = Math.random()
|
||||
.toString(36)
|
||||
.slice(2);
|
||||
export async function lock(
|
||||
lockName: string,
|
||||
ttl: number
|
||||
): Promise<(extendTtl?: number) => Promise<void>> {
|
||||
const token = Math.random().toString(36).slice(2);
|
||||
|
||||
async function unlockOrExtend(extendTtl): Promise<void> {
|
||||
async function unlockOrExtend(extendTtl?: number): Promise<void> {
|
||||
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<Function> {
|
||||
);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+5
-15
@@ -17,22 +17,12 @@
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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, string>
|
||||
): 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, "\\$&");
|
||||
}
|
||||
@@ -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<string, boolean>;
|
||||
private getFilterCache: Map<string, Expression>;
|
||||
|
||||
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 => {
|
||||
|
||||
@@ -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), "");
|
||||
|
||||
@@ -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<Expression> {
|
||||
export async function mapAsync(
|
||||
exp: Expression,
|
||||
callback: (exp: Expression) => Promise<Expression>
|
||||
): Promise<Expression> {
|
||||
if (!Array.isArray(exp)) return callback(exp);
|
||||
|
||||
let clone;
|
||||
@@ -70,7 +76,7 @@ export async function mapAsync(exp, callback): Promise<Expression> {
|
||||
return callback(clone || exp);
|
||||
}
|
||||
|
||||
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
||||
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<unknown> {
|
||||
return parsimmon.seqMap(
|
||||
nextParser,
|
||||
parsimmon.seq(operatorsParser, nextParser).many(),
|
||||
@@ -101,10 +107,7 @@ const lang = parsimmon.createLanguage({
|
||||
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")
|
||||
@@ -162,7 +165,7 @@ 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");
|
||||
},
|
||||
@@ -171,7 +174,7 @@ const lang = parsimmon.createLanguage({
|
||||
.regexp(/'([^']*)'/, 1)
|
||||
.atLeast(1)
|
||||
.skip(parsimmon.optWhitespace)
|
||||
.map(s => s.join("'"))
|
||||
.map((s) => s.join("'"))
|
||||
.desc("string");
|
||||
},
|
||||
StringValueJs: function () {
|
||||
@@ -191,14 +194,8 @@ const lang = parsimmon.createLanguage({
|
||||
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);
|
||||
@@ -290,7 +287,7 @@ const lang = parsimmon.createLanguage({
|
||||
);
|
||||
},
|
||||
Expression: function (r) {
|
||||
function unary(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
||||
function unary(operatorsParser, nextParser): parsimmon.Parser<unknown> {
|
||||
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);
|
||||
}
|
||||
+36
-26
@@ -24,7 +24,7 @@ import { Expression } from "../types";
|
||||
|
||||
const isArray = Array.isArray;
|
||||
|
||||
const regExpCache = new WeakMap<object, RegExp>();
|
||||
const regExpCache = new WeakMap<any, RegExp>();
|
||||
|
||||
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<string, unknown> | ((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<string, unknown> | ((exp: string) => Expression),
|
||||
now?: number,
|
||||
cb?: (exp: Expression) => Expression
|
||||
): Expression;
|
||||
export function evaluate(
|
||||
exp: Expression,
|
||||
obj?: Record<string, unknown> | ((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<string, unknown>,
|
||||
now: number,
|
||||
cb?: Function
|
||||
cb?: (exp: Expression) => Promise<Expression>
|
||||
): Promise<string | number | boolean | null>;
|
||||
export async function evaluateAsync(
|
||||
exp,
|
||||
obj?,
|
||||
exp: Expression,
|
||||
obj?: Record<string, unknown>,
|
||||
now?: number,
|
||||
cb?: Function
|
||||
cb?: (exp: Expression) => Promise<Expression>
|
||||
): Promise<Expression>;
|
||||
export async function evaluateAsync(
|
||||
exp,
|
||||
obj?,
|
||||
exp: Expression,
|
||||
obj?: Record<string, unknown>,
|
||||
now?: number,
|
||||
cb?: Function
|
||||
cb?: (exp: Expression) => Promise<Expression>
|
||||
): Promise<Expression> {
|
||||
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<Expression>();
|
||||
map(exp, e => {
|
||||
map(exp, (e) => {
|
||||
if (isArray(e) && e[0] === "PARAM") params.add(e[1]);
|
||||
return e;
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ function getKey(obj): string {
|
||||
return k;
|
||||
}
|
||||
|
||||
export default function memoize<T extends Function>(func: T): T {
|
||||
export default function memoize<T extends (...args: any[]) => any>(func: T): T {
|
||||
const funcKey = getKey(func);
|
||||
return ((...args) => {
|
||||
const key = JSON.stringify(args.map(getKey)) + funcKey;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+12
-36
@@ -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;
|
||||
|
||||
|
||||
+11
-11
@@ -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<string, string>;
|
||||
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<void> {
|
||||
|
||||
+66
-65
@@ -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<string, string>; 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<void> {
|
||||
logger.accessWarn({
|
||||
sessionContext: sessionContext,
|
||||
message: "Unrecognized command key",
|
||||
rpc: rpc
|
||||
rpc: rpc,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -346,7 +347,7 @@ async function transferComplete(sessionContext, rpc): Promise<void> {
|
||||
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<void> {
|
||||
"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<void> {
|
||||
}
|
||||
}
|
||||
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
});
|
||||
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<SessionContext> {
|
||||
|
||||
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<void> {
|
||||
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<string, unknown>[],
|
||||
body: string
|
||||
): Promise<void> {
|
||||
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
|
||||
|
||||
@@ -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<void> {
|
||||
clientPromise = MongoClient.connect("" + get("MONGODB_CONNECTION_URL"), {
|
||||
useNewUrlParser: true
|
||||
useNewUrlParser: true,
|
||||
});
|
||||
|
||||
client = await clientPromise;
|
||||
@@ -77,7 +77,7 @@ export async function disconnect(): Promise<void> {
|
||||
|
||||
// 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<void> {
|
||||
export async function saveFault(
|
||||
deviceId: string,
|
||||
channel: string,
|
||||
fault: SessionFault
|
||||
): Promise<void> {
|
||||
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<string, any>;
|
||||
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<void> {
|
||||
export async function deleteFault(
|
||||
deviceId: string,
|
||||
channel: string
|
||||
): Promise<void> {
|
||||
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<void> {
|
||||
export async function clearTasks(
|
||||
deviceId: string,
|
||||
taskIds: string[]
|
||||
): Promise<void> {
|
||||
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<void> {
|
||||
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<string, any>;
|
||||
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<void> {
|
||||
export async function deleteOperation(
|
||||
deviceId: string,
|
||||
commandKey: string
|
||||
): Promise<void> {
|
||||
await operationsCollection.deleteOne({ _id: `${deviceId}:${commandKey}` });
|
||||
}
|
||||
|
||||
export async function getPresets(): Promise<{}[]> {
|
||||
export async function getPresets(): Promise<Record<string, any>[]> {
|
||||
return presetsCollection.find().toArray();
|
||||
}
|
||||
|
||||
export async function getObjects(): Promise<{}[]> {
|
||||
export async function getObjects(): Promise<Record<string, any>[]> {
|
||||
return objectsCollection.find().toArray();
|
||||
}
|
||||
|
||||
export async function getProvisions(): Promise<{}[]> {
|
||||
export async function getProvisions(): Promise<Record<string, any>[]> {
|
||||
return provisionsCollection.find().toArray();
|
||||
}
|
||||
|
||||
export async function getVirtualParameters(): Promise<{}[]> {
|
||||
export async function getVirtualParameters(): Promise<Record<string, any>[]> {
|
||||
return virtualParametersCollection.find().toArray();
|
||||
}
|
||||
|
||||
export function getFiles(): Promise<{}[]> {
|
||||
export function getFiles(): Promise<Record<string, any>[]> {
|
||||
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"]),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -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")
|
||||
|
||||
+47
-22
@@ -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;
|
||||
|
||||
+24
-15
@@ -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];
|
||||
|
||||
+7
-7
@@ -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<void> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
const timeToKill = Date.now() + 5000;
|
||||
|
||||
process.kill();
|
||||
|
||||
+3
-3
@@ -35,7 +35,7 @@ const FORWARDED_HEADER = "" + config.get("FORWARDED_HEADER");
|
||||
const cache = new WeakMap<IncomingMessage, RequestOrigin>();
|
||||
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") {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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][] = [];
|
||||
|
||||
+50
-49
@@ -95,11 +95,11 @@ export async function getStatus(): Promise<Status> {
|
||||
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<void> {
|
||||
export async function seed(options: Record<string, boolean>): Promise<void> {
|
||||
const resources = {};
|
||||
const proms = [];
|
||||
|
||||
@@ -117,12 +117,12 @@ export async function seed(options): Promise<void> {
|
||||
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<void> {
|
||||
{ _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<void> {
|
||||
{ _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<void> {
|
||||
{ _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<void> {
|
||||
{ _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<void> {
|
||||
{ _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<void> {
|
||||
{ _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<void> {
|
||||
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 },
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+41
-33
@@ -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<string, unknown>,
|
||||
prefix = "",
|
||||
dst = {}
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>, `${prefix}${k}.`, dst);
|
||||
else dst[`${prefix}${k}`] = v;
|
||||
}
|
||||
return dst;
|
||||
@@ -121,14 +125,14 @@ async function fetchPresets(): Promise<Preset[]> {
|
||||
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<Preset[]> {
|
||||
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<Preset[]> {
|
||||
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<Preset[]> {
|
||||
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<Preset[]> {
|
||||
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<Preset[]> {
|
||||
schedule: schedule,
|
||||
events: events,
|
||||
precondition: precondition,
|
||||
provisions: _provisions
|
||||
provisions: _provisions,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,7 +264,9 @@ async function fetchProvisions(): Promise<Provisions> {
|
||||
.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<VirtualParameters> {
|
||||
.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> {
|
||||
|
||||
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> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
fetchFiles(),
|
||||
fetchPermissions(),
|
||||
fetchUsers(),
|
||||
fetchConfig()
|
||||
fetchConfig(),
|
||||
]);
|
||||
|
||||
const snapshot = {
|
||||
@@ -411,7 +416,7 @@ async function refresh(): Promise<void> {
|
||||
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<string> {
|
||||
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<string, unknown>,
|
||||
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");
|
||||
|
||||
|
||||
+74
-45
@@ -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<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>,
|
||||
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<string, unknown>,
|
||||
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<string, unknown>): 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<string, unknown>): void {
|
||||
details.severity = "info";
|
||||
log(details);
|
||||
}
|
||||
|
||||
export function warn(details): void {
|
||||
export function warn(details: Record<string, unknown>): void {
|
||||
details.severity = "warn";
|
||||
log(details);
|
||||
}
|
||||
|
||||
export function error(details): void {
|
||||
export function error(details: Record<string, unknown>): void {
|
||||
details.severity = "error";
|
||||
log(details);
|
||||
}
|
||||
|
||||
export function accessLog(details): void {
|
||||
export function accessLog(details: Record<string, unknown>): 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<string, unknown>): void {
|
||||
details.severity = "info";
|
||||
accessLog(details);
|
||||
}
|
||||
|
||||
export function accessWarn(details): void {
|
||||
export function accessWarn(details: Record<string, unknown>): void {
|
||||
details.severity = "warn";
|
||||
accessLog(details);
|
||||
}
|
||||
|
||||
export function accessError(details): void {
|
||||
export function accessError(details: Record<string, unknown>): void {
|
||||
details.severity = "error";
|
||||
accessLog(details);
|
||||
}
|
||||
+67
-55
@@ -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<string, unknown> {
|
||||
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<string, unknown> {
|
||||
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<string, 1>
|
||||
): Record<string, 1> {
|
||||
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<string, number>
|
||||
): Record<string, number> {
|
||||
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<string, unknown>): 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<string, unknown>): 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<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
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)
|
||||
|
||||
+64
-57
@@ -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]");
|
||||
}
|
||||
|
||||
+5
-5
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+18
-98
@@ -17,7 +17,9 @@
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>,
|
||||
types: Record<string, (v: unknown) => unknown>
|
||||
): Record<string, unknown> {
|
||||
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":
|
||||
|
||||
+15
-15
@@ -157,7 +157,7 @@ class ParameterWrapper {
|
||||
if (!attr) return UNDEFINED;
|
||||
|
||||
return attr[1];
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ class ParameterWrapper {
|
||||
if (!unpacked.length) return UNDEFINED;
|
||||
|
||||
return unpacked[0].toString();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "size", {
|
||||
@@ -196,7 +196,7 @@ class ParameterWrapper {
|
||||
if (!unpacked.length) return UNDEFINED;
|
||||
|
||||
return unpacked.length;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this[Symbol.iterator] = function* () {
|
||||
@@ -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<string, unknown>): 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<string, unknown>,
|
||||
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,
|
||||
};
|
||||
}
|
||||
+15
-10
@@ -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;
|
||||
|
||||
+9
-8
@@ -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);
|
||||
|
||||
+190
-181
File diff suppressed because it is too large.
Load diff
+80
-63
@@ -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<string, unknown>[];
|
||||
|
||||
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 `<cwmp:GetParameterValues><ParameterNames soap-enc:arrayType="xsd:string[${
|
||||
methodRequest.parameterNames.length
|
||||
}]">${methodRequest.parameterNames
|
||||
.map(p => `<string>${p}</string>`)
|
||||
.map((p) => `<string>${p}</string>`)
|
||||
.join("")}</ParameterNames></cwmp:GetParameterValues>`;
|
||||
}
|
||||
|
||||
@@ -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 `<cwmp:SetParameterValues><ParameterList soap-enc:arrayType="cwmp:ParameterValueStruct[${
|
||||
methodRequest.parameterList.length
|
||||
}]">${params.join(
|
||||
""
|
||||
)}</ParameterList><ParameterKey>${methodRequest.parameterKey ||
|
||||
""}</ParameterKey></cwmp:SetParameterValues>`;
|
||||
}]">${params.join("")}</ParameterList><ParameterKey>${
|
||||
methodRequest.parameterKey || ""
|
||||
}</ParameterKey></cwmp:SetParameterValues>`;
|
||||
}
|
||||
|
||||
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 `<cwmp:AddObject><ObjectName>${
|
||||
methodRequest.objectName
|
||||
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
|
||||
""}</ParameterKey></cwmp:AddObject>`;
|
||||
}</ObjectName><ParameterKey>${
|
||||
methodRequest.parameterKey || ""
|
||||
}</ParameterKey></cwmp:AddObject>`;
|
||||
}
|
||||
|
||||
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 `<cwmp:DeleteObject><ObjectName>${
|
||||
methodRequest.objectName
|
||||
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
|
||||
""}</ParameterKey></cwmp:DeleteObject>`;
|
||||
}</ObjectName><ParameterKey>${
|
||||
methodRequest.parameterKey || ""
|
||||
}</ParameterKey></cwmp:DeleteObject>`;
|
||||
}
|
||||
|
||||
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 `<cwmp:Reboot><CommandKey>${methodRequest.commandKey ||
|
||||
""}</CommandKey></cwmp:Reboot>`;
|
||||
return `<cwmp:Reboot><CommandKey>${
|
||||
methodRequest.commandKey || ""
|
||||
}</CommandKey></cwmp:Reboot>`;
|
||||
}
|
||||
|
||||
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 `<cwmp:Download><CommandKey>${methodRequest.commandKey ||
|
||||
""}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
|
||||
return `<cwmp:Download><CommandKey>${
|
||||
methodRequest.commandKey || ""
|
||||
}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
|
||||
methodRequest.url
|
||||
}</URL><Username>${encodeEntities(
|
||||
methodRequest.username || ""
|
||||
)}</Username><Password>${encodeEntities(
|
||||
methodRequest.password || ""
|
||||
)}</Password><FileSize>${methodRequest.fileSize ||
|
||||
"0"}</FileSize><TargetFileName>${encodeEntities(
|
||||
)}</Password><FileSize>${
|
||||
methodRequest.fileSize || "0"
|
||||
}</FileSize><TargetFileName>${encodeEntities(
|
||||
methodRequest.targetFileName || ""
|
||||
)}</TargetFileName><DelaySeconds>${methodRequest.delaySeconds ||
|
||||
"0"}</DelaySeconds><SuccessURL>${encodeEntities(
|
||||
)}</TargetFileName><DelaySeconds>${
|
||||
methodRequest.delaySeconds || "0"
|
||||
}</DelaySeconds><SuccessURL>${encodeEntities(
|
||||
methodRequest.successUrl || ""
|
||||
)}</SuccessURL><FailureURL>${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 `<cwmp:GetRPCMethodsResponse><MethodList soap-enc:arrayType="xsd:string[${
|
||||
methodResponse.methodList.length
|
||||
}]">${methodResponse.methodList
|
||||
.map(m => `<string>${m}</string>`)
|
||||
.map((m) => `<string>${m}</string>`)
|
||||
.join("")}</MethodList></cwmp:GetRPCMethodsResponse>`;
|
||||
}
|
||||
|
||||
@@ -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<string, unknown>[]
|
||||
): 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<string, string>; 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]
|
||||
}><soap-env:Header><cwmp:ID soap-env:mustUnderstand="1">${
|
||||
rpc.id
|
||||
}</cwmp:ID></soap-env:Header><soap-env:Body>${body}</soap-env:Body></soap-env:Envelope>`
|
||||
}</cwmp:ID></soap-env:Header><soap-env:Body>${body}</soap-env:Body></soap-env:Envelope>`,
|
||||
};
|
||||
}
|
||||
+8
-8
@@ -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<string, unknown>;
|
||||
device: Record<string, unknown>;
|
||||
index: Record<string, unknown>;
|
||||
overview: {
|
||||
charts?: {};
|
||||
groups?: {};
|
||||
charts?: Record<string, unknown>;
|
||||
groups?: Record<string, unknown>;
|
||||
};
|
||||
pageSize?: Expression;
|
||||
}
|
||||
|
||||
@@ -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 => {
|
||||
<noscript>GenieACS UI requires JavaScript to work. Please enable JavaScript in your browser.</noscript>
|
||||
<script>
|
||||
window.clientConfig = ${JSON.stringify({
|
||||
ui: localCache.getUiConfig(ctx.state.configSnapshot)
|
||||
ui: localCache.getUiConfig(ctx.state.configSnapshot),
|
||||
})};
|
||||
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
|
||||
window.genieacsVersion = ${JSON.stringify(VERSION)};
|
||||
@@ -251,7 +251,7 @@ router.get("/", async ctx => {
|
||||
|
||||
koa.use(
|
||||
koaCompress({
|
||||
flush: Z_SYNC_FLUSH
|
||||
flush: Z_SYNC_FLUSH,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
+30
-17
@@ -26,8 +26,9 @@ import {
|
||||
insertTasks,
|
||||
watchTask,
|
||||
connectionRequest,
|
||||
deleteDevice
|
||||
deleteDevice,
|
||||
} from "../api-functions";
|
||||
import { Task } from "../types";
|
||||
|
||||
async function deleteFault(id): Promise<void> {
|
||||
const deviceId = id.split(":", 1)[0];
|
||||
@@ -37,13 +38,16 @@ async function deleteFault(id): Promise<void> {
|
||||
db.deleteFault(id),
|
||||
channel.startsWith("task_")
|
||||
? db.deleteTask(new ObjectID(channel.slice(5)))
|
||||
: null
|
||||
: null,
|
||||
]);
|
||||
|
||||
await del(`${deviceId}_tasks_faults_operations`);
|
||||
}
|
||||
|
||||
export async function deleteResource(resource, id): Promise<void> {
|
||||
export async function deleteResource(
|
||||
resource: string,
|
||||
id: string
|
||||
): Promise<void> {
|
||||
switch (resource) {
|
||||
case "devices":
|
||||
await deleteDevice(id);
|
||||
@@ -86,18 +90,18 @@ export async function deleteResource(resource, id): Promise<void> {
|
||||
}
|
||||
|
||||
export async function postTasks(
|
||||
deviceId,
|
||||
tasks,
|
||||
timeout,
|
||||
device
|
||||
deviceId: string,
|
||||
tasks: Task[],
|
||||
timeout: number,
|
||||
device: Record<string, { value?: [boolean | number | string, string] }>
|
||||
): Promise<{ connectionRequest: string; tasks: any[] }> {
|
||||
for (const task of tasks) {
|
||||
delete task._id;
|
||||
task.device = deviceId;
|
||||
task["device"] = deviceId;
|
||||
}
|
||||
|
||||
tasks = await insertTasks(tasks);
|
||||
const statuses = tasks.map(t => {
|
||||
const statuses = tasks.map((t) => {
|
||||
return { _id: t._id, status: "pending" };
|
||||
});
|
||||
|
||||
@@ -106,9 +110,10 @@ export async function postTasks(
|
||||
try {
|
||||
await connectionRequest(deviceId, device);
|
||||
} catch (err) {
|
||||
for (const t of statuses) db.deleteTask(new ObjectID(t._id));
|
||||
return {
|
||||
connectionRequest: err.message,
|
||||
tasks: statuses
|
||||
tasks: statuses,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,9 +136,9 @@ export async function postTasks(
|
||||
r.status = "done";
|
||||
} else if (res[i * 2 + 1].length === 1) {
|
||||
r.status = "fault";
|
||||
r.fault = res[i * 2 + 1][0];
|
||||
r["fault"] = res[i * 2 + 1][0];
|
||||
}
|
||||
db.deleteTask(r._id);
|
||||
db.deleteTask(new ObjectID(r._id));
|
||||
}
|
||||
|
||||
return { connectionRequest: "OK", tasks: statuses };
|
||||
@@ -149,7 +154,11 @@ interface PingResponse {
|
||||
mdev: number;
|
||||
}
|
||||
|
||||
export async function putResource(resource, id, data): Promise<void> {
|
||||
export async function putResource(
|
||||
resource: string,
|
||||
id: string,
|
||||
data: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
if (resource === "presets") {
|
||||
await db.putPreset(id, data);
|
||||
} else if (resource === "provisions") {
|
||||
@@ -161,21 +170,25 @@ export async function putResource(resource, id, data): Promise<void> {
|
||||
} else if (resource === "permissions") {
|
||||
await db.putPermission(id, data);
|
||||
} else if (resource === "users") {
|
||||
delete data.password;
|
||||
delete data.salt;
|
||||
delete data["password"];
|
||||
delete data["salt"];
|
||||
await db.putUser(id, data);
|
||||
}
|
||||
|
||||
await del("presets_hash");
|
||||
}
|
||||
|
||||
export function authLocal(snapshot, username, password): Promise<boolean> {
|
||||
export function authLocal(
|
||||
snapshot: string,
|
||||
username: string,
|
||||
password: string
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const users = getUsers(snapshot);
|
||||
const user = users[username];
|
||||
if (!user || !user.password) return void resolve(null);
|
||||
hashPassword(password, user.salt)
|
||||
.then(hash => {
|
||||
.then((hash) => {
|
||||
if (hash === user.password) resolve(true);
|
||||
else resolve(false);
|
||||
})
|
||||
|
||||
+41
-39
@@ -53,7 +53,7 @@ const RESOURCE_IDS = {
|
||||
permissions: "_id",
|
||||
users: "_id",
|
||||
faults: "_id",
|
||||
tasks: "_id"
|
||||
tasks: "_id",
|
||||
};
|
||||
|
||||
const resources = {
|
||||
@@ -66,21 +66,21 @@ const resources = {
|
||||
permissions: 0 | RESOURCE_DELETE | RESOURCE_PUT,
|
||||
users: 0 | RESOURCE_DELETE | RESOURCE_PUT,
|
||||
faults: 0 | RESOURCE_DELETE,
|
||||
tasks: 0
|
||||
tasks: 0,
|
||||
};
|
||||
|
||||
router.get(`/devices/:id.csv`, async ctx => {
|
||||
router.get(`/devices/:id.csv`, async (ctx) => {
|
||||
const authorizer: Authorizer = ctx.state.authorizer;
|
||||
const log = {
|
||||
message: "Query device (CSV)",
|
||||
context: ctx,
|
||||
id: ctx.params.id
|
||||
id: ctx.params.id,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter("devices", 2), [
|
||||
"=",
|
||||
["PARAM", RESOURCE_IDS.devices],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
|
||||
if (!authorizer.hasAccess("devices", 2)) {
|
||||
@@ -113,9 +113,9 @@ router.get(`/devices/:id.csv`, async ctx => {
|
||||
p.writableTimestamp,
|
||||
p.value != null ? `"${p.value[0].toString().replace(/"/g, '""')}"` : "",
|
||||
p.value != null ? p.value[1] : "",
|
||||
p.valueTimestamp
|
||||
p.valueTimestamp,
|
||||
];
|
||||
ctx.body.write(row.map(r => (r != null ? r : "")).join(",") + "\n");
|
||||
ctx.body.write(row.map((r) => (r != null ? r : "")).join(",") + "\n");
|
||||
}
|
||||
ctx.body.end();
|
||||
logger.accessInfo(log);
|
||||
@@ -132,7 +132,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
message: `Count ${resource}`,
|
||||
context: ctx,
|
||||
filter: ctx.request.query.filter,
|
||||
count: null
|
||||
count: null,
|
||||
};
|
||||
|
||||
if (!authorizer.hasAccess(resource, 1)) {
|
||||
@@ -144,7 +144,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
if (resource === "tasks" || resource === "faults") {
|
||||
filter = and(filter, [
|
||||
"NOT",
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
limit: options.limit,
|
||||
skip: options.skip,
|
||||
sort: options.sort,
|
||||
projection: options.projection
|
||||
projection: options.projection,
|
||||
};
|
||||
|
||||
if (!authorizer.hasAccess(resource, 2)) {
|
||||
@@ -192,7 +192,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
if (resource === "tasks" || resource === "faults") {
|
||||
filter = and(filter, [
|
||||
"NOT",
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
|
||||
let c = 0;
|
||||
ctx.body.write("[\n");
|
||||
await db.query(resource, filter, options, obj => {
|
||||
await db.query(resource, filter, options, (obj) => {
|
||||
ctx.body.write((c++ ? "," : "") + JSON.stringify(obj) + "\n");
|
||||
});
|
||||
ctx.body.end("]");
|
||||
@@ -227,7 +227,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
filter: ctx.request.query.filter,
|
||||
limit: options.limit,
|
||||
skip: options.skip,
|
||||
sort: options.sort
|
||||
sort: options.sort,
|
||||
};
|
||||
|
||||
if (!authorizer.hasAccess(resource, 2)) {
|
||||
@@ -235,7 +235,9 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
return void next();
|
||||
}
|
||||
|
||||
const columns = JSON.parse(ctx.request.query.columns);
|
||||
const columns: Record<string, Expression> = JSON.parse(
|
||||
ctx.request.query.columns
|
||||
);
|
||||
const now = Date.now();
|
||||
|
||||
for (const [k, v] of Object.entries(columns)) {
|
||||
@@ -249,7 +251,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
if (resource === "tasks" || resource === "faults") {
|
||||
filter = and(filter, [
|
||||
"NOT",
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||
["<", ["PARAM", "expiry"], Date.now() + 60000],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -260,11 +262,11 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
);
|
||||
|
||||
ctx.body.write(
|
||||
Object.keys(columns).map(k => `"${k.replace(/"/, '""')}"`) + "\n"
|
||||
Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) + "\n"
|
||||
);
|
||||
await db.query(resource, filter, options, obj => {
|
||||
const arr = Object.values(columns).map(exp => {
|
||||
const v = evaluate(exp, obj, null, e => {
|
||||
await db.query(resource, filter, options, (obj) => {
|
||||
const arr = Object.values(columns).map((exp) => {
|
||||
const v = evaluate(exp, obj, null, (e) => {
|
||||
if (Array.isArray(e)) {
|
||||
if (e[0] === "PARAM") {
|
||||
if (resource === "devices") {
|
||||
@@ -303,13 +305,13 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
const log = {
|
||||
message: `Count ${resource}`,
|
||||
context: ctx,
|
||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter(resource, 2), [
|
||||
"=",
|
||||
["PARAM", RESOURCE_IDS[resource]],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
if (!authorizer.hasAccess(resource, 2)) {
|
||||
logUnauthorizedWarning(log);
|
||||
@@ -329,13 +331,13 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
const log = {
|
||||
message: `Query ${resource}`,
|
||||
context: ctx,
|
||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter(resource, 2), [
|
||||
"=",
|
||||
["PARAM", RESOURCE_IDS[resource]],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
if (!authorizer.hasAccess(resource, 2)) {
|
||||
logUnauthorizedWarning(log);
|
||||
@@ -356,13 +358,13 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
const log = {
|
||||
message: `Delete ${resource}`,
|
||||
context: ctx,
|
||||
id: ctx.params.id
|
||||
id: ctx.params.id,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter(resource, 3), [
|
||||
"=",
|
||||
["PARAM", RESOURCE_IDS[resource]],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
if (!authorizer.hasAccess(resource, 3)) {
|
||||
logUnauthorizedWarning(log);
|
||||
@@ -393,7 +395,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
||||
const log = {
|
||||
message: `Put ${resource}`,
|
||||
context: ctx,
|
||||
id: id
|
||||
id: id,
|
||||
};
|
||||
|
||||
if (!authorizer.hasAccess(resource, 3)) {
|
||||
@@ -434,7 +436,7 @@ router.put("/files/:id", async (ctx, next) => {
|
||||
message: `Upload ${resource}`,
|
||||
context: ctx,
|
||||
id: id,
|
||||
metadata: null
|
||||
metadata: null,
|
||||
};
|
||||
|
||||
if (!authorizer.hasAccess(resource, 3)) {
|
||||
@@ -446,7 +448,7 @@ router.put("/files/:id", async (ctx, next) => {
|
||||
fileType: ctx.request.headers["metadata.filetype"] || "",
|
||||
oui: ctx.request.headers["metadata.oui"] || "",
|
||||
productClass: ctx.request.headers["metadata.productclass"] || "",
|
||||
version: ctx.request.headers["metadata.version"] || ""
|
||||
version: ctx.request.headers["metadata.version"] || "",
|
||||
};
|
||||
|
||||
const validate = authorizer.getValidator(resource, metadata);
|
||||
@@ -474,13 +476,13 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
|
||||
message: "Commit tasks",
|
||||
context: ctx,
|
||||
deviceId: ctx.params.id,
|
||||
tasks: null
|
||||
tasks: null,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter("devices", 3), [
|
||||
"=",
|
||||
["PARAM", "DeviceID.ID"],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
if (!authorizer.hasAccess("devices", 3)) {
|
||||
logUnauthorizedWarning(log);
|
||||
@@ -503,7 +505,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
|
||||
"cwmp.deviceOnlineThreshold",
|
||||
{},
|
||||
Date.now(),
|
||||
exp => {
|
||||
(exp) => {
|
||||
if (!Array.isArray(exp)) return exp;
|
||||
if (exp[0] === "PARAM") {
|
||||
const p = device[exp[1]];
|
||||
@@ -519,7 +521,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
);
|
||||
) as number;
|
||||
|
||||
const res = await apiFunctions.postTasks(
|
||||
ctx.params.id,
|
||||
@@ -528,7 +530,7 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
|
||||
device
|
||||
);
|
||||
|
||||
log.tasks = res.tasks.map(t => t._id).join(",");
|
||||
log.tasks = res.tasks.map((t) => t._id).join(",");
|
||||
|
||||
logger.accessInfo(log);
|
||||
|
||||
@@ -542,13 +544,13 @@ router.post("/devices/:id/tags", async (ctx, next) => {
|
||||
message: "Update tags",
|
||||
context: ctx,
|
||||
deviceId: ctx.params.id,
|
||||
tags: ctx.request.body
|
||||
tags: ctx.request.body,
|
||||
};
|
||||
|
||||
const filter = and(authorizer.getFilter("devices", 3), [
|
||||
"=",
|
||||
["PARAM", "DeviceID.ID"],
|
||||
ctx.params.id
|
||||
ctx.params.id,
|
||||
]);
|
||||
if (!authorizer.hasAccess("devices", 3)) {
|
||||
logUnauthorizedWarning(log);
|
||||
@@ -577,8 +579,8 @@ router.post("/devices/:id/tags", async (ctx, next) => {
|
||||
ctx.body = "";
|
||||
});
|
||||
|
||||
router.get("/ping/:host", async ctx => {
|
||||
return new Promise(resolve => {
|
||||
router.get("/ping/:host", async (ctx) => {
|
||||
return new Promise((resolve) => {
|
||||
ping(ctx.params.host, (err, parsed) => {
|
||||
if (parsed) {
|
||||
ctx.body = parsed;
|
||||
@@ -597,7 +599,7 @@ router.put("/users/:id/password", async (ctx, next) => {
|
||||
const log = {
|
||||
message: "Change password",
|
||||
context: ctx,
|
||||
username: username
|
||||
username: username,
|
||||
};
|
||||
|
||||
if (!ctx.state.user) {
|
||||
@@ -622,7 +624,7 @@ router.put("/users/:id/password", async (ctx, next) => {
|
||||
const filter = and(authorizer.getFilter("users", 3), [
|
||||
"=",
|
||||
["PARAM", RESOURCE_IDS.users],
|
||||
username
|
||||
username,
|
||||
]);
|
||||
const res = await db.query("users", filter);
|
||||
if (!res.length) return void next();
|
||||
|
||||
+83
-50
@@ -17,19 +17,20 @@
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { MongoClient, GridFSBucket } from "mongodb";
|
||||
import { MongoClient, GridFSBucket, ObjectID } from "mongodb";
|
||||
import { Script } from "vm";
|
||||
import * as config from "../config";
|
||||
import * as mongodbFunctions from "../mongodb-functions";
|
||||
import * as expression from "../common/expression";
|
||||
import { QueryOptions } from "../types";
|
||||
import { QueryOptions, Expression } from "../types";
|
||||
import { Readable } from "stream";
|
||||
|
||||
const CACHE_TTL = 300000;
|
||||
|
||||
let clientPromise: Promise<MongoClient>;
|
||||
|
||||
const RESOURCE_COLLECTION = {
|
||||
files: "fs.files"
|
||||
files: "fs.files",
|
||||
};
|
||||
|
||||
function ensureIndexes(client): void {
|
||||
@@ -58,27 +59,31 @@ function getClient(): Promise<MongoClient> {
|
||||
return clientPromise;
|
||||
}
|
||||
|
||||
export function cache<T>(key, valueGetter: () => Promise<T>, ttl): Promise<T> {
|
||||
export function cache<T>(
|
||||
key: string,
|
||||
valueGetter: () => Promise<T>,
|
||||
ttl: number
|
||||
): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const collection = client.db().collection("cache");
|
||||
collection.findOne({ _id: key }, (err, doc) => {
|
||||
if (err) return void reject(err);
|
||||
if (doc != null) return void resolve(JSON.parse(doc.value) as T);
|
||||
valueGetter()
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
const expire = Date.now() + (ttl || CACHE_TTL);
|
||||
const cacheDoc = {
|
||||
_id: key,
|
||||
value: JSON.stringify(res),
|
||||
expire: new Date(expire)
|
||||
expire: new Date(expire),
|
||||
};
|
||||
collection.updateOne(
|
||||
{ _id: key },
|
||||
{ $set: cacheDoc },
|
||||
{ upsert: true },
|
||||
err => {
|
||||
(err) => {
|
||||
if (err) reject(err);
|
||||
else resolve(res);
|
||||
}
|
||||
@@ -92,8 +97,8 @@ export function cache<T>(key, valueGetter: () => Promise<T>, ttl): Promise<T> {
|
||||
}
|
||||
|
||||
export function query(
|
||||
resource,
|
||||
filter,
|
||||
resource: string,
|
||||
filter: Expression,
|
||||
options?: QueryOptions,
|
||||
callback?: (doc: any) => void
|
||||
): Promise<any[]> {
|
||||
@@ -121,7 +126,7 @@ export function query(
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient().then(client => {
|
||||
getClient().then((client) => {
|
||||
const collection = client
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
@@ -160,20 +165,20 @@ export function query(
|
||||
cursor.toArray((err, docs) => {
|
||||
if (err) return reject(err);
|
||||
if (resource === "devices")
|
||||
docs = docs.map(d => mongodbFunctions.flattenDevice(d));
|
||||
docs = docs.map((d) => mongodbFunctions.flattenDevice(d));
|
||||
else if (resource === "faults")
|
||||
docs = docs.map(d => mongodbFunctions.flattenFault(d));
|
||||
docs = docs.map((d) => mongodbFunctions.flattenFault(d));
|
||||
else if (resource === "tasks")
|
||||
docs = docs.map(d => mongodbFunctions.flattenTask(d));
|
||||
docs = docs.map((d) => mongodbFunctions.flattenTask(d));
|
||||
else if (resource === "presets")
|
||||
docs = docs.map(d => mongodbFunctions.flattenPreset(d));
|
||||
docs = docs.map((d) => mongodbFunctions.flattenPreset(d));
|
||||
else if (resource === "files")
|
||||
docs = docs.map(d => mongodbFunctions.flattenFile(d));
|
||||
docs = docs.map((d) => mongodbFunctions.flattenFile(d));
|
||||
return resolve(docs);
|
||||
});
|
||||
} else {
|
||||
cursor.forEach(
|
||||
doc => {
|
||||
(doc) => {
|
||||
if (resource === "devices")
|
||||
doc = mongodbFunctions.flattenDevice(doc);
|
||||
else if (resource === "faults")
|
||||
@@ -186,7 +191,7 @@ export function query(
|
||||
doc = mongodbFunctions.flattenFile(doc);
|
||||
callback(doc);
|
||||
},
|
||||
err => {
|
||||
(err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
}
|
||||
@@ -196,7 +201,7 @@ export function query(
|
||||
});
|
||||
}
|
||||
|
||||
export function count(resource, filter): Promise<number> {
|
||||
export function count(resource: string, filter: Expression): Promise<number> {
|
||||
let q;
|
||||
filter = expression.evaluate(filter, null, Date.now());
|
||||
|
||||
@@ -213,7 +218,7 @@ export function count(resource, filter): Promise<number> {
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient().then(client => {
|
||||
getClient().then((client) => {
|
||||
const collection = client
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
@@ -225,7 +230,10 @@ export function count(resource, filter): Promise<number> {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateDeviceTags(deviceId, tags): Promise<void> {
|
||||
export function updateDeviceTags(
|
||||
deviceId: string,
|
||||
tags: Record<string, boolean>
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const add = [];
|
||||
const pull = [];
|
||||
@@ -242,13 +250,13 @@ export function updateDeviceTags(deviceId, tags): Promise<void> {
|
||||
}
|
||||
}
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const collection = client.db().collection("devices");
|
||||
|
||||
const object = {};
|
||||
if (add && add.length) object["$addToSet"] = { _tags: { $each: add } };
|
||||
if (pull && pull.length) object["$pullAll"] = { _tags: pull };
|
||||
collection.updateOne({ _id: deviceId }, object, err => {
|
||||
collection.updateOne({ _id: deviceId }, object, (err) => {
|
||||
if (err) return void reject(err);
|
||||
resolve();
|
||||
});
|
||||
@@ -260,11 +268,11 @@ export function updateDeviceTags(deviceId, tags): Promise<void> {
|
||||
function putResource(resource, id, object): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const collection = client
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
collection.replaceOne({ _id: id }, object, { upsert: true }, err => {
|
||||
collection.replaceOne({ _id: id }, object, { upsert: true }, (err) => {
|
||||
if (err) return void reject(err);
|
||||
resolve();
|
||||
});
|
||||
@@ -273,14 +281,17 @@ function putResource(resource, id, object): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function deleteResource(resource, id): Promise<void> {
|
||||
function deleteResource(
|
||||
resource: string,
|
||||
id: string | ObjectID
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const collection = client
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
collection.deleteOne({ _id: id }, err => {
|
||||
collection.deleteOne({ _id: id }, (err) => {
|
||||
if (err) return void reject(err);
|
||||
resolve();
|
||||
});
|
||||
@@ -289,16 +300,22 @@ function deleteResource(resource, id): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function putPreset(id, object): Promise<void> {
|
||||
export function putPreset(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
object = mongodbFunctions.preProcessPreset(object);
|
||||
return putResource("presets", id, object);
|
||||
}
|
||||
|
||||
export function deletePreset(id): Promise<void> {
|
||||
export function deletePreset(id: string): Promise<void> {
|
||||
return deleteResource("presets", id);
|
||||
}
|
||||
|
||||
export function putProvision(id, object): Promise<void> {
|
||||
export function putProvision(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
if (!object.script) object.script = "";
|
||||
try {
|
||||
new Script(`"use strict";(function(){\n${object.script}\n})();`);
|
||||
@@ -308,11 +325,14 @@ export function putProvision(id, object): Promise<void> {
|
||||
return putResource("provisions", id, object);
|
||||
}
|
||||
|
||||
export function deleteProvision(id): Promise<void> {
|
||||
export function deleteProvision(id: string): Promise<void> {
|
||||
return deleteResource("provisions", id);
|
||||
}
|
||||
|
||||
export function putVirtualParameter(id, object): Promise<void> {
|
||||
export function putVirtualParameter(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
if (!object.script) object.script = "";
|
||||
try {
|
||||
new Script(`"use strict";(function(){\n${object.script}\n})();`);
|
||||
@@ -322,37 +342,46 @@ export function putVirtualParameter(id, object): Promise<void> {
|
||||
return putResource("virtualParameters", id, object);
|
||||
}
|
||||
|
||||
export function deleteVirtualParameter(id): Promise<void> {
|
||||
export function deleteVirtualParameter(id: string): Promise<void> {
|
||||
return deleteResource("virtualParameters", id);
|
||||
}
|
||||
|
||||
export function putConfig(id, object): Promise<void> {
|
||||
export function putConfig(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
return putResource("config", id, object);
|
||||
}
|
||||
|
||||
export function deleteConfig(id): Promise<void> {
|
||||
export function deleteConfig(id: string): Promise<void> {
|
||||
return deleteResource("config", id);
|
||||
}
|
||||
|
||||
export function putPermission(id, object): Promise<void> {
|
||||
export function putPermission(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
return putResource("permissions", id, object);
|
||||
}
|
||||
|
||||
export function deletePermission(id): Promise<void> {
|
||||
export function deletePermission(id: string): Promise<void> {
|
||||
return deleteResource("permissions", id);
|
||||
}
|
||||
|
||||
export function putUser(id, object): Promise<void> {
|
||||
export function putUser(
|
||||
id: string,
|
||||
object: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const collection = client.db().collection("users");
|
||||
// update instead of replace to keep the password if not set by user
|
||||
collection.updateOne(
|
||||
{ _id: id },
|
||||
{ $set: object },
|
||||
{ upsert: true },
|
||||
err => {
|
||||
(err) => {
|
||||
if (err) return void reject(err);
|
||||
resolve();
|
||||
}
|
||||
@@ -362,17 +391,21 @@ export function putUser(id, object): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteUser(id): Promise<void> {
|
||||
export function deleteUser(id: string): Promise<void> {
|
||||
return deleteResource("users", id);
|
||||
}
|
||||
|
||||
export function putFile(filename, metadata, contentStream): Promise<void> {
|
||||
export function putFile(
|
||||
filename: string,
|
||||
metadata: Record<string, string>,
|
||||
contentStream: Readable
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const bucket = new GridFSBucket(client.db());
|
||||
const uploadStream = bucket.openUploadStreamWithId(filename, filename, {
|
||||
metadata: metadata
|
||||
metadata: metadata,
|
||||
});
|
||||
uploadStream.on("error", reject);
|
||||
contentStream.on("error", reject);
|
||||
@@ -383,12 +416,12 @@ export function putFile(filename, metadata, contentStream): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteFile(filename): Promise<void> {
|
||||
export function deleteFile(filename: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
const bucket = new GridFSBucket(client.db());
|
||||
bucket.delete(filename, err => {
|
||||
bucket.delete(filename as any, (err) => {
|
||||
if (err) return void reject(err);
|
||||
resolve();
|
||||
});
|
||||
@@ -397,11 +430,11 @@ export function deleteFile(filename): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteFault(id): Promise<void> {
|
||||
export function deleteFault(id: string): Promise<void> {
|
||||
return deleteResource("faults", id);
|
||||
}
|
||||
|
||||
export function deleteTask(id): Promise<void> {
|
||||
export function deleteTask(id: ObjectID): Promise<void> {
|
||||
return deleteResource("tasks", id);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class VersionedMap<K, V> {
|
||||
return this._revision;
|
||||
}
|
||||
|
||||
public set revision(rev) {
|
||||
public set revision(rev: number) {
|
||||
for (let i = this._sizeDiff.length; i <= rev; ++i)
|
||||
this._sizeDiff[i] = this._sizeDiff[i - 1];
|
||||
|
||||
@@ -193,7 +193,7 @@ export default class VersionedMap<K, V> {
|
||||
const last = v[l];
|
||||
v.splice(revision, l - revision);
|
||||
|
||||
if (last === NONEXISTENT && !v.some(vv => vv !== NONEXISTENT))
|
||||
if (last === NONEXISTENT && !v.some((vv) => vv !== NONEXISTENT))
|
||||
this.map.delete(k);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -90,7 +90,7 @@ export function parseAttrs(string: string): Attribute[] {
|
||||
name: name,
|
||||
namespace: namespace,
|
||||
localName: localName,
|
||||
value: value
|
||||
value: value,
|
||||
};
|
||||
attrs.push(e);
|
||||
name = "";
|
||||
@@ -128,8 +128,8 @@ export function parseAttrs(string: string): Attribute[] {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
export function decodeEntities(string): string {
|
||||
return string.replace(/&[0-9a-z#]+;/gi, match => {
|
||||
export function decodeEntities(string: string): string {
|
||||
return string.replace(/&[0-9a-z#]+;/gi, (match) => {
|
||||
switch (match) {
|
||||
case """:
|
||||
return '"';
|
||||
@@ -161,15 +161,15 @@ export function decodeEntities(string): string {
|
||||
});
|
||||
}
|
||||
|
||||
export function encodeEntities(string): string {
|
||||
export function encodeEntities(string: string): string {
|
||||
const entities = {
|
||||
"&": "&",
|
||||
'"': """,
|
||||
"'": "'",
|
||||
"<": "<",
|
||||
">": ">"
|
||||
">": ">",
|
||||
};
|
||||
return string.replace(/[&"'<>]/g, m => entities[m]);
|
||||
return string.replace(/[&"'<>]/g, (m) => entities[m]);
|
||||
}
|
||||
|
||||
export function parseXml(string: string): Element {
|
||||
@@ -186,7 +186,7 @@ export function parseXml(string: string): Element {
|
||||
attrs: "",
|
||||
text: "",
|
||||
bodyIndex: 0,
|
||||
children: []
|
||||
children: [],
|
||||
};
|
||||
|
||||
const stack: Element[] = [root];
|
||||
@@ -330,7 +330,7 @@ export function parseXml(string: string): Element {
|
||||
: "",
|
||||
text: "",
|
||||
bodyIndex: i + 1,
|
||||
children: []
|
||||
children: [],
|
||||
};
|
||||
parent.children.push(e);
|
||||
if (!selfClosing) stack.push(e);
|
||||
|
||||
Generated
+560
-447
File diff suppressed because it is too large.
Load diff
+9
-11
@@ -46,28 +46,27 @@
|
||||
"@types/parsimmon": "^1.10.0",
|
||||
"@types/seedrandom": "^2.4.28",
|
||||
"@types/svgo": "^1.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^1.13.0",
|
||||
"@typescript-eslint/parser": "^1.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||
"@typescript-eslint/parser": "^3.7.0",
|
||||
"ava": "^2.2.0",
|
||||
"codemirror": "^5.48.2",
|
||||
"cssnano": "^4.1.10",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"esm": "^3.2.25",
|
||||
"mithril": "^2.0.3",
|
||||
"postcss": "^7.0.17",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prettier": "^1.18.2",
|
||||
"prettier": "^2.0.5",
|
||||
"rollup": "^1.17.0",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-terser": "^5.1.1",
|
||||
"rollup-plugin-typescript": "^1.0.1",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"svgo": "^1.3.0",
|
||||
"ts-node": "^8.3.0",
|
||||
"typescript": "^3.5.3",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.9.7",
|
||||
"webpack": "^4.38.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -75,11 +74,10 @@
|
||||
},
|
||||
"scripts": {
|
||||
"test": "ava --verbose --color",
|
||||
"lint-ln": "find bin -type f -exec sh -c 'ln -fs \"$(sh -c pwd)/$1\" \"bin/.${1#bin/}.ts\"' _ {} \\;; find tools -type f -exec sh -c 'ln -fs \"$(sh -c pwd)/$1\" \"tools/.${1#tools/}.ts\"' _ {} \\;",
|
||||
"lint-eslint": "eslint --fix \"bin/**/*.ts\" \"lib/**/*.ts\" \"ui/**/*.ts\" \"test/**/*.ts\" \"build/**/*.ts\" \"tools/**/*.ts\"",
|
||||
"lint-eslint": "eslint --fix \"bin/*.ts\" \"lib/**/*.ts\" \"ui/**/*.ts\" \"test/**/*.ts\" \"build/**/*.ts\" \"tools/*.ts\"",
|
||||
"lint-prettier": "prettier --parser typescript --write \"bin/**/*.ts\" \"lib/**/*.ts\" \"ui/**/*.ts\" \"test/**/*.ts\" \"build/**/*.ts\" \"tools/**/*.ts\"",
|
||||
"lint-tsc": "tsc --noEmit",
|
||||
"lint": "npm run lint-ln && npm run lint-eslint && npm run lint-prettier && npm run lint-tsc",
|
||||
"lint": "npm run lint-eslint && npm run lint-prettier && npm run lint-tsc",
|
||||
"build": "node -r esm -r ts-node/register/transpile-only build/build"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,7 +2,7 @@ import ava from "ava";
|
||||
import { randomBytes } from "crypto";
|
||||
import * as auth from "../lib/auth";
|
||||
|
||||
ava("digest", t => {
|
||||
ava("digest", (t) => {
|
||||
const username = "test";
|
||||
const password = "test";
|
||||
const uri = "/";
|
||||
@@ -14,7 +14,7 @@ ava("digest", t => {
|
||||
const challenges = [
|
||||
`Digest realm="${realm}",nonce="${nonce}"`,
|
||||
`Digest realm="${realm}",nonce="${nonce}",qop="auth"`,
|
||||
`Digest realm="${realm}",nonce="${nonce}",qop="auth-int"`
|
||||
`Digest realm="${realm}",nonce="${nonce}",qop="auth-int"`,
|
||||
];
|
||||
|
||||
t.plan(challenges.length);
|
||||
|
||||
+11
-38
@@ -1,35 +1,7 @@
|
||||
import ava from "ava";
|
||||
import * as common from "../lib/common";
|
||||
|
||||
ava("typeOf", t => {
|
||||
const cases = [
|
||||
[undefined, common.UNDEFINED_TYPE],
|
||||
[null, common.NULL_TYPE],
|
||||
[true, common.BOOLEAN_TYPE],
|
||||
[false, common.BOOLEAN_TYPE],
|
||||
[0, common.NUMBER_TYPE],
|
||||
[1, common.NUMBER_TYPE],
|
||||
[-1, common.NUMBER_TYPE],
|
||||
[0.0, common.NUMBER_TYPE],
|
||||
[1.0, common.NUMBER_TYPE],
|
||||
[1.1, common.NUMBER_TYPE],
|
||||
[-1.0, common.NUMBER_TYPE],
|
||||
[-1.1, common.NUMBER_TYPE],
|
||||
["", common.STRING_TYPE],
|
||||
["TEST", common.STRING_TYPE],
|
||||
[[], common.ARRAY_TYPE],
|
||||
[[1, 2, 3], common.ARRAY_TYPE],
|
||||
[{}, common.OBJECT_TYPE],
|
||||
[{ test: 1 }, common.OBJECT_TYPE],
|
||||
[/[^A-Za-z0-9_]/g, common.REGEXP_TYPE],
|
||||
[new RegExp("//"), common.REGEXP_TYPE],
|
||||
[new Date(), common.DATE_TYPE]
|
||||
];
|
||||
t.plan(cases.length);
|
||||
for (const c of cases) t.is(common.typeOf(c[0]), c[1]);
|
||||
});
|
||||
|
||||
ava("generateDeviceId", t => {
|
||||
ava("generateDeviceId", (t) => {
|
||||
const space = [" ", "%20"];
|
||||
const special = [";", "%3B"];
|
||||
const cases = [
|
||||
@@ -37,30 +9,31 @@ ava("generateDeviceId", t => {
|
||||
{
|
||||
ProductClass: "TestProductClass",
|
||||
OUI: "TestOUI",
|
||||
SerialNumber: "TestSerialNumber"
|
||||
SerialNumber: "TestSerialNumber",
|
||||
},
|
||||
"TestOUI-TestProductClass-TestSerialNumber"
|
||||
"TestOUI-TestProductClass-TestSerialNumber",
|
||||
],
|
||||
[
|
||||
{
|
||||
OUI: "TestOUI",
|
||||
SerialNumber: "TestSerialNumber"
|
||||
SerialNumber: "TestSerialNumber",
|
||||
},
|
||||
"TestOUI-TestSerialNumber"
|
||||
"TestOUI-TestSerialNumber",
|
||||
],
|
||||
[
|
||||
{
|
||||
OUI: `TestOUIWith${space[0]}_${special[0]}2912`,
|
||||
SerialNumber: `TestSerialNumberWith${space[0]}_${special[0]}2912`
|
||||
SerialNumber: `TestSerialNumberWith${space[0]}_${special[0]}2912`,
|
||||
},
|
||||
`TestOUIWith${space[1]}_${special[1]}2912-TestSerialNumberWith${space[1]}_${special[1]}2912`
|
||||
]
|
||||
`TestOUIWith${space[1]}_${special[1]}2912-TestSerialNumberWith${space[1]}_${special[1]}2912`,
|
||||
],
|
||||
];
|
||||
t.plan(cases.length);
|
||||
for (const c of cases) t.is(common.generateDeviceId(c[0]), c[1]);
|
||||
for (const c of cases)
|
||||
t.is(common.generateDeviceId(c[0] as Record<string, string>), c[1]);
|
||||
});
|
||||
|
||||
ava("escapeRegExp", t => {
|
||||
ava("escapeRegExp", (t) => {
|
||||
t.is(
|
||||
common.escapeRegExp("\\ ^ $ * + ? . ( ) | { } [ ]"),
|
||||
"\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]"
|
||||
|
||||
+19
-19
@@ -5,7 +5,7 @@ import PathSet from "../lib/common/path-set";
|
||||
import VersionedMap from "../lib/versioned-map";
|
||||
import { Attributes } from "../lib/types";
|
||||
|
||||
ava("getAliasDeclarations", t => {
|
||||
ava("getAliasDeclarations", (t) => {
|
||||
const path = Path.parse("a.[aa:10,bb.[aaa:100].cc:1].b");
|
||||
const decs = device.getAliasDeclarations(path, 99);
|
||||
|
||||
@@ -22,64 +22,64 @@ ava("getAliasDeclarations", t => {
|
||||
}
|
||||
});
|
||||
|
||||
ava("unpack", t => {
|
||||
ava("unpack", (t) => {
|
||||
const now = Date.now();
|
||||
const deviceData = {
|
||||
paths: new PathSet(),
|
||||
timestamps: new VersionedMap<Path, number>(),
|
||||
attributes: new VersionedMap<Path, Attributes>(),
|
||||
trackers: new Map<Path, { [name: string]: number }>(),
|
||||
changes: new Set<string>()
|
||||
changes: new Set<string>(),
|
||||
};
|
||||
|
||||
device.set(deviceData, Path.parse("a.1.b"), now, {
|
||||
value: [now, ["b", "xsd:string"]]
|
||||
value: [now, ["b", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.c"), now, {
|
||||
value: [now, ["c", "xsd:string"]]
|
||||
value: [now, ["c", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.1.a"), now, {
|
||||
value: [now, ["", "xsd:string"]]
|
||||
value: [now, ["", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.1.b"), now, {
|
||||
value: [now, ["b1", "xsd:string"]]
|
||||
value: [now, ["b1", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.1.c"), now, {
|
||||
value: [now, ["c1", "xsd:string"]]
|
||||
value: [now, ["c1", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.2.a"), now, {
|
||||
value: [now, ["", "xsd:string"]]
|
||||
value: [now, ["", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.2.b"), now, {
|
||||
value: [now, ["b2", "xsd:string"]]
|
||||
value: [now, ["b2", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.1.a.2.c"), now, {
|
||||
value: [now, ["c2", "xsd:string"]]
|
||||
value: [now, ["c2", "xsd:string"]],
|
||||
});
|
||||
|
||||
device.set(deviceData, Path.parse("a.2.b"), now, {
|
||||
value: [now, ["b", "xsd:string"]]
|
||||
value: [now, ["b", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.c"), now, {
|
||||
value: [now, ["c", "xsd:string"]]
|
||||
value: [now, ["c", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.1.a"), now, {
|
||||
value: [now, ["", "xsd:string"]]
|
||||
value: [now, ["", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.1.b"), now, {
|
||||
value: [now, ["b1", "xsd:string"]]
|
||||
value: [now, ["b1", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.1.c"), now, {
|
||||
value: [now, ["c1", "xsd:string"]]
|
||||
value: [now, ["c1", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.2.a"), now, {
|
||||
value: [now, ["", "xsd:string"]]
|
||||
value: [now, ["", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.2.b"), now, {
|
||||
value: [now, ["c1", "xsd:string"]]
|
||||
value: [now, ["c1", "xsd:string"]],
|
||||
});
|
||||
device.set(deviceData, Path.parse("a.2.a.2.c"), now, {
|
||||
value: [now, ["b1", "xsd:string"]]
|
||||
value: [now, ["b1", "xsd:string"]],
|
||||
});
|
||||
|
||||
let unpacked: Path[];
|
||||
|
||||
@@ -2,7 +2,7 @@ import ava from "ava";
|
||||
import * as mongodbFunctions from "../lib/mongodb-functions";
|
||||
import { stringify } from "../lib/common/expression-parser";
|
||||
|
||||
ava("mongoQueryToFilter", t => {
|
||||
ava("mongoQueryToFilter", (t) => {
|
||||
const tests = [
|
||||
[{}, "true"],
|
||||
[{ test: "test" }, 'test = "test"'],
|
||||
@@ -22,21 +22,21 @@ ava("mongoQueryToFilter", t => {
|
||||
[{ _tags: { $eq: "test" } }, "Tags.test IS NOT NULL"],
|
||||
[
|
||||
{ $and: [{ test: "test" }, { test: { $ne: "test" } }] },
|
||||
'test = "test" AND test <> "test"'
|
||||
'test = "test" AND test <> "test"',
|
||||
],
|
||||
[{ test: "test", test2: "test2" }, 'test = "test" AND test2 = "test2"'],
|
||||
[
|
||||
{ $or: [{ test: "test" }, { test: { $ne: "test" } }] },
|
||||
'test = "test" OR test <> "test"'
|
||||
'test = "test" OR test <> "test"',
|
||||
],
|
||||
[
|
||||
{ test: { $gte: "test1", $ne: "test2" } },
|
||||
'test >= "test1" AND test <> "test2"'
|
||||
'test >= "test1" AND test <> "test2"',
|
||||
],
|
||||
[
|
||||
{ test: "test", test2: { $ne: "test2" } },
|
||||
'test = "test" AND test2 <> "test2"'
|
||||
]
|
||||
'test = "test" AND test2 <> "test2"',
|
||||
],
|
||||
];
|
||||
|
||||
const shouldFailTests = [
|
||||
@@ -45,7 +45,7 @@ ava("mongoQueryToFilter", t => {
|
||||
[{ "Tags.test": { $gt: true } }, "Invalid tag query"],
|
||||
[{ _tags: [] }, "Invalid type"],
|
||||
[{ _tags: { $gt: "test" } }, "Invalid tag query"],
|
||||
[{ $nor: [] }, "Operator $nor not supported"]
|
||||
[{ $nor: [] }, "Operator $nor not supported"],
|
||||
];
|
||||
|
||||
t.plan(tests.length + 2 * shouldFailTests.length);
|
||||
@@ -54,7 +54,7 @@ ava("mongoQueryToFilter", t => {
|
||||
|
||||
for (const test of shouldFailTests) {
|
||||
const func = (): void => {
|
||||
mongodbFunctions.mongoQueryToFilter(test[0]);
|
||||
mongodbFunctions.mongoQueryToFilter(test[0] as Record<string, unknown>);
|
||||
};
|
||||
const error = t.throws(func, Error);
|
||||
t.is(error.message, test[1]);
|
||||
|
||||
+10
-10
@@ -2,7 +2,7 @@ import ava from "ava";
|
||||
import Path from "../lib/common/path";
|
||||
import PathSet from "../lib/common/path-set";
|
||||
|
||||
ava("depth", t => {
|
||||
ava("depth", (t) => {
|
||||
const pathSet = new PathSet();
|
||||
t.is(pathSet.depth, 0);
|
||||
pathSet.add(Path.parse(""));
|
||||
@@ -11,14 +11,14 @@ ava("depth", t => {
|
||||
t.is(pathSet.depth, 2);
|
||||
});
|
||||
|
||||
ava("add", t => {
|
||||
ava("add", (t) => {
|
||||
const pathSet = new PathSet();
|
||||
pathSet.add(Path.parse("a"));
|
||||
pathSet.add(Path.parse("a"));
|
||||
t.is(pathSet.find(Path.parse("a"), true, true, 99).length, 1);
|
||||
});
|
||||
|
||||
ava("get", t => {
|
||||
ava("get", (t) => {
|
||||
const pathSet = new PathSet();
|
||||
pathSet.add(Path.parse("a.*"));
|
||||
pathSet.add(Path.parse("a.a"));
|
||||
@@ -28,7 +28,7 @@ ava("get", t => {
|
||||
t.is(pathSet.get(Path.parse("*.a")), null);
|
||||
});
|
||||
|
||||
ava("find", t => {
|
||||
ava("find", (t) => {
|
||||
const pathSet = new PathSet();
|
||||
pathSet.add(Path.parse("a"));
|
||||
pathSet.add(Path.parse("a.*"));
|
||||
@@ -37,32 +37,32 @@ ava("find", t => {
|
||||
pathSet.add(Path.parse("*.*"));
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse(""), true, true, 1).map(p => p.toString()),
|
||||
pathSet.find(Path.parse(""), true, true, 1).map((p) => p.toString()),
|
||||
["a"]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse(""), false, false, 2).map(p => p.toString()),
|
||||
pathSet.find(Path.parse(""), false, false, 2).map((p) => p.toString()),
|
||||
["a", "a.*", "a.a", "*.a", "*.*"]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse("a.*"), false, false).map(p => p.toString()),
|
||||
pathSet.find(Path.parse("a.*"), false, false).map((p) => p.toString()),
|
||||
["a.*"]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse("a.*"), false, true).map(p => p.toString()),
|
||||
pathSet.find(Path.parse("a.*"), false, true).map((p) => p.toString()),
|
||||
["a.*", "a.a"]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse("a.*"), true, false).map(p => p.toString()),
|
||||
pathSet.find(Path.parse("a.*"), true, false).map((p) => p.toString()),
|
||||
["a.*", "*.*"]
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
pathSet.find(Path.parse("a.*"), true, true).map(p => p.toString()),
|
||||
pathSet.find(Path.parse("a.*"), true, true).map((p) => p.toString()),
|
||||
["a.*", "a.a", "*.a", "*.*"]
|
||||
);
|
||||
});
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
import ava from "ava";
|
||||
import Path from "../lib/common/path";
|
||||
|
||||
ava("toString", t => {
|
||||
ava("toString", (t) => {
|
||||
const path1 = Path.parse('abc.[ abc : 123 , 123 : abc , 123: " abc "].123');
|
||||
const path2 = Path.parse('abc.[123: " abc ",abc:123,123:abc].123');
|
||||
t.is(path1.toString(), path2.toString());
|
||||
});
|
||||
|
||||
ava("slice", t => {
|
||||
ava("slice", (t) => {
|
||||
const path = Path.parse("a.*.b.[x:y].c");
|
||||
const sliced = path.slice(1, -1);
|
||||
t.is(sliced.toString(), '*.b.[x:"y"]');
|
||||
@@ -15,7 +15,7 @@ ava("slice", t => {
|
||||
t.is(sliced.wildcard, 0b1);
|
||||
});
|
||||
|
||||
ava("concat", t => {
|
||||
ava("concat", (t) => {
|
||||
const path1 = Path.parse("a");
|
||||
const path2 = Path.parse("*.[a:b]");
|
||||
const concat = path1.concat(path2);
|
||||
|
||||
+13
-13
@@ -2,10 +2,10 @@ import ava from "ava";
|
||||
import {
|
||||
parseXmlDeclaration,
|
||||
decodeEntities,
|
||||
parseXml
|
||||
parseXml,
|
||||
} from "../lib/xml-parser";
|
||||
|
||||
ava("parseXmlDeclaration", t => {
|
||||
ava("parseXmlDeclaration", (t) => {
|
||||
const buf = Buffer.from(
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n<soap-env:Envelope />'
|
||||
);
|
||||
@@ -15,25 +15,25 @@ ava("parseXmlDeclaration", t => {
|
||||
name: "version",
|
||||
namespace: "",
|
||||
localName: "version",
|
||||
value: "1.0"
|
||||
value: "1.0",
|
||||
},
|
||||
{
|
||||
name: "encoding",
|
||||
namespace: "",
|
||||
localName: "encoding",
|
||||
value: "UTF-8"
|
||||
}
|
||||
value: "UTF-8",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
ava("decodeEntities", t => {
|
||||
ava("decodeEntities", (t) => {
|
||||
t.is(
|
||||
decodeEntities("&&<>"'>§��;"),
|
||||
"&&<>\"'>§𠮷;"
|
||||
);
|
||||
});
|
||||
|
||||
ava("parse", t => {
|
||||
ava("parse", (t) => {
|
||||
const xml =
|
||||
'<?xml version="1.0"?>\n<a-b:c><d f="1<g>"/><!-- comment --><h >i</h></a-b:c>';
|
||||
const parsed = parseXml(xml);
|
||||
@@ -60,7 +60,7 @@ ava("parse", t => {
|
||||
attrs: 'f="1<g>"',
|
||||
text: "",
|
||||
bodyIndex: 42,
|
||||
children: []
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
name: "h",
|
||||
@@ -69,10 +69,10 @@ ava("parse", t => {
|
||||
attrs: "",
|
||||
text: "i",
|
||||
bodyIndex: 62,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env -S node -r esm -r ts-node/register/transpile-only
|
||||
|
||||
/**
|
||||
* Copyright 2013-2019 GenieACS Inc.
|
||||
*
|
||||
@@ -60,7 +58,7 @@ connect()
|
||||
.then(() => {
|
||||
devicesCollection.findOne(
|
||||
{
|
||||
_id: process.argv[2]
|
||||
_id: process.argv[2],
|
||||
},
|
||||
{},
|
||||
(err, device) => {
|
||||
@@ -79,6 +77,6 @@ connect()
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
+2
-2
@@ -14,11 +14,11 @@
|
||||
"allowJs": true
|
||||
},
|
||||
"include": [
|
||||
"./bin/.*.ts",
|
||||
"./bin/*.ts",
|
||||
"./lib/**/*",
|
||||
"./ui/**/*",
|
||||
"./test/**/*",
|
||||
"./build/**/*",
|
||||
"./tools/.*.ts"
|
||||
"./tools/*.ts"
|
||||
]
|
||||
}
|
||||
+2
-2
@@ -22,7 +22,7 @@ import { ClosureComponent, Component } from "mithril";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const active = { [vnode.attrs["page"]]: "active" };
|
||||
const tabs = [];
|
||||
|
||||
@@ -97,7 +97,7 @@ const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
|
||||
return m("nav#side-menu", m("ul", tabs));
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ const adminPages = [
|
||||
"files",
|
||||
"config",
|
||||
"users",
|
||||
"permissions"
|
||||
"permissions",
|
||||
];
|
||||
|
||||
let state;
|
||||
@@ -71,7 +71,7 @@ let fulfillTimeout = null;
|
||||
function fulfill(): void {
|
||||
clearTimeout(fulfillTimeout);
|
||||
fulfillTimeout = setTimeout(() => {
|
||||
store.fulfill(lastRenderTimestamp, pageVisitTimestamp).then(updated => {
|
||||
store.fulfill(lastRenderTimestamp, pageVisitTimestamp).then((updated) => {
|
||||
if (updated) m.redraw();
|
||||
});
|
||||
}, 100);
|
||||
@@ -89,7 +89,7 @@ function pagify(pageName, page): RouteResolver {
|
||||
attrs["page"] = pageName;
|
||||
return m(layout, attrs, p);
|
||||
},
|
||||
onmatch: null
|
||||
onmatch: null,
|
||||
};
|
||||
|
||||
component.onmatch = (args, requestedPath) => {
|
||||
@@ -100,16 +100,16 @@ function pagify(pageName, page): RouteResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
page
|
||||
.init(args)
|
||||
.then(st => {
|
||||
.then((st) => {
|
||||
if (!st) return void m.route.set("/");
|
||||
state = st;
|
||||
resolve();
|
||||
fulfill();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
if (!window.username && err.message.indexOf("authorized") >= 0) {
|
||||
notifications.push("error", err.message);
|
||||
m.route.set("/login", { continue: requestedPath });
|
||||
@@ -133,7 +133,7 @@ function redirectAdminPage(): RouteResolver {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
return component;
|
||||
}
|
||||
@@ -155,5 +155,5 @@ m.route(document.body, "/overview", {
|
||||
"/admin/files": pagify("files", filesPage),
|
||||
"/admin/config": pagify("config", configPage),
|
||||
"/admin/users": pagify("users", usersPage),
|
||||
"/admin/permissions": pagify("permissions", permissionsPage)
|
||||
"/admin/permissions": pagify("permissions", permissionsPage),
|
||||
});
|
||||
@@ -17,11 +17,13 @@
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export default class Autocomplete {
|
||||
private callback: (
|
||||
type AutocompleteCallback = (
|
||||
value: string,
|
||||
callback: (suggestions: { value: string; tip?: string }[]) => void
|
||||
) => void;
|
||||
|
||||
export default class Autocomplete {
|
||||
private callback: AutocompleteCallback;
|
||||
private element: HTMLInputElement;
|
||||
private hideTimeout: NodeJS.Timeout;
|
||||
private visible: boolean;
|
||||
@@ -29,7 +31,7 @@ export default class Autocomplete {
|
||||
private selection: number;
|
||||
private container: HTMLElement;
|
||||
|
||||
public constructor(className, callback) {
|
||||
public constructor(className: string, callback: AutocompleteCallback) {
|
||||
this.callback = callback;
|
||||
this.element = null;
|
||||
this.hideTimeout = null;
|
||||
@@ -44,7 +46,7 @@ export default class Autocomplete {
|
||||
this.container.className = className;
|
||||
}
|
||||
|
||||
public attach(el): void {
|
||||
public attach(el: HTMLInputElement): void {
|
||||
el.setAttribute("autocomplete", "off");
|
||||
|
||||
el.addEventListener("focus", () => {
|
||||
@@ -62,7 +64,7 @@ export default class Autocomplete {
|
||||
this.hide();
|
||||
});
|
||||
|
||||
el.addEventListener("keydown", e => {
|
||||
el.addEventListener("keydown", (e) => {
|
||||
if (this.element !== el) return;
|
||||
if (e.key === "Escape") {
|
||||
if (this.visible) this.hide();
|
||||
@@ -108,7 +110,7 @@ export default class Autocomplete {
|
||||
private update(): void {
|
||||
const el = this.element;
|
||||
|
||||
this.callback(el.value, suggestions => {
|
||||
this.callback(el.value, (suggestions) => {
|
||||
if (this.element !== el) return;
|
||||
this.default = null;
|
||||
|
||||
@@ -154,7 +156,7 @@ export default class Autocomplete {
|
||||
|
||||
const t = document.createTextNode(suggestion.value);
|
||||
e.appendChild(t);
|
||||
e.addEventListener("mousedown", ev => {
|
||||
e.addEventListener("mousedown", (ev) => {
|
||||
ev.preventDefault();
|
||||
el.value = suggestion.value;
|
||||
if (this.element === el) this.update();
|
||||
|
||||
@@ -24,7 +24,7 @@ import { m } from "./components";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const onPasswordChange = vnode.attrs["onPasswordChange"];
|
||||
const enforceAuth = !vnode.attrs["noAuth"];
|
||||
const username = vnode.attrs["username"];
|
||||
@@ -40,19 +40,19 @@ const component: ClosureComponent = (): Component => {
|
||||
type: "text",
|
||||
value: vnode.state["username"],
|
||||
disabled: !!username,
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state["username"] = e.target.value;
|
||||
},
|
||||
oncreate: _vnode => {
|
||||
oncreate: (_vnode) => {
|
||||
(_vnode.dom as HTMLSelectElement).focus();
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
let fields = {
|
||||
newPassword: "New password",
|
||||
confirmPassword: "Confirm password"
|
||||
confirmPassword: "Confirm password",
|
||||
};
|
||||
if (enforceAuth)
|
||||
fields = Object.assign({ authPassword: "Your password" }, fields);
|
||||
@@ -66,9 +66,9 @@ const component: ClosureComponent = (): Component => {
|
||||
name: f,
|
||||
type: "password",
|
||||
value: vnode.state[f],
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state[f] = e.target.value;
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -77,7 +77,7 @@ const component: ClosureComponent = (): Component => {
|
||||
const submit = m(
|
||||
"button.primary",
|
||||
{
|
||||
type: "submit"
|
||||
type: "submit",
|
||||
},
|
||||
"Change password"
|
||||
) as VnodeDOM;
|
||||
@@ -89,7 +89,7 @@ const component: ClosureComponent = (): Component => {
|
||||
m(
|
||||
"form",
|
||||
{
|
||||
onsubmit: e => {
|
||||
onsubmit: (e) => {
|
||||
e.redraw = false;
|
||||
e.preventDefault();
|
||||
if (
|
||||
@@ -120,19 +120,19 @@ const component: ClosureComponent = (): Component => {
|
||||
if (onPasswordChange) onPasswordChange();
|
||||
(submit.dom as HTMLFormElement).disabled = false;
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
(submit.dom as HTMLFormElement).disabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
form
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
return m("div.put-form", children);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ import { codeMirror } from "./dynamic-loader";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
return m("textarea", {
|
||||
name: vnode.attrs["id"],
|
||||
value: vnode.attrs["value"],
|
||||
oncreate: _vnode => {
|
||||
oncreate: (_vnode) => {
|
||||
const editor = codeMirror.fromTextArea(_vnode.dom, {
|
||||
mode: vnode.attrs["mode"],
|
||||
lineNumbers: true,
|
||||
@@ -40,21 +40,21 @@ const component: ClosureComponent = (): Component => {
|
||||
"Cmd-Enter": () => {
|
||||
if (vnode.attrs["onSubmit"])
|
||||
vnode.attrs["onSubmit"](_vnode.dom);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (vnode.attrs["onChange"]) {
|
||||
editor.on("change", e => {
|
||||
editor.on("change", (e) => {
|
||||
vnode.attrs["onChange"](e.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
if (vnode.attrs["focus"]) editor.focus();
|
||||
if (vnode.attrs["onReady"]) vnode.attrs["onReady"](editor);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ import m, {
|
||||
ComponentTypes,
|
||||
Lifecycle,
|
||||
ClosureComponent,
|
||||
Vnode
|
||||
Vnode,
|
||||
} from "mithril";
|
||||
import parameter from "./components/parameter";
|
||||
import parameterList from "./components/parameter-list";
|
||||
@@ -53,7 +53,7 @@ const comps = {
|
||||
tags,
|
||||
ping,
|
||||
"device-link": deviceLink,
|
||||
"long-text": longTextComponent
|
||||
"long-text": longTextComponent,
|
||||
};
|
||||
|
||||
const contextifiedComponents = new WeakMap<ComponentTypes, ComponentTypes>();
|
||||
@@ -96,7 +96,7 @@ const M = new Proxy(m, {
|
||||
get: (target, prop) => {
|
||||
if (prop === "context") return contextFn;
|
||||
else return Reflect.get(target, prop);
|
||||
}
|
||||
},
|
||||
}) as MC;
|
||||
|
||||
function contextFn(context, ...argumentsList): Vnode {
|
||||
@@ -132,7 +132,7 @@ export function contextifyComponent(component: ComponentTypes): ComponentTypes {
|
||||
return res;
|
||||
};
|
||||
} else if (!component.prototype || !component.prototype.view) {
|
||||
c = initialNode => {
|
||||
c = (initialNode) => {
|
||||
const state = (component as ClosureComponent)(initialNode);
|
||||
const view = state.view;
|
||||
state.view = function (vnode) {
|
||||
|
||||
@@ -32,31 +32,33 @@ function escapeRegExp(str): string {
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
|
||||
const search = m("input", {
|
||||
type: "text",
|
||||
placeholder: "Search parameters",
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state["searchString"] = e.target.value;
|
||||
e.redraw = false;
|
||||
clearTimeout(vnode.state["timeout"]);
|
||||
vnode.state["timeout"] = setTimeout(m.redraw, 500);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const instanceRegex = /\.[0-9]+$/;
|
||||
let re;
|
||||
if (vnode.state["searchString"]) {
|
||||
const keywords = vnode.state["searchString"].split(" ").filter(s => s);
|
||||
const keywords = vnode.state["searchString"]
|
||||
.split(" ")
|
||||
.filter((s) => s);
|
||||
if (keywords.length)
|
||||
re = new RegExp(keywords.map(s => escapeRegExp(s)).join(".*"), "i");
|
||||
re = new RegExp(keywords.map((s) => escapeRegExp(s)).join(".*"), "i");
|
||||
}
|
||||
|
||||
const rows = Object.keys(device)
|
||||
.sort()
|
||||
.map(k => {
|
||||
.map((k) => {
|
||||
const p = device[k];
|
||||
const val = [];
|
||||
const attrs = {};
|
||||
@@ -83,9 +85,9 @@ const component: ClosureComponent = (): Component => {
|
||||
taskQueue.queueTask({
|
||||
name: "deleteObject",
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
objectName: k
|
||||
objectName: k,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("delete-instance")
|
||||
)
|
||||
@@ -100,9 +102,9 @@ const component: ClosureComponent = (): Component => {
|
||||
taskQueue.queueTask({
|
||||
name: "addObject",
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
objectName: k
|
||||
objectName: k,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("add-instance")
|
||||
)
|
||||
@@ -119,9 +121,9 @@ const component: ClosureComponent = (): Component => {
|
||||
taskQueue.queueTask({
|
||||
name: "getParameterValues",
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
parameterNames: [k]
|
||||
parameterNames: [k],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("refresh")
|
||||
)
|
||||
@@ -144,14 +146,14 @@ const component: ClosureComponent = (): Component => {
|
||||
device["DeviceID.ID"].value[0]
|
||||
)}.csv`,
|
||||
download: "",
|
||||
style: "float: right;"
|
||||
style: "float: right;",
|
||||
},
|
||||
"Download"
|
||||
),
|
||||
search,
|
||||
m(".parameter-list", m("table", m("tbody", rows)))
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import * as store from "../store";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
if (vnode.attrs["filter"]) {
|
||||
if (
|
||||
!store.evaluateExpression(
|
||||
@@ -34,13 +34,13 @@ const component: ClosureComponent = (): Component => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const children = Object.values(vnode.attrs["components"]).map(c => {
|
||||
const children = Object.values(vnode.attrs["components"]).map((c) => {
|
||||
if (typeof c !== "object") return `${c}`;
|
||||
return m(c["type"], c);
|
||||
});
|
||||
if (vnode.attrs["element"]) return m(vnode.attrs["element"], children);
|
||||
else return children;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import * as store from "../store";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
|
||||
const buttons = [];
|
||||
@@ -38,9 +38,9 @@ const component: ClosureComponent = (): Component => {
|
||||
onclick: () => {
|
||||
taskQueue.queueTask({
|
||||
name: "reboot",
|
||||
device: device["DeviceID.ID"].value[0]
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Reboot"
|
||||
)
|
||||
@@ -54,9 +54,9 @@ const component: ClosureComponent = (): Component => {
|
||||
onclick: () => {
|
||||
taskQueue.queueTask({
|
||||
name: "factoryReset",
|
||||
device: device["DeviceID.ID"].value[0]
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Reset"
|
||||
)
|
||||
@@ -70,9 +70,9 @@ const component: ClosureComponent = (): Component => {
|
||||
onclick: () => {
|
||||
taskQueue.stageDownload({
|
||||
name: "download",
|
||||
device: device["DeviceID.ID"].value[0]
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Push file"
|
||||
)
|
||||
@@ -93,17 +93,17 @@ const component: ClosureComponent = (): Component => {
|
||||
notifications.push("success", `${deviceId}: Device deleted`);
|
||||
m.route.set("/devices");
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
);
|
||||
|
||||
return m(".actions-bar", buttons);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@ import { getIcon } from "../icons";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
const deviceId = device["DeviceID.ID"].value[0];
|
||||
const faults = store.fetch("faults", [
|
||||
"AND",
|
||||
[">", ["PARAM", "_id"], `${deviceId}:`],
|
||||
["<", ["PARAM", "_id"], `${deviceId}:zzzz`]
|
||||
["<", ["PARAM", "_id"], `${deviceId}:zzzz`],
|
||||
]);
|
||||
|
||||
const headers = [
|
||||
@@ -39,8 +39,8 @@ const component: ClosureComponent = (): Component => {
|
||||
"Code",
|
||||
"Message",
|
||||
"Retries",
|
||||
"Timestamp"
|
||||
].map(l => m("th", l));
|
||||
"Timestamp",
|
||||
].map((l) => m("th", l));
|
||||
const thead = m("thead", m("tr", headers));
|
||||
|
||||
const rows = [];
|
||||
@@ -57,7 +57,7 @@ const component: ClosureComponent = (): Component => {
|
||||
"button",
|
||||
{
|
||||
title: "Delete fault",
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.redraw = false;
|
||||
store
|
||||
.deleteResource("faults", f["_id"])
|
||||
@@ -66,21 +66,24 @@ const component: ClosureComponent = (): Component => {
|
||||
store.fulfill(Date.now(), Date.now());
|
||||
m.redraw();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(Date.now(), Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("remove")
|
||||
)
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
let tbody;
|
||||
if (rows.length) {
|
||||
tbody = m("tbody", rows.map(r => m("tr", r)));
|
||||
tbody = m(
|
||||
"tbody",
|
||||
rows.map((r) => m("tr", r))
|
||||
);
|
||||
} else {
|
||||
tbody = m(
|
||||
"tbody",
|
||||
@@ -89,7 +92,7 @@ const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
|
||||
return m("table.table", thead, tbody);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ import { m } from "../components";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
let deviceId;
|
||||
if (vnode.attrs["device"])
|
||||
deviceId = vnode.attrs["device"]["DeviceID.ID"].value[0];
|
||||
|
||||
const children = Object.values(vnode.attrs["components"]).map(c => {
|
||||
const children = Object.values(vnode.attrs["components"]).map((c) => {
|
||||
if (typeof c !== "object") return `${c}`;
|
||||
const attrs = Object.assign({}, vnode.attrs, c);
|
||||
return m(attrs["type"], attrs);
|
||||
@@ -41,7 +41,7 @@ const component: ClosureComponent = (): Component => {
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ const CHARTS = config.ui.overview.charts;
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
const chart = CHARTS[vnode.attrs["chart"]];
|
||||
const chart = CHARTS[vnode.attrs["chart"]] as Record<string, unknown>;
|
||||
if (!chart) return null;
|
||||
for (const slice of Object.values(chart.slices)) {
|
||||
const filter = slice["filter"];
|
||||
@@ -39,20 +39,20 @@ const component: ClosureComponent = (): Component => {
|
||||
width: "1em",
|
||||
height: "1em",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
"xmlns:xlink": "http://www.w3.org/1999/xlink"
|
||||
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||||
},
|
||||
m("circle", {
|
||||
cx: "0.5em",
|
||||
cy: "0.5em",
|
||||
r: "0.4em",
|
||||
fill: slice["color"]
|
||||
fill: slice["color"],
|
||||
})
|
||||
);
|
||||
return m("span.overview-dot", dot, `${slice["label"]}`);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ import { m } from "../components";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
|
||||
const rows = Object.values(vnode.attrs["parameters"]).map(parameter => {
|
||||
const rows = Object.values(vnode.attrs["parameters"]).map((parameter) => {
|
||||
const p = m.context(
|
||||
{
|
||||
device: device,
|
||||
parameter: parameter["parameter"]
|
||||
parameter: parameter["parameter"],
|
||||
},
|
||||
parameter["type"] || "parameter",
|
||||
parameter
|
||||
@@ -38,11 +38,11 @@ const component: ClosureComponent = (): Component => {
|
||||
return m(
|
||||
"tr",
|
||||
{
|
||||
onupdate: vn => {
|
||||
onupdate: (vn) => {
|
||||
(vn.dom as HTMLElement).style.display = (p as VnodeDOM).dom
|
||||
? ""
|
||||
: "none";
|
||||
}
|
||||
},
|
||||
},
|
||||
m("th", parameter["label"]),
|
||||
m("td", p)
|
||||
@@ -50,7 +50,7 @@ const component: ClosureComponent = (): Component => {
|
||||
});
|
||||
|
||||
return m("table.parameter-list", rows);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -26,21 +26,21 @@ import { getIcon } from "../icons";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
oninit: vnode => {
|
||||
oninit: (vnode) => {
|
||||
const obj = vnode.attrs["parameter"];
|
||||
if (!Array.isArray(obj) || obj[0] !== "PARAM")
|
||||
throw new Error("Object must be a parameter path");
|
||||
vnode.state["object"] = obj[1];
|
||||
vnode.state["parameters"] = Object.values(vnode.attrs["childParameters"]);
|
||||
},
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
const object = store.evaluateExpression(vnode.state["object"], device);
|
||||
const parameters = vnode.state["parameters"];
|
||||
|
||||
if (!device[object]) return null;
|
||||
if (typeof object !== "string" || !device[object]) return null;
|
||||
|
||||
const instances = new Set();
|
||||
const instances: Set<string> = new Set();
|
||||
const prefix = `${object}.`;
|
||||
for (const p in device) {
|
||||
if (p.startsWith(prefix)) {
|
||||
@@ -50,7 +50,7 @@ const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
}
|
||||
|
||||
const headers = Object.values(parameters).map(p => m("th", p["label"]));
|
||||
const headers = Object.values(parameters).map((p) => m("th", p["label"]));
|
||||
|
||||
const thead = m("thead", m("tr", headers));
|
||||
|
||||
@@ -59,7 +59,7 @@ const component: ClosureComponent = (): Component => {
|
||||
let filter =
|
||||
vnode.attrs["filter"] != null ? vnode.attrs["filter"] : true;
|
||||
|
||||
filter = expressionParser.map(filter, e => {
|
||||
filter = expressionParser.map(filter, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "PARAM")
|
||||
return ["PARAM", ["||", i, ".", e[1]]];
|
||||
return e;
|
||||
@@ -67,8 +67,8 @@ const component: ClosureComponent = (): Component => {
|
||||
|
||||
if (!store.evaluateExpression(filter, device)) continue;
|
||||
|
||||
const row = parameters.map(p => {
|
||||
const param = expressionParser.map(p.parameter, e => {
|
||||
const row = parameters.map((p) => {
|
||||
const param = expressionParser.map(p.parameter, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "PARAM")
|
||||
return ["PARAM", ["||", i, ".", e[1]]];
|
||||
return e;
|
||||
@@ -78,13 +78,13 @@ const component: ClosureComponent = (): Component => {
|
||||
m.context(
|
||||
{
|
||||
device: device,
|
||||
parameter: param
|
||||
parameter: param,
|
||||
},
|
||||
p.type || "parameter",
|
||||
Object.assign({}, p, {
|
||||
device: device,
|
||||
parameter: param,
|
||||
label: null
|
||||
label: null,
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -102,9 +102,9 @@ const component: ClosureComponent = (): Component => {
|
||||
taskQueue.queueTask({
|
||||
name: "deleteObject",
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
objectName: i
|
||||
objectName: i,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("delete-instance")
|
||||
)
|
||||
@@ -135,9 +135,9 @@ const component: ClosureComponent = (): Component => {
|
||||
taskQueue.queueTask({
|
||||
name: "addObject",
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
objectName: object
|
||||
objectName: object,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("add-instance")
|
||||
)
|
||||
@@ -150,7 +150,7 @@ const component: ClosureComponent = (): Component => {
|
||||
if (vnode.attrs["label"]) label = m("h2", vnode.attrs["label"]);
|
||||
|
||||
return [label, m("table.table", thead, m("tbody", rows))];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import { getIcon } from "../icons";
|
||||
const evaluateParam = memoize((exp, obj, now: number) => {
|
||||
let timestamp = now;
|
||||
const params = new Set();
|
||||
const value = expression.evaluate(exp, obj, now, e => {
|
||||
const value = expression.evaluate(exp, obj, now, (e) => {
|
||||
if (Array.isArray(e)) {
|
||||
if (e[0] === "PARAM") {
|
||||
params.add(e[1]);
|
||||
@@ -52,7 +52,7 @@ const evaluateParam = memoize((exp, obj, now: number) => {
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
|
||||
const { value, timestamp, parameter } = evaluateParam(
|
||||
@@ -77,11 +77,11 @@ const component: ClosureComponent = (): Component => {
|
||||
[
|
||||
parameter,
|
||||
device[parameter].value[0],
|
||||
device[parameter].value[1]
|
||||
]
|
||||
]
|
||||
device[parameter].value[1],
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("edit")
|
||||
);
|
||||
@@ -93,7 +93,7 @@ const component: ClosureComponent = (): Component => {
|
||||
"span",
|
||||
{
|
||||
class: "parameter-value",
|
||||
onmouseover: e => {
|
||||
onmouseover: (e) => {
|
||||
e.redraw = false;
|
||||
// Don't update any child element
|
||||
if (e.target === (el as VnodeDOM).dom) {
|
||||
@@ -101,12 +101,12 @@ const component: ClosureComponent = (): Component => {
|
||||
const localeString = new Date(timestamp).toLocaleString();
|
||||
e.target.title = `${localeString} (${timeAgo(now - timestamp)})`;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
el,
|
||||
edit
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const component: ClosureComponent = (vn): Component => {
|
||||
onremove: () => {
|
||||
clearInterval(interval);
|
||||
},
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
let param =
|
||||
device["InternetGatewayDevice.ManagementServer.ConnectionRequestURL"];
|
||||
@@ -73,7 +73,7 @@ const component: ClosureComponent = (vn): Component => {
|
||||
}
|
||||
|
||||
return m("div", `Pinging ${host}:`);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,26 +25,26 @@ import * as notifications from "../notifications";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
|
||||
return m(
|
||||
"button.primary",
|
||||
{
|
||||
title: "Initiate session and refresh basic parameters",
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.target.disabled = true;
|
||||
const params = Object.values(vnode.attrs["parameters"])
|
||||
.map(exp => {
|
||||
.map((exp) => {
|
||||
if (!Array.isArray(exp) || exp[0] !== "PARAM") return null;
|
||||
return store.evaluateExpression(exp[1], device);
|
||||
return store.evaluateExpression(exp[1], device) as string;
|
||||
})
|
||||
.filter(exp => !!exp);
|
||||
.filter((exp) => !!exp);
|
||||
|
||||
const task = {
|
||||
name: "getParameterValues",
|
||||
parameterNames: params,
|
||||
device: device["DeviceID.ID"].value[0]
|
||||
device: device["DeviceID.ID"].value[0],
|
||||
};
|
||||
|
||||
taskQueue
|
||||
@@ -80,11 +80,11 @@ const component: ClosureComponent = (): Component => {
|
||||
e.target.disabled = false;
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Summon"
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+15
-10
@@ -25,7 +25,7 @@ import { getIcon } from "../icons";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const device = vnode.attrs["device"];
|
||||
let writable = true;
|
||||
if (vnode.attrs["writable"] != null) writable = vnode.attrs["writable"];
|
||||
@@ -36,18 +36,23 @@ const component: ClosureComponent = (): Component => {
|
||||
|
||||
tags.sort();
|
||||
|
||||
if (!writable) return m(".tags", tags.map(t => m("span.tag", t)));
|
||||
if (!writable) {
|
||||
return m(
|
||||
".tags",
|
||||
tags.map((t) => m("span.tag", t))
|
||||
);
|
||||
}
|
||||
|
||||
return m(
|
||||
".tags",
|
||||
tags.map(tag =>
|
||||
tags.map((tag) =>
|
||||
m(
|
||||
"span.tag",
|
||||
tag,
|
||||
m(
|
||||
"button",
|
||||
{
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.target.disabled = true;
|
||||
const deviceId = device["DeviceID.ID"].value[0];
|
||||
store
|
||||
@@ -60,14 +65,14 @@ const component: ClosureComponent = (): Component => {
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
e.target.disabled = false;
|
||||
notifications.push(
|
||||
"error",
|
||||
`${deviceId}: ${err.message}`
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("remove")
|
||||
)
|
||||
@@ -79,7 +84,7 @@ const component: ClosureComponent = (): Component => {
|
||||
m(
|
||||
"button",
|
||||
{
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.target.disabled = true;
|
||||
const deviceId = device["DeviceID.ID"].value[0];
|
||||
const tag = prompt(`Enter tag to assign to device:`);
|
||||
@@ -95,17 +100,17 @@ const component: ClosureComponent = (): Component => {
|
||||
notifications.push("success", `${deviceId}: Tags updated`);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
e.target.disabled = false;
|
||||
notifications.push("error", `${deviceId}: ${err.message}`);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("add")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ interface Diff {
|
||||
remove: string[];
|
||||
}
|
||||
|
||||
export function flattenConfig(config): any {
|
||||
export function flattenConfig(config: Record<string, unknown>): any {
|
||||
const flatten = {};
|
||||
const recuresive = (obj: any, root: string): void => {
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
@@ -96,10 +96,13 @@ export function structureConfig(config: Config[]): any {
|
||||
return toArray(_config);
|
||||
}
|
||||
|
||||
export function diffConfig(current, target): Diff {
|
||||
export function diffConfig(
|
||||
current: Record<string, unknown>,
|
||||
target: Record<string, unknown>
|
||||
): Diff {
|
||||
const diff = {
|
||||
add: [],
|
||||
remove: []
|
||||
remove: [],
|
||||
};
|
||||
|
||||
for (const [k, v] of Object.entries(target))
|
||||
|
||||
+30
-30
@@ -30,7 +30,7 @@ import { getIcon } from "./icons";
|
||||
|
||||
const attributes = [
|
||||
{ id: "_id", label: "Key" },
|
||||
{ id: "value", label: "Value", type: "textarea" }
|
||||
{ id: "value", label: "Value", type: "textarea" },
|
||||
];
|
||||
|
||||
interface ValidationErrors {
|
||||
@@ -52,13 +52,13 @@ function putActionHandler(action, _object, isNew?): Promise<ValidationErrors> {
|
||||
object.value = stringify(parse(object.value || ""));
|
||||
} catch (err) {
|
||||
return void resolve({
|
||||
value: "Config value must be valid expression"
|
||||
value: "Config value must be valid expression",
|
||||
});
|
||||
}
|
||||
|
||||
store
|
||||
.resourceExists("config", id)
|
||||
.then(exists => {
|
||||
.then((exists) => {
|
||||
if (exists && isNew) {
|
||||
store.fulfill(0, Date.now());
|
||||
return void resolve({ _id: "Config already exists" });
|
||||
@@ -90,7 +90,7 @@ function putActionHandler(action, _object, isNew?): Promise<ValidationErrors> {
|
||||
store.fulfill(0, Date.now());
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
store.fulfill(0, Date.now());
|
||||
reject(err);
|
||||
});
|
||||
@@ -102,14 +102,14 @@ function putActionHandler(action, _object, isNew?): Promise<ValidationErrors> {
|
||||
|
||||
const formData = {
|
||||
resource: "config",
|
||||
attributes: attributes
|
||||
attributes: attributes,
|
||||
};
|
||||
|
||||
function escapeRegExp(str): string {
|
||||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
|
||||
}
|
||||
|
||||
export function init(): Promise<{}> {
|
||||
export function init(): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("config", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -131,9 +131,9 @@ function renderTable(confsResponse, searchString): Children {
|
||||
|
||||
let regex;
|
||||
if (searchString) {
|
||||
const keywords = searchString.split(" ").filter(s => s);
|
||||
const keywords = searchString.split(" ").filter((s) => s);
|
||||
if (keywords.length)
|
||||
regex = new RegExp(keywords.map(s => escapeRegExp(s)).join(".*"), "i");
|
||||
regex = new RegExp(keywords.map((s) => escapeRegExp(s)).join(".*"), "i");
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
@@ -154,9 +154,9 @@ function renderTable(confsResponse, searchString): Children {
|
||||
{
|
||||
base: conf,
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, false)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const ErrorList = errors ? Object.values(errors) : [];
|
||||
if (ErrorList.length) {
|
||||
for (const err of ErrorList)
|
||||
@@ -166,19 +166,19 @@ function renderTable(confsResponse, searchString): Children {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("edit")
|
||||
);
|
||||
@@ -190,10 +190,10 @@ function renderTable(confsResponse, searchString): Children {
|
||||
onclick: () => {
|
||||
if (!confirm(`Deleting ${conf._id} config. Are you sure?`)) return;
|
||||
|
||||
putActionHandler("delete", conf).catch(err => {
|
||||
putActionHandler("delete", conf).catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("remove")
|
||||
);
|
||||
@@ -219,18 +219,18 @@ function renderTable(confsResponse, searchString): Children {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Config - GenieACS";
|
||||
|
||||
const search = m("input", {
|
||||
type: "text",
|
||||
placeholder: "Search config",
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state["searchString"] = e.target.value;
|
||||
e.redraw = false;
|
||||
clearTimeout(vnode.state["timeout"]);
|
||||
vnode.state["timeout"] = setTimeout(m.redraw, 250);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const confs = store.fetch("config", true);
|
||||
@@ -250,9 +250,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
Object.assign(
|
||||
{
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, true)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -264,19 +264,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"New config"
|
||||
);
|
||||
@@ -286,7 +286,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{ name: "charts", prefix: "ui.overview.charts.", data: [] },
|
||||
{ name: "filters", prefix: "ui.filters.", data: [] },
|
||||
{ name: "index page", prefix: "ui.index.", data: [] },
|
||||
{ name: "device page", prefix: "ui.device.", data: [] }
|
||||
{ name: "device page", prefix: "ui.device.", data: [] },
|
||||
];
|
||||
|
||||
if (confs.fulfilled) {
|
||||
@@ -312,7 +312,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
uiConfigComponent,
|
||||
Object.assign(
|
||||
{
|
||||
onUpdate: errs => {
|
||||
onUpdate: (errs: string[]) => {
|
||||
const errors = errs ? Object.values(errs) : [];
|
||||
if (errors.length) {
|
||||
for (const err of errors)
|
||||
@@ -329,18 +329,18 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
store.fulfill(0, Date.now());
|
||||
},
|
||||
onError: err => {
|
||||
onError: (err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
overlay.close(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
attrs
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
`Edit ${sub.name}`
|
||||
)
|
||||
@@ -359,8 +359,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
renderTable(confs, vnode.state["searchString"])
|
||||
),
|
||||
m(".actions-bar", [newConfig].concat(subs))
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+6
-4
@@ -22,7 +22,9 @@ import { m } from "./components";
|
||||
import config from "./config";
|
||||
import * as store from "./store";
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("devices", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -31,13 +33,13 @@ export function init(args): Promise<{}> {
|
||||
|
||||
return Promise.resolve({
|
||||
deviceId: args.id,
|
||||
deviceFilter: ["=", ["PARAM", "DeviceID.ID"], args.id]
|
||||
deviceFilter: ["=", ["PARAM", "DeviceID.ID"], args.id],
|
||||
});
|
||||
}
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = `${vnode.attrs["deviceId"]} - Devices - GenieACS`;
|
||||
|
||||
const dev = store.fetch("devices", vnode.attrs["deviceFilter"]).value;
|
||||
@@ -49,6 +51,6 @@ export const component: ClosureComponent = (): Component => {
|
||||
cmps.push(m.context({ device: dev[0] }, c["type"], c));
|
||||
|
||||
return [m("h1", vnode.attrs["deviceId"]), cmps];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+24
-22
@@ -35,7 +35,7 @@ const PAGE_SIZE = config.ui.pageSize || 10;
|
||||
|
||||
const memoizedParse = memoize(parse);
|
||||
const memoizedJsonParse = memoize(JSON.parse);
|
||||
const memoizedGetSortable = memoize(p => {
|
||||
const memoizedGetSortable = memoize((p) => {
|
||||
const expressionParams = extractParams(p);
|
||||
if (expressionParams.length === 1) {
|
||||
const param = evaluate(expressionParams[0]);
|
||||
@@ -49,19 +49,21 @@ const getDownloadUrl = memoize((filter, indexParameters) => {
|
||||
for (const p of indexParameters) columns[p.label] = stringify(p.parameter);
|
||||
return `api/devices.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(columns)
|
||||
columns: JSON.stringify(columns),
|
||||
})}`;
|
||||
});
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return expressionParser.map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return expressionParser.map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("devices", e[2], e[3]);
|
||||
return e;
|
||||
});
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!window.authorizer.hasAccess("devices", 2))
|
||||
return void reject(new Error("You are not authorized to view this page"));
|
||||
@@ -72,14 +74,14 @@ export function init(args): Promise<{}> {
|
||||
if (!indexParameters.length) {
|
||||
indexParameters.push({
|
||||
label: "ID",
|
||||
parameter: ["PARAM", "DeviceID.ID"]
|
||||
parameter: ["PARAM", "DeviceID.ID"],
|
||||
});
|
||||
}
|
||||
resolve({ filter, indexParameters, sort });
|
||||
});
|
||||
}
|
||||
|
||||
function renderActions(selected): Children {
|
||||
function renderActions(selected: Set<string>): Children {
|
||||
const buttons = [];
|
||||
|
||||
buttons.push(
|
||||
@@ -92,10 +94,10 @@ function renderActions(selected): Children {
|
||||
for (const d of selected) {
|
||||
taskQueue.queueTask({
|
||||
name: "reboot",
|
||||
device: d
|
||||
device: d,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Reboot"
|
||||
)
|
||||
@@ -111,10 +113,10 @@ function renderActions(selected): Children {
|
||||
for (const d of selected) {
|
||||
taskQueue.queueTask({
|
||||
name: "factoryReset",
|
||||
device: d
|
||||
device: d,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"Reset"
|
||||
)
|
||||
@@ -139,13 +141,13 @@ function renderActions(selected): Children {
|
||||
notifications.push("success", `${id}: Deleted`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", `${id}: ${err.message}`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
@@ -171,13 +173,13 @@ function renderActions(selected): Children {
|
||||
notifications.push("success", `${id}: Tags updated`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", `${id}: ${err.message}`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
}
|
||||
},
|
||||
},
|
||||
"Tag"
|
||||
)
|
||||
@@ -205,13 +207,13 @@ function renderActions(selected): Children {
|
||||
notifications.push("success", `${id}: Tags updated`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", `${id}: ${err.message}`);
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
if (--counter === 0) store.fulfill(0, Date.now());
|
||||
}
|
||||
},
|
||||
},
|
||||
"Untag"
|
||||
)
|
||||
@@ -222,7 +224,7 @@ function renderActions(selected): Children {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Devices - GenieACS";
|
||||
const attributes = vnode.attrs["indexParameters"];
|
||||
|
||||
@@ -273,7 +275,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const devs = store.fetch("devices", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
const count = store.count("devices", filter);
|
||||
|
||||
@@ -302,7 +304,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
href: `#!/devices/${encodeURIComponent(
|
||||
device["DeviceID.ID"].value[0]
|
||||
)}`
|
||||
)}`,
|
||||
},
|
||||
"Show"
|
||||
);
|
||||
@@ -319,8 +321,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing devices"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+36
-36
@@ -22,7 +22,7 @@ import m, {
|
||||
Child,
|
||||
ClosureComponent,
|
||||
Component,
|
||||
VnodeDOM
|
||||
VnodeDOM,
|
||||
} from "mithril";
|
||||
import * as taskQueue from "./task-queue";
|
||||
import * as store from "./store";
|
||||
@@ -50,18 +50,18 @@ function renderStagingSpv(task, queueFunc, cancelFunc): Children {
|
||||
"select",
|
||||
{
|
||||
value: task.parameterValues[0][1].toString(),
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
e.redraw = false;
|
||||
task.parameterValues[0][1] = input.dom.value;
|
||||
},
|
||||
onkeydown: keydown,
|
||||
oncreate: vnode => {
|
||||
oncreate: (vnode) => {
|
||||
(vnode.dom as HTMLSelectElement).focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
[
|
||||
m("option", { value: "true" }, "true"),
|
||||
m("option", { value: "false" }, "false")
|
||||
m("option", { value: "false" }, "false"),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
@@ -72,18 +72,18 @@ function renderStagingSpv(task, queueFunc, cancelFunc): Children {
|
||||
? "number"
|
||||
: "text",
|
||||
value: task.parameterValues[0][1],
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
e.redraw = false;
|
||||
task.parameterValues[0][1] = input.dom.value;
|
||||
},
|
||||
onkeydown: keydown,
|
||||
oncreate: vnode => {
|
||||
oncreate: (vnode) => {
|
||||
(vnode.dom as HTMLInputElement).focus();
|
||||
(vnode.dom as HTMLInputElement).select();
|
||||
// Need to prevent scrolling on focus because
|
||||
// we're animating height and using overflow: hidden
|
||||
(vnode.dom.parentNode.parentNode as Element).scrollTop = 0;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ function renderStagingDownload(task): Children {
|
||||
"2 Web Content",
|
||||
"3 Vendor Configuration File",
|
||||
"4 Tone File",
|
||||
"5 Ringer File"
|
||||
].map(t =>
|
||||
"5 Ringer File",
|
||||
].map((t) =>
|
||||
m(
|
||||
"option",
|
||||
{ disabled: !t, value: t, selected: (task.fileType || "") === t },
|
||||
@@ -117,14 +117,14 @@ function renderStagingDownload(task): Children {
|
||||
.concat(
|
||||
files.value
|
||||
.filter(
|
||||
f =>
|
||||
(f) =>
|
||||
(!f["metadata.oui"] || f["metadata.oui"] === oui) &&
|
||||
(!f["metadata.productClass"] ||
|
||||
f["metadata.productClass"] === productClass)
|
||||
)
|
||||
.map(f => f._id)
|
||||
.map((f) => f._id)
|
||||
)
|
||||
.map(f =>
|
||||
.map((f) =>
|
||||
m(
|
||||
"option",
|
||||
{ disabled: !f, value: f, selected: (task.fileName || "") === f },
|
||||
@@ -137,7 +137,7 @@ function renderStagingDownload(task): Children {
|
||||
m(
|
||||
"select",
|
||||
{
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
const f = e.target.value;
|
||||
task.fileName = f;
|
||||
task.fileType = "";
|
||||
@@ -145,7 +145,7 @@ function renderStagingDownload(task): Children {
|
||||
if (file._id === f) task.fileType = file["metadata.fileType"];
|
||||
},
|
||||
disabled: files.fulfilling,
|
||||
style: "width: 350px"
|
||||
style: "width: 350px",
|
||||
},
|
||||
filesList
|
||||
),
|
||||
@@ -153,12 +153,12 @@ function renderStagingDownload(task): Children {
|
||||
m(
|
||||
"select",
|
||||
{
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
task.fileType = e.target.value;
|
||||
}
|
||||
},
|
||||
},
|
||||
typesList
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ function renderQueue(queue): Child[] {
|
||||
title: "Retry this task",
|
||||
onclick: () => {
|
||||
taskQueue.queueTask(t);
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("retry")
|
||||
)
|
||||
@@ -230,7 +230,7 @@ function renderQueue(queue): Child[] {
|
||||
title: "Remove this task",
|
||||
onclick: () => {
|
||||
taskQueue.deleteTask(t);
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("remove")
|
||||
)
|
||||
@@ -324,18 +324,18 @@ function renderNotifications(notifs): Child[] {
|
||||
{
|
||||
class: n.type,
|
||||
style: "position: absolute;opacity: 0",
|
||||
oncreate: vnode => {
|
||||
oncreate: (vnode) => {
|
||||
(vnode.dom as HTMLDivElement).style.opacity = "1";
|
||||
},
|
||||
onbeforeremove: vnode => {
|
||||
onbeforeremove: (vnode) => {
|
||||
(vnode.dom as HTMLDivElement).style.opacity = "0";
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
key: n.timestamp
|
||||
key: n.timestamp,
|
||||
},
|
||||
m("div", buttons, n.message)
|
||||
)
|
||||
@@ -346,7 +346,7 @@ function renderNotifications(notifs): Child[] {
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const queue = taskQueue.getQueue();
|
||||
const staging = taskQueue.getStaging();
|
||||
const notifs = notifications.getNotifications();
|
||||
@@ -395,7 +395,7 @@ const component: ClosureComponent = (): Component => {
|
||||
disabled: !statusCount.queued,
|
||||
onclick: () => {
|
||||
const tasks = Array.from(taskQueue.getQueue()).filter(
|
||||
t => t["status"] === "queued"
|
||||
(t) => t["status"] === "queued"
|
||||
);
|
||||
taskQueue
|
||||
.commit(
|
||||
@@ -442,7 +442,7 @@ const component: ClosureComponent = (): Component => {
|
||||
.then(() => {
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Commit"
|
||||
),
|
||||
@@ -451,7 +451,7 @@ const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
title: "Clear tasks",
|
||||
onclick: taskQueue.clear,
|
||||
disabled: !queueElements.length
|
||||
disabled: !queueElements.length,
|
||||
},
|
||||
"Clear"
|
||||
)
|
||||
@@ -487,31 +487,31 @@ const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
key: "drawer",
|
||||
style: "opacity: 0;height: 0;",
|
||||
oncreate: vnode2 => {
|
||||
oncreate: (vnode2) => {
|
||||
vnode.state["mouseIn"] = false;
|
||||
(vnode2.dom as HTMLDivElement).style.opacity = "1";
|
||||
resizeDrawer();
|
||||
},
|
||||
onmouseover: e => {
|
||||
onmouseover: (e) => {
|
||||
vnode.state["mouseIn"] = true;
|
||||
resizeDrawer();
|
||||
e.redraw = false;
|
||||
},
|
||||
onmouseleave: e => {
|
||||
onmouseleave: (e) => {
|
||||
vnode.state["mouseIn"] = false;
|
||||
resizeDrawer();
|
||||
e.redraw = false;
|
||||
},
|
||||
onupdate: resizeDrawer,
|
||||
onbeforeremove: vnode2 => {
|
||||
onbeforeremove: (vnode2) => {
|
||||
(vnode2.dom as HTMLDivElement).onmouseover = null;
|
||||
(vnode2.dom as HTMLDivElement).onmouseleave = null;
|
||||
(vnode2.dom as HTMLDivElement).style.opacity = "0";
|
||||
(vnode2.dom as HTMLDivElement).style.height = "0";
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 500);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
statusElement,
|
||||
stagingElements.length ? stagingElements : m(".queue", queueElements)
|
||||
@@ -527,12 +527,12 @@ const component: ClosureComponent = (): Component => {
|
||||
key: "notifications",
|
||||
style: "position: relative;",
|
||||
onupdate: repositionNotifications,
|
||||
oncreate: repositionNotifications
|
||||
oncreate: repositionNotifications,
|
||||
},
|
||||
notificationElements
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ function onError(): void {
|
||||
{
|
||||
Reload: () => {
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -47,14 +47,14 @@ export function loadCodeMirror(): Promise<void> {
|
||||
import(
|
||||
/* webpackChunkName: "codemirror" */ "codemirror/mode/javascript/javascript"
|
||||
),
|
||||
import(/* webpackChunkName: "codemirror" */ "codemirror/mode/yaml/yaml")
|
||||
import(/* webpackChunkName: "codemirror" */ "codemirror/mode/yaml/yaml"),
|
||||
];
|
||||
Promise.all(promises)
|
||||
.then(modules => {
|
||||
.then((modules) => {
|
||||
codeMirror = modules[0];
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
onError();
|
||||
reject(err);
|
||||
});
|
||||
@@ -66,11 +66,11 @@ export function loadYaml(): Promise<void> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
import(/* webpackChunkName: "yaml" */ "yaml")
|
||||
.then(module => {
|
||||
.then((module) => {
|
||||
yaml = module;
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
onError();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@ export const component: ClosureComponent = (): Component => {
|
||||
view: function (vnode) {
|
||||
document.title = "Error! - GenieACS";
|
||||
return m("p.error", vnode.attrs["error"]);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+18
-16
@@ -41,10 +41,10 @@ const attributes = [
|
||||
{ id: "message", label: "Message" },
|
||||
{ id: "detail", label: "Detail" },
|
||||
{ id: "retries", label: "Retries" },
|
||||
{ id: "timestamp", label: "Timestamp" }
|
||||
{ id: "timestamp", label: "Timestamp" },
|
||||
];
|
||||
|
||||
const getDownloadUrl = memoize(filter => {
|
||||
const getDownloadUrl = memoize((filter) => {
|
||||
const cols = {};
|
||||
for (const attr of attributes) {
|
||||
cols[attr.label] =
|
||||
@@ -53,19 +53,21 @@ const getDownloadUrl = memoize(filter => {
|
||||
|
||||
return `api/faults.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(cols)
|
||||
columns: JSON.stringify(cols),
|
||||
})}`;
|
||||
});
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("faults", e[2], e[3]);
|
||||
return e;
|
||||
});
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("faults", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -85,7 +87,7 @@ export function init(args): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Faults - GenieACS";
|
||||
|
||||
function showMore(): void {
|
||||
@@ -130,7 +132,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const faults = store.fetch("faults", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
const count = store.count("faults", filter);
|
||||
|
||||
@@ -165,13 +167,13 @@ export const component: ClosureComponent = (): Component => {
|
||||
attrs["downloadUrl"] = downloadUrl;
|
||||
|
||||
if (window.authorizer.hasAccess("faults", 3)) {
|
||||
attrs["actionsCallback"] = (selected): Children => {
|
||||
attrs["actionsCallback"] = (selected: Set<string>): Children => {
|
||||
return m(
|
||||
"button.primary",
|
||||
{
|
||||
disabled: selected.size === 0,
|
||||
title: "Delete selected faults",
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.redraw = false;
|
||||
e.target.disabled = true;
|
||||
|
||||
@@ -179,22 +181,22 @@ export const component: ClosureComponent = (): Component => {
|
||||
return;
|
||||
|
||||
Promise.all(
|
||||
Array.from(selected).map(id =>
|
||||
Array.from(selected).map((id) =>
|
||||
store.deleteResource("faults", id)
|
||||
)
|
||||
)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
notifications.push(
|
||||
"success",
|
||||
`${res.length} faults deleted`
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
);
|
||||
@@ -209,8 +211,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing faults"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+31
-29
@@ -51,23 +51,23 @@ const attributes: {
|
||||
"2 Web Content",
|
||||
"3 Vendor Configuration File",
|
||||
"4 Tone File",
|
||||
"5 Ringer File"
|
||||
]
|
||||
"5 Ringer File",
|
||||
],
|
||||
},
|
||||
{ id: "metadata.oui", label: "OUI" },
|
||||
{ id: "metadata.productClass", label: "Product Class" },
|
||||
{ id: "metadata.version", label: "Version" }
|
||||
{ id: "metadata.version", label: "Version" },
|
||||
];
|
||||
|
||||
const formData = {
|
||||
resource: "files",
|
||||
attributes: attributes
|
||||
.slice(1) // remove _id from new object form
|
||||
.concat([{ id: "file", label: "File", type: "file" }])
|
||||
.concat([{ id: "file", label: "File", type: "file" }]),
|
||||
};
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("files", e[2], e[3]);
|
||||
return e;
|
||||
@@ -91,7 +91,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
|
||||
store
|
||||
.resourceExists("files", id)
|
||||
.then(exists => {
|
||||
.then((exists) => {
|
||||
if (exists) {
|
||||
store.fulfill(0, Date.now());
|
||||
return void resolve({ file: "File already exists" });
|
||||
@@ -99,7 +99,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
const headers = Object.assign(
|
||||
{
|
||||
"Content-Type": "application/octet-stream",
|
||||
Accept: "application/octet-stream"
|
||||
Accept: "application/octet-stream",
|
||||
},
|
||||
object
|
||||
);
|
||||
@@ -109,8 +109,8 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
method: "PUT",
|
||||
headers: headers,
|
||||
url: `api/files/${encodeURIComponent(id)}`,
|
||||
serialize: body => body, // Identity function to prevent JSON.parse on blob data
|
||||
body: file
|
||||
serialize: (body) => body, // Identity function to prevent JSON.parse on blob data
|
||||
body: file,
|
||||
})
|
||||
.then(() => {
|
||||
notifications.push(
|
||||
@@ -129,16 +129,18 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
});
|
||||
}
|
||||
|
||||
const getDownloadUrl = memoize(filter => {
|
||||
const getDownloadUrl = memoize((filter) => {
|
||||
const cols = {};
|
||||
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||
return `api/files.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(cols)
|
||||
columns: JSON.stringify(cols),
|
||||
})}`;
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("files", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -152,7 +154,7 @@ export function init(args): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Files - GenieACS";
|
||||
|
||||
function showMore(): void {
|
||||
@@ -195,7 +197,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const files = store.fetch("files", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
|
||||
const count = store.count("files", filter);
|
||||
@@ -212,7 +214,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
attrs["downloadUrl"] = downloadUrl;
|
||||
|
||||
if (window.authorizer.hasAccess("files", 3)) {
|
||||
attrs["actionsCallback"] = (selected): Children => {
|
||||
attrs["actionsCallback"] = (selected: Set<string>): Children => {
|
||||
return [
|
||||
m(
|
||||
"button.primary",
|
||||
@@ -225,9 +227,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
Object.assign(
|
||||
{
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -239,19 +241,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"New"
|
||||
),
|
||||
@@ -260,7 +262,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
title: "Delete selected files",
|
||||
disabled: !selected.size,
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (
|
||||
!confirm(`Deleting ${selected.size} files. Are you sure?`)
|
||||
)
|
||||
@@ -269,25 +271,25 @@ export const component: ClosureComponent = (): Component => {
|
||||
e.redraw = false;
|
||||
e.target.disabled = true;
|
||||
Promise.all(
|
||||
Array.from(selected).map(id =>
|
||||
Array.from(selected).map((id) =>
|
||||
store.deleteResource("files", id)
|
||||
)
|
||||
)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
notifications.push(
|
||||
"success",
|
||||
`${res.length} files deleted`
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -300,8 +302,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing files"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+15
-13
@@ -25,16 +25,16 @@ import * as smartQuery from "./smart-query";
|
||||
import { filterToMongoQuery } from "../lib/mongodb-functions";
|
||||
import { Expression } from "../lib/types";
|
||||
|
||||
const getAutocomplete = memoize(resource => {
|
||||
const getAutocomplete = memoize((resource) => {
|
||||
const labels = smartQuery.getLabels(resource);
|
||||
const autocomplete = new Autocomplete("autocomplete", (txt, cb) => {
|
||||
txt = txt.toLowerCase();
|
||||
cb(
|
||||
labels
|
||||
.filter(s => s.toLowerCase().includes(txt))
|
||||
.map(s => ({
|
||||
.filter((s) => s.toLowerCase().includes(txt))
|
||||
.map((s) => ({
|
||||
value: `${s}: `,
|
||||
tip: smartQuery.getTip(resource, s)
|
||||
tip: smartQuery.getTip(resource, s),
|
||||
}))
|
||||
);
|
||||
});
|
||||
@@ -53,7 +53,7 @@ function parseFilter(resource, f): Expression {
|
||||
|
||||
// Throw exception if invalid Mongo query
|
||||
filterToMongoQuery(
|
||||
map(exp, e => {
|
||||
map(exp, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack(resource, e[2], e[3]);
|
||||
return e;
|
||||
@@ -69,7 +69,7 @@ function stringifyFilter(f: Expression): string {
|
||||
return stringify(f);
|
||||
}
|
||||
|
||||
const splitFilter = memoize(filter => {
|
||||
const splitFilter = memoize((filter) => {
|
||||
if (!filter) return [""];
|
||||
const list = [];
|
||||
const f = parse(filter);
|
||||
@@ -83,7 +83,7 @@ const splitFilter = memoize(filter => {
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
if (
|
||||
!vnode.state["filterList"] ||
|
||||
vnode.attrs["filter"] !== vnode.state["filter"]
|
||||
@@ -95,7 +95,7 @@ const component: ClosureComponent = (): Component => {
|
||||
|
||||
function onChange(): void {
|
||||
vnode.state["filterInvalid"] = 0;
|
||||
vnode.state["filterList"] = vnode.state["filterList"].filter(f => f);
|
||||
vnode.state["filterList"] = vnode.state["filterList"].filter((f) => f);
|
||||
let filter = vnode.state["filterList"].map((f, idx) => {
|
||||
try {
|
||||
return parseFilter(vnode.attrs["resource"], f);
|
||||
@@ -128,19 +128,21 @@ const component: ClosureComponent = (): Component => {
|
||||
(vnode.state["filterInvalid"] >> idx) & 1 ? "error" : ""
|
||||
}`,
|
||||
value: fltr,
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
vnode.state["filterList"] = vnode.state["filterList"].slice();
|
||||
vnode.state["filterList"][idx] = e.target.value.trim();
|
||||
onChange();
|
||||
},
|
||||
oncreate: vn => {
|
||||
getAutocomplete(vnode.attrs["resource"]).attach(vn.dom);
|
||||
}
|
||||
oncreate: (vn) => {
|
||||
getAutocomplete(vnode.attrs["resource"]).attach(
|
||||
vn.dom as HTMLInputElement
|
||||
);
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+12
-12
@@ -57,14 +57,14 @@ function renderTable(
|
||||
const selectAll = m("input", {
|
||||
type: "checkbox",
|
||||
checked: records.length && selected.size === records.length,
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
for (const record of records) {
|
||||
const id = record["_id"] || record["DeviceID.ID"].value[0];
|
||||
if (e.target.checked) selected.add(id);
|
||||
else selected.delete(id);
|
||||
}
|
||||
},
|
||||
disabled: !total
|
||||
disabled: !total,
|
||||
});
|
||||
labels.push(m("th", selectAll));
|
||||
}
|
||||
@@ -89,7 +89,7 @@ function renderTable(
|
||||
onclick: () => {
|
||||
if (sortAttributes[i] > 0) direction *= -1;
|
||||
return onSortChange({ [i]: direction });
|
||||
}
|
||||
},
|
||||
},
|
||||
symbol
|
||||
);
|
||||
@@ -106,14 +106,14 @@ function renderTable(
|
||||
const checkbox = m("input", {
|
||||
type: "checkbox",
|
||||
checked: selected.has(id),
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
if (e.target.checked) selected.add(id);
|
||||
else selected.delete(id);
|
||||
},
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.redraw = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
tds.push(m("td", checkbox));
|
||||
}
|
||||
@@ -152,14 +152,14 @@ function renderTable(
|
||||
m(
|
||||
"tr",
|
||||
{
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (["INPUT", "BUTTON", "A"].includes(e.target.nodeName)) {
|
||||
e.redraw = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selected.delete(id)) selected.add(id);
|
||||
}
|
||||
},
|
||||
},
|
||||
tds
|
||||
)
|
||||
@@ -180,7 +180,7 @@ function renderTable(
|
||||
{
|
||||
title: "Show more records",
|
||||
onclick: showMoreCallback,
|
||||
disabled: !data.length || records.length >= total
|
||||
disabled: !data.length || records.length >= total,
|
||||
},
|
||||
"More"
|
||||
)
|
||||
@@ -203,7 +203,7 @@ function renderTable(
|
||||
m("thead", m("tr", labels)),
|
||||
m("tbody", rows),
|
||||
tfoot
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
if (buttons.length) children.push(m("div.actions-bar", buttons));
|
||||
@@ -214,7 +214,7 @@ const component: ClosureComponent = (): Component => {
|
||||
let selected = new Set<string>();
|
||||
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const attributes = vnode.attrs["attributes"];
|
||||
const data = vnode.attrs["data"];
|
||||
const valueCallback = vnode.attrs["valueCallback"];
|
||||
@@ -246,7 +246,7 @@ const component: ClosureComponent = (): Component => {
|
||||
actionsCallback,
|
||||
recordActionsCallback
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -31,12 +31,12 @@ const adminPages = [
|
||||
"files",
|
||||
"config",
|
||||
"users",
|
||||
"permissions"
|
||||
"permissions",
|
||||
];
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
let sideMenu, group;
|
||||
|
||||
if (adminPages.includes(vnode.attrs["page"])) {
|
||||
@@ -54,18 +54,18 @@ const component: ClosureComponent = (): Component => {
|
||||
m("img.logo", { src: "logo.svg" }),
|
||||
m(userMenu),
|
||||
m(menu, attrs),
|
||||
m(drawerComponent)
|
||||
m(drawerComponent),
|
||||
]),
|
||||
m(
|
||||
"#content-wrapper",
|
||||
sideMenu,
|
||||
m("#content", { class: `page-${vnode.attrs["page"]}` }, [
|
||||
vnode.children
|
||||
vnode.children,
|
||||
])
|
||||
),
|
||||
overlay.render()
|
||||
overlay.render(),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+16
-14
@@ -24,13 +24,15 @@ import * as notifications from "./notifications";
|
||||
import * as overlay from "./overlay";
|
||||
import changePasswordComponent from "./change-password-component";
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return Promise.resolve(args);
|
||||
}
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
if (window.username) m.route.set(vnode.attrs["continue"] || "/");
|
||||
|
||||
document.title = "Login - GenieACS";
|
||||
@@ -45,12 +47,12 @@ export const component: ClosureComponent = (): Component => {
|
||||
name: "username",
|
||||
type: "text",
|
||||
value: vnode.state["username"],
|
||||
oncreate: vnode2 => {
|
||||
oncreate: (vnode2) => {
|
||||
(vnode2.dom as HTMLInputElement).focus();
|
||||
},
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state["username"] = e.target.value;
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
m(
|
||||
@@ -60,9 +62,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
name: "password",
|
||||
type: "password",
|
||||
value: vnode.state["password"],
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
vnode.state["password"] = e.target.value;
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
m(
|
||||
@@ -71,19 +73,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
"button.primary",
|
||||
{
|
||||
type: "submit",
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.target.disabled = true;
|
||||
store
|
||||
.logIn(vnode.state["username"], vnode.state["password"])
|
||||
.then(() => {
|
||||
location.reload();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
e.target.disabled = false;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
"Login"
|
||||
)
|
||||
@@ -98,16 +100,16 @@ export const component: ClosureComponent = (): Component => {
|
||||
onPasswordChange: () => {
|
||||
overlay.close(cb);
|
||||
m.redraw();
|
||||
}
|
||||
},
|
||||
};
|
||||
return m(changePasswordComponent, attrs);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"Change password"
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -22,23 +22,23 @@ import * as overlay from "./overlay";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const text = vnode.attrs["text"];
|
||||
const element = vnode.attrs["element"] || "span";
|
||||
|
||||
function overflowed(_vnode): void {
|
||||
_vnode.dom.classList.add("long-text-overflowed");
|
||||
_vnode.dom.onclick = e => {
|
||||
_vnode.dom.onclick = (e) => {
|
||||
overlay.open(() => {
|
||||
return m("textarea.long-text", {
|
||||
value: text,
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
readonly: "",
|
||||
oncreate: vnode2 => {
|
||||
oncreate: (vnode2) => {
|
||||
(vnode2.dom as HTMLTextAreaElement).focus();
|
||||
(vnode2.dom as HTMLTextAreaElement).select();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
// prevent index page selection
|
||||
@@ -50,11 +50,11 @@ const component: ClosureComponent = (): Component => {
|
||||
return m(
|
||||
element,
|
||||
{
|
||||
oncreate: vnode2 => {
|
||||
oncreate: (vnode2) => {
|
||||
if (vnode2.dom.clientWidth !== vnode2.dom.scrollWidth)
|
||||
overflowed(vnode2);
|
||||
},
|
||||
onupdate: vnode2 => {
|
||||
onupdate: (vnode2) => {
|
||||
if (vnode2.dom.clientWidth === vnode2.dom.scrollWidth) {
|
||||
(vnode2.dom as HTMLElement).classList.remove(
|
||||
"long-text-overflowed"
|
||||
@@ -65,11 +65,11 @@ const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
},
|
||||
class: "long-text",
|
||||
title: text
|
||||
title: text,
|
||||
},
|
||||
text
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import m, { ClosureComponent, Component } from "mithril";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
const active = { [vnode.attrs["page"]]: "active" };
|
||||
|
||||
const tabs = [];
|
||||
@@ -59,7 +59,7 @@ const component: ClosureComponent = (): Component => {
|
||||
"presets",
|
||||
"provisions",
|
||||
"virtualParameters",
|
||||
"files"
|
||||
"files",
|
||||
];
|
||||
for (const page of adminPages) {
|
||||
if (window.authorizer.hasAccess(page, 2)) {
|
||||
@@ -75,7 +75,7 @@ const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
|
||||
return m("nav", m("ul", tabs));
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+6
-2
@@ -28,12 +28,16 @@ interface Notification {
|
||||
|
||||
const notifications = new Set<Notification>();
|
||||
|
||||
export function push(type, message, actions?): Notification {
|
||||
export function push(
|
||||
type: string,
|
||||
message: string,
|
||||
actions?: { [label: string]: () => void }
|
||||
): Notification {
|
||||
const n: Notification = {
|
||||
type: type,
|
||||
message: message,
|
||||
timestamp: Date.now(),
|
||||
actions: actions
|
||||
actions: actions,
|
||||
};
|
||||
notifications.add(n);
|
||||
m.redraw();
|
||||
|
||||
+7
-7
@@ -46,25 +46,25 @@ export function render(): Children {
|
||||
close(overlayCallback);
|
||||
},
|
||||
style: "opacity: 0",
|
||||
oncreate: vnode => {
|
||||
oncreate: (vnode) => {
|
||||
(vnode.dom as HTMLDivElement).focus();
|
||||
(vnode.dom as HTMLDivElement).style.opacity = "1";
|
||||
},
|
||||
onbeforeremove: vnode => {
|
||||
onbeforeremove: (vnode) => {
|
||||
(vnode.dom as HTMLDivElement).style.opacity = "0";
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
m(
|
||||
".overlay",
|
||||
{
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
},
|
||||
overlayCallback()
|
||||
)
|
||||
@@ -74,7 +74,7 @@ export function render(): Children {
|
||||
return null;
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", e => {
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (overlayCallback && e.keyCode === 27 && close(overlayCallback)) m.redraw();
|
||||
});
|
||||
|
||||
|
||||
+4
-4
@@ -30,7 +30,7 @@ for (const group of Object.values(GROUPS)) {
|
||||
CHARTS[chartName] = config.ui.overview.charts[chartName];
|
||||
}
|
||||
|
||||
function queryCharts(charts): {} {
|
||||
function queryCharts(charts: Record<string, unknown>): Record<string, unknown> {
|
||||
charts = Object.assign({}, charts);
|
||||
for (let [chartName, chart] of Object.entries(charts)) {
|
||||
charts[chartName] = chart = Object.assign({}, chart);
|
||||
@@ -44,7 +44,7 @@ function queryCharts(charts): {} {
|
||||
return charts;
|
||||
}
|
||||
|
||||
export function init(): Promise<{}> {
|
||||
export function init(): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("devices", 1)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -56,7 +56,7 @@ export function init(): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Overview - GenieACS";
|
||||
const children = [];
|
||||
for (const group of Object.values(GROUPS)) {
|
||||
@@ -79,6 +79,6 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+36
-34
@@ -51,21 +51,21 @@ const attributes = [
|
||||
"users",
|
||||
"presets",
|
||||
"provisions",
|
||||
"virtualParameters"
|
||||
]
|
||||
"virtualParameters",
|
||||
],
|
||||
},
|
||||
{ id: "filter", label: "Filter", type: "textarea" },
|
||||
{
|
||||
id: "access",
|
||||
label: "Access",
|
||||
type: "combo",
|
||||
options: ["1: count", "2: read", "3: write"]
|
||||
options: ["1: count", "2: read", "3: write"],
|
||||
},
|
||||
{ id: "validate", label: "Validate", type: "textarea" }
|
||||
{ id: "validate", label: "Validate", type: "textarea" },
|
||||
];
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("permissions", e[2], e[3]);
|
||||
return e;
|
||||
@@ -96,7 +96,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
object.filter = stringify(memoizedParse(object.filter));
|
||||
} catch (err) {
|
||||
return void resolve({
|
||||
filter: "Filter must be valid expression"
|
||||
filter: "Filter must be valid expression",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
object.validate = stringify(memoizedParse(object.validate));
|
||||
} catch (err) {
|
||||
return void resolve({
|
||||
validate: "Validate must be valid expression"
|
||||
validate: "Validate must be valid expression",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
|
||||
store
|
||||
.resourceExists("permissions", id)
|
||||
.then(exists => {
|
||||
.then((exists) => {
|
||||
if (exists) {
|
||||
store.fulfill(0, Date.now());
|
||||
return void resolve({ _id: "Permission already exists" });
|
||||
@@ -139,7 +139,7 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
store.fulfill(0, Date.now());
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
store.fulfill(0, Date.now());
|
||||
reject(err);
|
||||
});
|
||||
@@ -151,19 +151,21 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
|
||||
|
||||
const formData = {
|
||||
resource: "permissions",
|
||||
attributes: attributes
|
||||
attributes: attributes,
|
||||
};
|
||||
|
||||
const getDownloadUrl = memoize(filter => {
|
||||
const getDownloadUrl = memoize((filter) => {
|
||||
const cols = {};
|
||||
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||
return `api/permissions.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(cols)
|
||||
columns: JSON.stringify(cols),
|
||||
})}`;
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("permissions", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -176,7 +178,7 @@ export function init(args): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Permissions - GenieACS";
|
||||
|
||||
function showMore(): void {
|
||||
@@ -222,7 +224,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const permissions = store.fetch("permissions", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
|
||||
const count = store.count("permissions", filter);
|
||||
@@ -252,7 +254,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
attrs["downloadUrl"] = downloadUrl;
|
||||
|
||||
if (window.authorizer.hasAccess("permissions", 3)) {
|
||||
attrs["recordActionsCallback"] = permission => {
|
||||
attrs["recordActionsCallback"] = (permission) => {
|
||||
return [
|
||||
m(
|
||||
"button",
|
||||
@@ -266,17 +268,17 @@ export const component: ClosureComponent = (): Component => {
|
||||
)
|
||||
return;
|
||||
|
||||
putActionHandler("delete", permission).catch(err => {
|
||||
putActionHandler("delete", permission).catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getIcon("remove")
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
attrs["actionsCallback"] = (selected): Children => {
|
||||
attrs["actionsCallback"] = (selected: Set<string>): Children => {
|
||||
return [
|
||||
m(
|
||||
"button.primary",
|
||||
@@ -289,9 +291,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
Object.assign(
|
||||
{
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -303,19 +305,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"New"
|
||||
),
|
||||
@@ -324,7 +326,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
title: "Delete selected permissions",
|
||||
disabled: !selected.size,
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (
|
||||
!confirm(
|
||||
`Deleting ${selected.size} permissions. Are you sure?`
|
||||
@@ -335,25 +337,25 @@ export const component: ClosureComponent = (): Component => {
|
||||
e.redraw = false;
|
||||
e.target.disabled = true;
|
||||
Promise.all(
|
||||
Array.from(selected).map(id =>
|
||||
Array.from(selected).map((id) =>
|
||||
store.deleteResource("permissions", id)
|
||||
)
|
||||
)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
notifications.push(
|
||||
"success",
|
||||
`${res.length} permissions deleted`
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -366,8 +368,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing permissions"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+14
-14
@@ -43,19 +43,19 @@ function drawChart(chartData): Children {
|
||||
legend.push(
|
||||
m(".legend-line", [
|
||||
m("span.color", {
|
||||
style: `background-color: ${slice["color"]} !important;`
|
||||
style: `background-color: ${slice["color"]} !important;`,
|
||||
}),
|
||||
`${slice["label"]}: `,
|
||||
m(
|
||||
"a",
|
||||
{
|
||||
href: `#!/devices/?${m.buildQueryString({
|
||||
filter: memoizedStringify(slice["filter"])
|
||||
})}`
|
||||
filter: memoizedStringify(slice["filter"]),
|
||||
})}`,
|
||||
},
|
||||
slice["count"]["value"] || 0
|
||||
),
|
||||
` (${(percent * 100).toFixed(2)}%)`
|
||||
` (${(percent * 100).toFixed(2)}%)`,
|
||||
])
|
||||
);
|
||||
|
||||
@@ -76,7 +76,7 @@ function drawChart(chartData): Children {
|
||||
paths.push(
|
||||
m("path", {
|
||||
d: sketch,
|
||||
fill: slice["color"]
|
||||
fill: slice["color"],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -90,13 +90,13 @@ function drawChart(chartData): Children {
|
||||
"a",
|
||||
{
|
||||
"xlink:href": `#!/devices/?${m.buildQueryString({
|
||||
filter: memoizedStringify(slice["filter"])
|
||||
})}`
|
||||
filter: memoizedStringify(slice["filter"]),
|
||||
})}`,
|
||||
},
|
||||
[
|
||||
m("path", {
|
||||
d: sketch,
|
||||
"fill-opacity": 0
|
||||
"fill-opacity": 0,
|
||||
}),
|
||||
m(
|
||||
"text",
|
||||
@@ -104,10 +104,10 @@ function drawChart(chartData): Children {
|
||||
x: percentageX,
|
||||
y: percentageY,
|
||||
"dominant-baseline": "middle",
|
||||
"text-anchor": "middle"
|
||||
"text-anchor": "middle",
|
||||
},
|
||||
`${(percent * 100).toFixed(2)}%`
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
);
|
||||
@@ -125,19 +125,19 @@ function drawChart(chartData): Children {
|
||||
width: "204px",
|
||||
height: "204px",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
"xmlns:xlink": "http://www.w3.org/1999/xlink"
|
||||
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||||
},
|
||||
paths.concat(links)
|
||||
),
|
||||
m(".legend", legend)
|
||||
m(".legend", legend),
|
||||
]);
|
||||
}
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
return drawChart(vnode.attrs["chart"]);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+42
-40
@@ -17,7 +17,7 @@
|
||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ClosureComponent, Component, Children } from "mithril";
|
||||
import { ClosureComponent, Component, Children, Vnode } from "mithril";
|
||||
import { m } from "./components";
|
||||
import config from "./config";
|
||||
import filterComponent from "./filter-component";
|
||||
@@ -43,11 +43,11 @@ const attributes = [
|
||||
{ id: "events", label: "Events" },
|
||||
{ id: "precondition", label: "Precondition", type: "textarea" },
|
||||
{ id: "provision", label: "Provision", type: "combo" },
|
||||
{ id: "provisionArgs", label: "Arguments" }
|
||||
{ id: "provisionArgs", label: "Arguments" },
|
||||
];
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("presets", e[2], e[3]);
|
||||
return e;
|
||||
@@ -77,14 +77,14 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
object.precondition = stringify(memoizedParse(object.precondition));
|
||||
} catch (err) {
|
||||
return void resolve({
|
||||
precondition: "Precondition must be valid expression"
|
||||
precondition: "Precondition must be valid expression",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
store
|
||||
.resourceExists("presets", id)
|
||||
.then(exists => {
|
||||
.then((exists) => {
|
||||
if (exists && isNew) {
|
||||
store.fulfill(0, Date.now());
|
||||
return void resolve({ _id: "Preset already exists" });
|
||||
@@ -116,7 +116,7 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
store.fulfill(0, Date.now());
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
@@ -128,19 +128,21 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
|
||||
const formData = {
|
||||
resource: "presets",
|
||||
attributes: attributes
|
||||
attributes: attributes,
|
||||
};
|
||||
|
||||
const getDownloadUrl = memoize(filter => {
|
||||
const getDownloadUrl = memoize((filter) => {
|
||||
const cols = {};
|
||||
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||
return `api/presets.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(cols)
|
||||
columns: JSON.stringify(cols),
|
||||
})}`;
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("presets", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -154,7 +156,7 @@ export function init(args): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Presets - GenieACS";
|
||||
|
||||
function showMore(): void {
|
||||
@@ -207,7 +209,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const presets = store.fetch("presets", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
const count = store.count("presets", filter);
|
||||
|
||||
@@ -220,7 +222,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
"reboot",
|
||||
"reset",
|
||||
"download",
|
||||
"instances"
|
||||
"instances",
|
||||
]);
|
||||
|
||||
const provisions = store.fetch("provisions", true);
|
||||
@@ -231,19 +233,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
}
|
||||
|
||||
const provisionAttr = attributes.find(attr => {
|
||||
const provisionAttr = attributes.find((attr) => {
|
||||
return attr.id === "provision";
|
||||
});
|
||||
provisionAttr["options"] = Array.from(provisionIds);
|
||||
|
||||
const downloadUrl = getDownloadUrl(filter);
|
||||
|
||||
const valueCallback = (attr, preset): {} => {
|
||||
const valueCallback = (attr, preset): Vnode => {
|
||||
if (attr.id === "precondition") {
|
||||
let devicesUrl = "#!/devices";
|
||||
if (preset["precondition"].length) {
|
||||
devicesUrl += `?${m.buildQueryString({
|
||||
filter: preset["precondition"]
|
||||
filter: preset["precondition"],
|
||||
})}`;
|
||||
}
|
||||
|
||||
@@ -260,8 +262,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
"a",
|
||||
{
|
||||
href: `#!/admin/provisions?${m.buildQueryString({
|
||||
filter: `Q("ID", "${preset["provision"]}")`
|
||||
})}`
|
||||
filter: `Q("ID", "${preset["provision"]}")`,
|
||||
})}`,
|
||||
},
|
||||
preset["provision"]
|
||||
);
|
||||
@@ -279,7 +281,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
attrs["onSortChange"] = onSortChange;
|
||||
attrs["downloadUrl"] = downloadUrl;
|
||||
attrs["valueCallback"] = valueCallback;
|
||||
attrs["recordActionsCallback"] = preset => {
|
||||
attrs["recordActionsCallback"] = (preset) => {
|
||||
return [
|
||||
m(
|
||||
"a",
|
||||
@@ -300,9 +302,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
base: preset,
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, false)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -314,27 +316,27 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"Show"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
if (window.authorizer.hasAccess("presets", 3)) {
|
||||
attrs["actionsCallback"] = (selected): Children => {
|
||||
attrs["actionsCallback"] = (selected: Set<string>): Children => {
|
||||
return [
|
||||
m(
|
||||
"button.primary",
|
||||
@@ -347,9 +349,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
Object.assign(
|
||||
{
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, true)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -361,19 +363,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"New"
|
||||
),
|
||||
@@ -382,7 +384,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
title: "Delete selected presets",
|
||||
disabled: !selected.size,
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (
|
||||
!confirm(`Deleting ${selected.size} presets. Are you sure?`)
|
||||
)
|
||||
@@ -391,25 +393,25 @@ export const component: ClosureComponent = (): Component => {
|
||||
e.redraw = false;
|
||||
e.target.disabled = true;
|
||||
Promise.all(
|
||||
Array.from(selected).map(id =>
|
||||
Array.from(selected).map((id) =>
|
||||
store.deleteResource("presets", id)
|
||||
)
|
||||
)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
notifications.push(
|
||||
"success",
|
||||
`${res.length} presets deleted`
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -422,8 +424,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing presets"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
+34
-32
@@ -38,11 +38,11 @@ const memoizedJsonParse = memoize(JSON.parse);
|
||||
|
||||
const attributes = [
|
||||
{ id: "_id", label: "Name" },
|
||||
{ id: "script", label: "Script", type: "code" }
|
||||
{ id: "script", label: "Script", type: "code" },
|
||||
];
|
||||
|
||||
const unpackSmartQuery = memoize(query => {
|
||||
return map(query, e => {
|
||||
const unpackSmartQuery = memoize((query) => {
|
||||
return map(query, (e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
|
||||
return smartQuery.unpack("provisions", e[2], e[3]);
|
||||
return e;
|
||||
@@ -64,7 +64,7 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
|
||||
store
|
||||
.resourceExists("provisions", id)
|
||||
.then(exists => {
|
||||
.then((exists) => {
|
||||
if (exists && isNew) {
|
||||
store.fulfill(0, Date.now());
|
||||
return void resolve({ _id: "Provision already exists" });
|
||||
@@ -96,7 +96,7 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
store.fulfill(0, Date.now());
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
store.fulfill(0, Date.now());
|
||||
reject(err);
|
||||
});
|
||||
@@ -108,19 +108,21 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
||||
|
||||
const formData = {
|
||||
resource: "provisions",
|
||||
attributes: attributes
|
||||
attributes: attributes,
|
||||
};
|
||||
|
||||
const getDownloadUrl = memoize(filter => {
|
||||
const getDownloadUrl = memoize((filter) => {
|
||||
const cols = {};
|
||||
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||
return `api/provisions.csv?${m.buildQueryString({
|
||||
filter: stringify(filter),
|
||||
columns: JSON.stringify(cols)
|
||||
columns: JSON.stringify(cols),
|
||||
})}`;
|
||||
});
|
||||
|
||||
export function init(args): Promise<{}> {
|
||||
export function init(
|
||||
args: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
if (!window.authorizer.hasAccess("provisions", 2)) {
|
||||
return Promise.reject(
|
||||
new Error("You are not authorized to view this page")
|
||||
@@ -141,7 +143,7 @@ export function init(args): Promise<{}> {
|
||||
|
||||
export const component: ClosureComponent = (): Component => {
|
||||
return {
|
||||
view: vnode => {
|
||||
view: (vnode) => {
|
||||
document.title = "Provisions - GenieACS";
|
||||
|
||||
function showMore(): void {
|
||||
@@ -184,7 +186,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
|
||||
const provisions = store.fetch("provisions", filter, {
|
||||
limit: vnode.state["showCount"] || PAGE_SIZE,
|
||||
sort: sort
|
||||
sort: sort,
|
||||
});
|
||||
|
||||
const count = store.count("provisions", filter);
|
||||
@@ -199,7 +201,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
attrs["sortAttributes"] = sortAttributes;
|
||||
attrs["onSortChange"] = onSortChange;
|
||||
attrs["downloadUrl"] = downloadUrl;
|
||||
attrs["recordActionsCallback"] = provision => {
|
||||
attrs["recordActionsCallback"] = (provision) => {
|
||||
return [
|
||||
m(
|
||||
"a",
|
||||
@@ -212,9 +214,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
base: provision,
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, false)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -226,27 +228,27 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"Show"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
if (window.authorizer.hasAccess("provisions", 3)) {
|
||||
attrs["actionsCallback"] = (selected): Children => {
|
||||
attrs["actionsCallback"] = (selected: Set<string>): Children => {
|
||||
return [
|
||||
m(
|
||||
"button.primary",
|
||||
@@ -259,9 +261,9 @@ export const component: ClosureComponent = (): Component => {
|
||||
Object.assign(
|
||||
{
|
||||
actionHandler: (action, object) => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
putActionHandler(action, object, true)
|
||||
.then(errors => {
|
||||
.then((errors) => {
|
||||
const errorList = errors
|
||||
? Object.values(errors)
|
||||
: [];
|
||||
@@ -273,19 +275,19 @@ export const component: ClosureComponent = (): Component => {
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
formData
|
||||
)
|
||||
);
|
||||
};
|
||||
overlay.open(cb);
|
||||
}
|
||||
},
|
||||
},
|
||||
"New"
|
||||
),
|
||||
@@ -294,7 +296,7 @@ export const component: ClosureComponent = (): Component => {
|
||||
{
|
||||
title: "Delete selected provisions",
|
||||
disabled: !selected.size,
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (
|
||||
!confirm(
|
||||
`Deleting ${selected.size} provisions. Are you sure?`
|
||||
@@ -305,25 +307,25 @@ export const component: ClosureComponent = (): Component => {
|
||||
e.redraw = false;
|
||||
e.target.disabled = true;
|
||||
Promise.all(
|
||||
Array.from(selected).map(id =>
|
||||
Array.from(selected).map((id) =>
|
||||
store.deleteResource("provisions", id)
|
||||
)
|
||||
)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
notifications.push(
|
||||
"success",
|
||||
`${res.length} provisions deleted`
|
||||
);
|
||||
store.fulfill(0, Date.now());
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.fulfill(0, Date.now());
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
"Delete"
|
||||
)
|
||||
),
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -336,8 +338,8 @@ export const component: ClosureComponent = (): Component => {
|
||||
return [
|
||||
m("h1", "Listing provisions"),
|
||||
m(filterComponent, filterAttrs),
|
||||
m(indexTableComponent, attrs)
|
||||
m(indexTableComponent, attrs),
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user