Update linting
This commit is contained in:
1 parent
4f2dfb4f48
commit
0c9c027a77
112 files changed
+3781
-4125
No files matched your search
+1
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"es6": true,
|
"es2022": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
"no-shadow": ["error", { "allow": ["err"] }],
|
"no-shadow": ["error", { "allow": ["err"] }],
|
||||||
"handle-callback-err": "error",
|
"handle-callback-err": "error",
|
||||||
"prefer-arrow-callback": "error",
|
"prefer-arrow-callback": "error",
|
||||||
"curly": ["error", "multi-or-nest", "consistent"],
|
|
||||||
"no-buffer-constructor": "error",
|
"no-buffer-constructor": "error",
|
||||||
"prefer-const": ["error", { "destructuring": "all" }],
|
"prefer-const": ["error", { "destructuring": "all" }],
|
||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
@@ -32,7 +31,6 @@
|
|||||||
],
|
],
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
"require-atomic-updates": 0,
|
|
||||||
"no-prototype-builtins": 0
|
"no-prototype-builtins": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ function exitWorkerGracefully(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exitWorkerUngracefully(): void {
|
function exitWorkerUngracefully(): void {
|
||||||
extensions.killAll().finally(() => {
|
void extensions.killAll().finally(() => {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ if (!cluster.worker) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server
|
server
|
||||||
.stop(false)
|
.stop(false)
|
||||||
.then(exitWorkerGracefully)
|
.then(exitWorkerGracefully)
|
||||||
@@ -126,7 +126,7 @@ if (!cluster.worker) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server
|
server
|
||||||
.stop(false)
|
.stop(false)
|
||||||
.then(exitWorkerGracefully)
|
.then(exitWorkerGracefully)
|
||||||
|
|||||||
+2
-2
@@ -41,12 +41,12 @@ function errorToFault(err: Error): Fault {
|
|||||||
fault.detail["stack"] = err.stack;
|
fault.detail["stack"] = err.stack;
|
||||||
// Trim the stack trace
|
// Trim the stack trace
|
||||||
const stackTrimIndex = fault.detail["stack"].match(
|
const stackTrimIndex = fault.detail["stack"].match(
|
||||||
/\s+at\s[^\s]+\s\(.*genieacs-ext:.+\)/
|
/\s+at\s[^\s]+\s\(.*genieacs-ext:.+\)/,
|
||||||
);
|
);
|
||||||
if (stackTrimIndex) {
|
if (stackTrimIndex) {
|
||||||
fault.detail["stack"] = fault.detail["stack"].slice(
|
fault.detail["stack"] = fault.detail["stack"].slice(
|
||||||
0,
|
0,
|
||||||
stackTrimIndex.index
|
stackTrimIndex.index,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -106,13 +106,13 @@ if (!cluster.worker) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-3
@@ -43,7 +43,7 @@ function exitWorkerGracefully(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exitWorkerUngracefully(): void {
|
function exitWorkerUngracefully(): void {
|
||||||
extensions.killAll().finally(() => {
|
void extensions.killAll().finally(() => {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -113,13 +113,13 @@ if (!cluster.worker) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-3
@@ -43,7 +43,7 @@ function exitWorkerGracefully(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exitWorkerUngracefully(): void {
|
function exitWorkerUngracefully(): void {
|
||||||
extensions.killAll().finally(() => {
|
void extensions.killAll().finally(() => {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -113,13 +113,13 @@ if (!cluster.worker) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
initPromise.finally(() => {
|
void initPromise.finally(() => {
|
||||||
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
server.stop().then(exitWorkerGracefully).catch(exitWorkerUngracefully);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+12
-12
@@ -106,7 +106,7 @@ const assetsPlugin = {
|
|||||||
setup(build) {
|
setup(build) {
|
||||||
build.onLoad({ filter: /\/build\/assets.ts$/ }, () => {
|
build.onLoad({ filter: /\/build\/assets.ts$/ }, () => {
|
||||||
const lines = Object.entries(ASSETS).map(
|
const lines = Object.entries(ASSETS).map(
|
||||||
([k, v]) => `export const ${k} = ${JSON.stringify(v)};`
|
([k, v]) => `export const ${k} = ${JSON.stringify(v)};`,
|
||||||
);
|
);
|
||||||
return { contents: lines.join("\n") };
|
return { contents: lines.join("\n") };
|
||||||
});
|
});
|
||||||
@@ -194,11 +194,11 @@ async function init(): Promise<void> {
|
|||||||
fsAsync.mkdir(path.join(OUTPUT_DIR, "public")),
|
fsAsync.mkdir(path.join(OUTPUT_DIR, "public")),
|
||||||
fsAsync.writeFile(
|
fsAsync.writeFile(
|
||||||
path.join(OUTPUT_DIR, "package.json"),
|
path.join(OUTPUT_DIR, "package.json"),
|
||||||
JSON.stringify(packageJson, null, 2)
|
JSON.stringify(packageJson, null, 2),
|
||||||
),
|
),
|
||||||
fsAsync.writeFile(
|
fsAsync.writeFile(
|
||||||
path.join(OUTPUT_DIR, "npm-shrinkwrap.json"),
|
path.join(OUTPUT_DIR, "npm-shrinkwrap.json"),
|
||||||
JSON.stringify(npmShrinkwrap, null, 2)
|
JSON.stringify(npmShrinkwrap, null, 2),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -228,9 +228,9 @@ async function copyStatic(): Promise<void> {
|
|||||||
files.map((f) =>
|
files.map((f) =>
|
||||||
fsAsync.copyFile(
|
fsAsync.copyFile(
|
||||||
path.join(INPUT_DIR, f),
|
path.join(INPUT_DIR, f),
|
||||||
path.join(OUTPUT_DIR, filenames[f] || f)
|
path.join(OUTPUT_DIR, filenames[f] || f),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ async function generateCss(): Promise<void> {
|
|||||||
if (v.entryPoint === "ui/css/app.css") {
|
if (v.entryPoint === "ui/css/app.css") {
|
||||||
ASSETS.APP_CSS = path.relative(
|
ASSETS.APP_CSS = path.relative(
|
||||||
path.join(OUTPUT_DIR, "public"),
|
path.join(OUTPUT_DIR, "public"),
|
||||||
path.join(INPUT_DIR, k)
|
path.join(INPUT_DIR, k),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ async function generateFrontendJs(): Promise<void> {
|
|||||||
if (v.entryPoint === "ui/app.ts") {
|
if (v.entryPoint === "ui/app.ts") {
|
||||||
ASSETS.APP_JS = path.relative(
|
ASSETS.APP_JS = path.relative(
|
||||||
path.join(OUTPUT_DIR, "public"),
|
path.join(OUTPUT_DIR, "public"),
|
||||||
path.join(INPUT_DIR, k)
|
path.join(INPUT_DIR, k),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -344,12 +344,12 @@ async function generateIconsSprite(): Promise<void> {
|
|||||||
symbols.push(generateSymbol(id, data));
|
symbols.push(generateSymbol(id, data));
|
||||||
}
|
}
|
||||||
const data = `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join(
|
const data = `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join(
|
||||||
""
|
"",
|
||||||
)}</svg>`;
|
)}</svg>`;
|
||||||
ASSETS.ICONS_SVG = `icons-${assetHash(data)}.svg`;
|
ASSETS.ICONS_SVG = `icons-${assetHash(data)}.svg`;
|
||||||
await fsAsync.writeFile(
|
await fsAsync.writeFile(
|
||||||
path.join(OUTPUT_DIR, "public", ASSETS.ICONS_SVG),
|
path.join(OUTPUT_DIR, "public", ASSETS.ICONS_SVG),
|
||||||
data
|
data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,10 +357,10 @@ init()
|
|||||||
.then(() =>
|
.then(() =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
Promise.all([generateIconsSprite(), copyStatic()]).then(
|
Promise.all([generateIconsSprite(), copyStatic()]).then(
|
||||||
generateFrontendJs
|
generateFrontendJs,
|
||||||
),
|
),
|
||||||
generateCss(),
|
generateCss(),
|
||||||
]).then(generateBackendJs)
|
]).then(generateBackendJs),
|
||||||
)
|
)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
process.stderr.write(err.stack + "\n");
|
process.stderr.write(err.stack + "\n");
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { exec } from "child_process";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
|
const execPromise = promisify(exec);
|
||||||
|
|
||||||
|
async function runEslint(): Promise<string> {
|
||||||
|
const CMD =
|
||||||
|
"eslint 'bin/*.ts' 'lib/**/*.ts' 'ui/**/*.ts' 'test/**/*.ts' 'build/**/*.ts'";
|
||||||
|
const env = {
|
||||||
|
...(process.stdout.isTTY && { FORCE_COLOR: "1" }),
|
||||||
|
...process.env,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { stdout, stderr } = await execPromise(CMD, { env });
|
||||||
|
if (stderr) throw new Error(stderr);
|
||||||
|
return stdout;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.killed || err.signal || err.stderr || err.code !== 1) throw err;
|
||||||
|
return err.stdout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTsc(): Promise<string> {
|
||||||
|
const CMD = "tsc --noEmit";
|
||||||
|
const env = {
|
||||||
|
...(process.stdout.isTTY && { FORCE_COLOR: "1" }),
|
||||||
|
...process.env,
|
||||||
|
};
|
||||||
|
const { stdout, stderr } = await execPromise(CMD, { env });
|
||||||
|
if (stderr) throw new Error(stderr);
|
||||||
|
return stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runPrettier(): Promise<string> {
|
||||||
|
const CMD = "prettier --prose-wrap always --write .";
|
||||||
|
const env = {
|
||||||
|
...(process.stdout.isTTY && { FORCE_COLOR: "1" }),
|
||||||
|
...process.env,
|
||||||
|
};
|
||||||
|
const { stdout, stderr } = await execPromise(CMD, { env });
|
||||||
|
if (stderr) throw new Error(stderr);
|
||||||
|
return stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runAll(): Promise<void> {
|
||||||
|
const prom1 = runPrettier();
|
||||||
|
const prom2 = runEslint();
|
||||||
|
const prom3 = runTsc();
|
||||||
|
|
||||||
|
console.log(await prom1);
|
||||||
|
console.log(await prom2);
|
||||||
|
console.log(await prom3);
|
||||||
|
}
|
||||||
|
|
||||||
|
runAll().catch(console.error);
|
||||||
+1
-1
@@ -16,7 +16,7 @@ function latlong(args, callback) {
|
|||||||
.get("http://api.open-notify.org/iss-now.json", (res) => {
|
.get("http://api.open-notify.org/iss-now.json", (res) => {
|
||||||
if (res.statusCode !== 200)
|
if (res.statusCode !== 200)
|
||||||
return callback(
|
return callback(
|
||||||
new Error(`Request failed (status code: ${res.statusCode})`)
|
new Error(`Request failed (status code: ${res.statusCode})`),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rawData = "";
|
let rawData = "";
|
||||||
|
|||||||
+16
-16
@@ -57,7 +57,7 @@ import { ResourceLockedError } from "./common/errors";
|
|||||||
|
|
||||||
export async function connectionRequest(
|
export async function connectionRequest(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
device?: Record<string, { value?: [boolean | number | string, string] }>
|
device?: Record<string, { value?: [boolean | number | string, string] }>,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (!device) {
|
if (!device) {
|
||||||
const res = await collections.devices.findOne({ _id: deviceId });
|
const res = await collections.devices.findOne({ _id: deviceId });
|
||||||
@@ -132,25 +132,25 @@ export async function connectionRequest(
|
|||||||
"cwmp.udpConnectionRequestPort",
|
"cwmp.udpConnectionRequestPort",
|
||||||
{},
|
{},
|
||||||
now,
|
now,
|
||||||
evalCallback
|
evalCallback,
|
||||||
);
|
);
|
||||||
const CONNECTION_REQUEST_TIMEOUT = +getConfig(
|
const CONNECTION_REQUEST_TIMEOUT = +getConfig(
|
||||||
snapshot,
|
snapshot,
|
||||||
"cwmp.connectionRequestTimeout",
|
"cwmp.connectionRequestTimeout",
|
||||||
{},
|
{},
|
||||||
now,
|
now,
|
||||||
evalCallback
|
evalCallback,
|
||||||
);
|
);
|
||||||
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = !!getConfig(
|
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = !!getConfig(
|
||||||
snapshot,
|
snapshot,
|
||||||
"cwmp.connectionRequestAllowBasicAuth",
|
"cwmp.connectionRequestAllowBasicAuth",
|
||||||
{},
|
{},
|
||||||
now,
|
now,
|
||||||
evalCallback
|
evalCallback,
|
||||||
);
|
);
|
||||||
let authExp: Expression = getConfigExpression(
|
let authExp: Expression = getConfigExpression(
|
||||||
snapshot,
|
snapshot,
|
||||||
"cwmp.connectionRequestAuth"
|
"cwmp.connectionRequestAuth",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (authExp === undefined) {
|
if (authExp === undefined) {
|
||||||
@@ -176,10 +176,10 @@ export async function connectionRequest(
|
|||||||
authExp,
|
authExp,
|
||||||
UDP_CONNECTION_REQUEST_PORT,
|
UDP_CONNECTION_REQUEST_PORT,
|
||||||
debug,
|
debug,
|
||||||
deviceId
|
deviceId,
|
||||||
).then(
|
).then(
|
||||||
() => true,
|
() => true,
|
||||||
() => false
|
() => false,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore invalid address
|
// Ignore invalid address
|
||||||
@@ -192,7 +192,7 @@ export async function connectionRequest(
|
|||||||
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
||||||
CONNECTION_REQUEST_TIMEOUT,
|
CONNECTION_REQUEST_TIMEOUT,
|
||||||
debug,
|
debug,
|
||||||
deviceId
|
deviceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (await udpProm) return "";
|
if (await udpProm) return "";
|
||||||
@@ -203,12 +203,12 @@ export async function connectionRequest(
|
|||||||
export async function awaitSessionStart(
|
export async function awaitSessionStart(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
lastInform: number,
|
lastInform: number,
|
||||||
timeout: number
|
timeout: number,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const device = await collections.devices.findOne(
|
const device = await collections.devices.findOne(
|
||||||
{ _id: deviceId },
|
{ _id: deviceId },
|
||||||
{ projection: { _lastInform: 1 } }
|
{ projection: { _lastInform: 1 } },
|
||||||
);
|
);
|
||||||
const li = (device["_lastInform"] as Date).getTime();
|
const li = (device["_lastInform"] as Date).getTime();
|
||||||
if (li > lastInform) return true;
|
if (li > lastInform) return true;
|
||||||
@@ -222,7 +222,7 @@ export async function awaitSessionStart(
|
|||||||
|
|
||||||
export async function awaitSessionEnd(
|
export async function awaitSessionEnd(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
timeout: number
|
timeout: number,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const token = await getToken(`cwmp_session_${deviceId}`);
|
const token = await getToken(`cwmp_session_${deviceId}`);
|
||||||
@@ -317,8 +317,8 @@ function sanitizeTask(task): void {
|
|||||||
!task.provisions.every((arr) =>
|
!task.provisions.every((arr) =>
|
||||||
arr.every(
|
arr.every(
|
||||||
(s) =>
|
(s) =>
|
||||||
s == null || ["boolean", "number", "string"].includes(typeof s)
|
s == null || ["boolean", "number", "string"].includes(typeof s),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
throw new Error("Invalid 'provisions' property");
|
throw new Error("Invalid 'provisions' property");
|
||||||
@@ -395,7 +395,7 @@ export async function deleteFault(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function deleteResource(
|
export async function deleteResource(
|
||||||
resource: string,
|
resource: string,
|
||||||
id: string
|
id: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (resource === "devices") {
|
if (resource === "devices") {
|
||||||
await deleteDevice(id);
|
await deleteDevice(id);
|
||||||
@@ -434,7 +434,7 @@ export async function deleteResource(
|
|||||||
export async function putResource(
|
export async function putResource(
|
||||||
resource: string,
|
resource: string,
|
||||||
id: string,
|
id: string,
|
||||||
data: any
|
data: any,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (resource === "presets") {
|
if (resource === "presets") {
|
||||||
await putPreset(id, data);
|
await putPreset(id, data);
|
||||||
@@ -467,7 +467,7 @@ export async function putResource(
|
|||||||
export function authLocal(
|
export function authLocal(
|
||||||
snapshot: string,
|
snapshot: string,
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const users = getUsers(snapshot);
|
const users = getUsers(snapshot);
|
||||||
|
|||||||
+5
-5
@@ -63,7 +63,7 @@ export function parseAuthorizationHeader(authHeader: string): {
|
|||||||
// Inspired by https://github.com/jshttp/basic-auth
|
// Inspired by https://github.com/jshttp/basic-auth
|
||||||
const USER_PASS_REGEX = /^([^:]*):(.*)$/;
|
const USER_PASS_REGEX = /^([^:]*):(.*)$/;
|
||||||
const creds = USER_PASS_REGEX.exec(
|
const creds = USER_PASS_REGEX.exec(
|
||||||
Buffer.from(authHeader.slice(method.length + 1), "base64").toString()
|
Buffer.from(authHeader.slice(method.length + 1), "base64").toString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!creds) throw new Error("Unable to parse auth header");
|
if (!creds) throw new Error("Unable to parse auth header");
|
||||||
@@ -77,7 +77,7 @@ export function parseAuthorizationHeader(authHeader: string): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseWwwAuthenticateHeader(
|
export function parseWwwAuthenticateHeader(
|
||||||
authHeader: string
|
authHeader: string,
|
||||||
): Record<string, string> {
|
): Record<string, string> {
|
||||||
authHeader = authHeader.trim();
|
authHeader = authHeader.trim();
|
||||||
const method = authHeader.split(" ", 1)[0];
|
const method = authHeader.split(" ", 1)[0];
|
||||||
@@ -100,7 +100,7 @@ export function digest(
|
|||||||
qop?: string | Buffer,
|
qop?: string | Buffer,
|
||||||
body?: string | Buffer,
|
body?: string | Buffer,
|
||||||
cnonce?: string | Buffer,
|
cnonce?: string | Buffer,
|
||||||
nc?: string | Buffer
|
nc?: string | Buffer,
|
||||||
): string {
|
): string {
|
||||||
const ha1 = createHash("md5");
|
const ha1 = createHash("md5");
|
||||||
ha1.update(username).update(":").update(realm).update(":").update(password);
|
ha1.update(username).update(":").update(realm).update(":").update(password);
|
||||||
@@ -141,7 +141,7 @@ export function solveDigest(
|
|||||||
uri: string | Buffer,
|
uri: string | Buffer,
|
||||||
httpMethod: string | Buffer,
|
httpMethod: string | Buffer,
|
||||||
body: string | Buffer,
|
body: string | Buffer,
|
||||||
authHeader: Record<string, string>
|
authHeader: Record<string, string>,
|
||||||
): string {
|
): string {
|
||||||
const cnonce = randomBytes(8).toString("hex");
|
const cnonce = randomBytes(8).toString("hex");
|
||||||
const nc = "00000001";
|
const nc = "00000001";
|
||||||
@@ -163,7 +163,7 @@ export function solveDigest(
|
|||||||
qop,
|
qop,
|
||||||
body,
|
body,
|
||||||
cnonce,
|
cnonce,
|
||||||
nc
|
nc,
|
||||||
);
|
);
|
||||||
|
|
||||||
let authString = `Digest username="${username}"`;
|
let authString = `Digest username="${username}"`;
|
||||||
|
|||||||
+3
-3
@@ -35,16 +35,16 @@ export async function del(key: string): Promise<void> {
|
|||||||
export async function set(
|
export async function set(
|
||||||
key: string,
|
key: string,
|
||||||
value: string,
|
value: string,
|
||||||
ttl: number = MAX_CACHE_TTL
|
ttl: number = MAX_CACHE_TTL,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
const expire = new Date(
|
const expire = new Date(
|
||||||
timestamp.getTime() + CLOCK_SKEW_TOLERANCE + ttl * 1000
|
timestamp.getTime() + CLOCK_SKEW_TOLERANCE + ttl * 1000,
|
||||||
);
|
);
|
||||||
await collections.cache.replaceOne(
|
await collections.cache.replaceOne(
|
||||||
{ _id: key },
|
{ _id: key },
|
||||||
{ value, expire, timestamp },
|
{ value, expire, timestamp },
|
||||||
{ upsert: true }
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ function restartWorker(worker, code, signal): void {
|
|||||||
export function start(
|
export function start(
|
||||||
workerCount: number,
|
workerCount: number,
|
||||||
servicePort: number,
|
servicePort: number,
|
||||||
serviceAddress: string
|
serviceAddress: string,
|
||||||
): void {
|
): void {
|
||||||
cluster.on("listening", (worker, address) => {
|
cluster.on("listening", (worker, address) => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default class Authorizer {
|
|||||||
|
|
||||||
public getValidator(
|
public getValidator(
|
||||||
resourceType: string,
|
resourceType: string,
|
||||||
resource: unknown
|
resource: unknown,
|
||||||
): (mutationType: string, mutation?: any, args?: any) => boolean {
|
): (mutationType: string, mutation?: any, args?: any) => boolean {
|
||||||
if (this.validatorCache.has(resource))
|
if (this.validatorCache.has(resource))
|
||||||
return this.validatorCache.get(resource);
|
return this.validatorCache.get(resource);
|
||||||
@@ -99,7 +99,7 @@ export default class Authorizer {
|
|||||||
const validator = (
|
const validator = (
|
||||||
mutationType: string,
|
mutationType: string,
|
||||||
mutation: any,
|
mutation: any,
|
||||||
any: any
|
any: any,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (!validators.length) return false;
|
if (!validators.length) return false;
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export default class Authorizer {
|
|||||||
const res = evaluate(
|
const res = evaluate(
|
||||||
validators.length > 1 ? ["OR", validators] : validators[0],
|
validators.length > 1 ? ["OR", validators] : validators[0],
|
||||||
valueFunction,
|
valueFunction,
|
||||||
Date.now()
|
Date.now(),
|
||||||
);
|
);
|
||||||
return !Array.isArray(res) && !!res;
|
return !Array.isArray(res) && !!res;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default function debounce<T>(
|
export default function debounce<T>(
|
||||||
func: (args: T[]) => void,
|
func: (args: T[]) => void,
|
||||||
timeout: number
|
timeout: number,
|
||||||
): (arg: T) => void {
|
): (arg: T) => void {
|
||||||
let timer: ReturnType<typeof setTimeout>;
|
let timer: ReturnType<typeof setTimeout>;
|
||||||
let args: T[] = [];
|
let args: T[] = [];
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class Indeterminates {
|
|||||||
|
|
||||||
public static multiply(
|
public static multiply(
|
||||||
indeterminates1: Indeterminates,
|
indeterminates1: Indeterminates,
|
||||||
indeterminates2: Indeterminates
|
indeterminates2: Indeterminates,
|
||||||
): Indeterminates {
|
): Indeterminates {
|
||||||
const res = new Indeterminates();
|
const res = new Indeterminates();
|
||||||
res.sortedKeys = indeterminates1.sortedKeys.slice();
|
res.sortedKeys = indeterminates1.sortedKeys.slice();
|
||||||
@@ -108,7 +108,7 @@ class Polynomial {
|
|||||||
const ts = terms
|
const ts = terms
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a: Term, b: Term) =>
|
.sort((a: Term, b: Term) =>
|
||||||
Indeterminates.compare(a.indeterminates, b.indeterminates)
|
Indeterminates.compare(a.indeterminates, b.indeterminates),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 1; i < ts.length; ++i) {
|
for (let i = 1; i < ts.length; ++i) {
|
||||||
@@ -117,12 +117,12 @@ class Polynomial {
|
|||||||
if (Indeterminates.compare(t1.indeterminates, t2.indeterminates) === 0) {
|
if (Indeterminates.compare(t1.indeterminates, t2.indeterminates) === 0) {
|
||||||
const numerator = BI.add(
|
const numerator = BI.add(
|
||||||
BI.mul(t1.coefficientNumerator, t2.coefficientDenominator),
|
BI.mul(t1.coefficientNumerator, t2.coefficientDenominator),
|
||||||
BI.mul(t2.coefficientNumerator, t1.coefficientDenominator)
|
BI.mul(t2.coefficientNumerator, t1.coefficientDenominator),
|
||||||
);
|
);
|
||||||
|
|
||||||
const denominator = BI.mul(
|
const denominator = BI.mul(
|
||||||
t1.coefficientDenominator,
|
t1.coefficientDenominator,
|
||||||
t2.coefficientDenominator
|
t2.coefficientDenominator,
|
||||||
);
|
);
|
||||||
|
|
||||||
const gcd = findGcd(numerator, denominator);
|
const gcd = findGcd(numerator, denominator);
|
||||||
@@ -163,7 +163,7 @@ class Polynomial {
|
|||||||
denominator = BI.exp(TWO, BI.BigInt(frac.length));
|
denominator = BI.exp(TWO, BI.BigInt(frac.length));
|
||||||
numerator = BI.add(
|
numerator = BI.add(
|
||||||
BI.mul(numerator, denominator),
|
BI.mul(numerator, denominator),
|
||||||
BI.BigInt("0b" + frac)
|
BI.BigInt("0b" + frac),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ class Polynomial {
|
|||||||
|
|
||||||
public add(rhs: Polynomial): Polynomial {
|
public add(rhs: Polynomial): Polynomial {
|
||||||
return new Polynomial(
|
return new Polynomial(
|
||||||
Polynomial.simplifyTerms(this.terms.concat(rhs.terms))
|
Polynomial.simplifyTerms(this.terms.concat(rhs.terms)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,18 +219,18 @@ class Polynomial {
|
|||||||
for (const t2 of rhs.terms) {
|
for (const t2 of rhs.terms) {
|
||||||
const numerator = BI.mul(
|
const numerator = BI.mul(
|
||||||
t1.coefficientNumerator,
|
t1.coefficientNumerator,
|
||||||
t2.coefficientNumerator
|
t2.coefficientNumerator,
|
||||||
);
|
);
|
||||||
const denominator = BI.mul(
|
const denominator = BI.mul(
|
||||||
t1.coefficientDenominator,
|
t1.coefficientDenominator,
|
||||||
t2.coefficientDenominator
|
t2.coefficientDenominator,
|
||||||
);
|
);
|
||||||
const gcd = findGcd(numerator, denominator);
|
const gcd = findGcd(numerator, denominator);
|
||||||
|
|
||||||
terms.push({
|
terms.push({
|
||||||
indeterminates: Indeterminates.multiply(
|
indeterminates: Indeterminates.multiply(
|
||||||
t1.indeterminates,
|
t1.indeterminates,
|
||||||
t2.indeterminates
|
t2.indeterminates,
|
||||||
),
|
),
|
||||||
coefficientNumerator: BI.div(numerator, gcd),
|
coefficientNumerator: BI.div(numerator, gcd),
|
||||||
coefficientDenominator: BI.div(denominator, gcd),
|
coefficientDenominator: BI.div(denominator, gcd),
|
||||||
@@ -339,7 +339,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
const e = (r[1] as Expression[]).slice();
|
const e = (r[1] as Expression[]).slice();
|
||||||
e[i] = t;
|
e[i] = t;
|
||||||
return [and(w, r[0]), e] as [Expression, Expression];
|
return [and(w, r[0]), e] as [Expression, Expression];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
if (!Array.isArray(w) && w) break;
|
if (!Array.isArray(w) && w) break;
|
||||||
}
|
}
|
||||||
@@ -373,7 +373,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
}
|
}
|
||||||
return args.reduce(
|
return args.reduce(
|
||||||
(previousValue, currentValue) => previousValue.add(currentValue),
|
(previousValue, currentValue) => previousValue.add(currentValue),
|
||||||
ADDITIVE_IDENTITY
|
ADDITIVE_IDENTITY,
|
||||||
) as unknown as Expression;
|
) as unknown as Expression;
|
||||||
} else if (op === "*") {
|
} else if (op === "*") {
|
||||||
const args: Polynomial[] = [];
|
const args: Polynomial[] = [];
|
||||||
@@ -384,7 +384,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
}
|
}
|
||||||
return args.reduce(
|
return args.reduce(
|
||||||
(previousValue, currentValue) => previousValue.multiply(currentValue),
|
(previousValue, currentValue) => previousValue.multiply(currentValue),
|
||||||
MULTIPLICATIVE_IDENTITY
|
MULTIPLICATIVE_IDENTITY,
|
||||||
) as unknown as Expression;
|
) as unknown as Expression;
|
||||||
} else if (op === "-") {
|
} else if (op === "-") {
|
||||||
const args: Polynomial[] = [];
|
const args: Polynomial[] = [];
|
||||||
@@ -394,7 +394,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
args.push(p);
|
args.push(p);
|
||||||
}
|
}
|
||||||
return args.reduce((previousValue, currentValue) =>
|
return args.reduce((previousValue, currentValue) =>
|
||||||
previousValue.subtract(currentValue)
|
previousValue.subtract(currentValue),
|
||||||
) as unknown as Expression;
|
) as unknown as Expression;
|
||||||
} else if (op === "/") {
|
} else if (op === "/") {
|
||||||
const args: Polynomial[] = [];
|
const args: Polynomial[] = [];
|
||||||
@@ -404,7 +404,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
args.push(p);
|
args.push(p);
|
||||||
}
|
}
|
||||||
return args.reduce((previousValue, currentValue) =>
|
return args.reduce((previousValue, currentValue) =>
|
||||||
previousValue.divide(currentValue)
|
previousValue.divide(currentValue),
|
||||||
) as unknown as Expression;
|
) as unknown as Expression;
|
||||||
} else if (["=", "<>", ">", ">=", "<", "<="].includes(op)) {
|
} else if (["=", "<>", ">", ">=", "<", "<="].includes(op)) {
|
||||||
if (exp[1] == null || exp[2] == null) return null;
|
if (exp[1] == null || exp[2] == null) return null;
|
||||||
@@ -471,7 +471,7 @@ function normalizeCallback(exp: Expression): Expression {
|
|||||||
|
|
||||||
// Restore polynomial expressions
|
// Restore polynomial expressions
|
||||||
exp = exp.map((e) =>
|
exp = exp.map((e) =>
|
||||||
e instanceof Polynomial ? JSON.parse(e.toString()) : e
|
e instanceof Polynomial ? JSON.parse(e.toString()) : e,
|
||||||
);
|
);
|
||||||
|
|
||||||
exp = evaluateCallback(exp);
|
exp = evaluateCallback(exp);
|
||||||
@@ -485,7 +485,7 @@ export default function normalize(expr: Expression): Expression {
|
|||||||
expr = JSON.parse(expr.toString());
|
expr = JSON.parse(expr.toString());
|
||||||
} else if (Array.isArray(expr) && expr[0] === "CASE") {
|
} else if (Array.isArray(expr) && expr[0] === "CASE") {
|
||||||
expr = expr.map((e) =>
|
expr = expr.map((e) =>
|
||||||
e instanceof Polynomial ? JSON.parse(e.toString()) : e
|
e instanceof Polynomial ? JSON.parse(e.toString()) : e,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function map<T>(exp: Expression, callback: (e: Expression) => T): T {
|
|||||||
|
|
||||||
export async function mapAsync(
|
export async function mapAsync(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
callback: (e: Expression) => Promise<Expression>
|
callback: (e: Expression) => Promise<Expression>,
|
||||||
): Promise<Expression> {
|
): Promise<Expression> {
|
||||||
if (!Array.isArray(exp)) return callback(exp);
|
if (!Array.isArray(exp)) return callback(exp);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export async function mapAsync(
|
|||||||
|
|
||||||
function binaryLeft(
|
function binaryLeft(
|
||||||
operatorsParser: parsimmon.Parser<string>,
|
operatorsParser: parsimmon.Parser<string>,
|
||||||
nextParser: parsimmon.Parser<Expression>
|
nextParser: parsimmon.Parser<Expression>,
|
||||||
): parsimmon.Parser<Expression> {
|
): parsimmon.Parser<Expression> {
|
||||||
return parsimmon.seqMap(
|
return parsimmon.seqMap(
|
||||||
nextParser,
|
nextParser,
|
||||||
@@ -85,7 +85,7 @@ function binaryLeft(
|
|||||||
const [op, another] = ch;
|
const [op, another] = ch;
|
||||||
if (Array.isArray(acc) && op === acc[0]) return acc.concat([another]);
|
if (Array.isArray(acc) && op === acc[0]) return acc.concat([another]);
|
||||||
return [op, acc, another];
|
return [op, acc, another];
|
||||||
}, first)
|
}, first),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
parsimmon.string("<="),
|
parsimmon.string("<="),
|
||||||
parsimmon.string("="),
|
parsimmon.string("="),
|
||||||
parsimmon.string(">"),
|
parsimmon.string(">"),
|
||||||
parsimmon.string("<")
|
parsimmon.string("<"),
|
||||||
)
|
)
|
||||||
.skip(parsimmon.optWhitespace);
|
.skip(parsimmon.optWhitespace);
|
||||||
},
|
},
|
||||||
@@ -109,7 +109,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
parsimmon
|
parsimmon
|
||||||
.regexp(/not\s+like/i)
|
.regexp(/not\s+like/i)
|
||||||
.result("NOT LIKE")
|
.result("NOT LIKE")
|
||||||
.desc("NOT LIKE")
|
.desc("NOT LIKE"),
|
||||||
)
|
)
|
||||||
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
||||||
.skip(parsimmon.optWhitespace);
|
.skip(parsimmon.optWhitespace);
|
||||||
@@ -124,7 +124,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
parsimmon
|
parsimmon
|
||||||
.regexp(/is\s+not\s+null/i)
|
.regexp(/is\s+not\s+null/i)
|
||||||
.result("IS NOT NULL")
|
.result("IS NOT NULL")
|
||||||
.desc("IS NOT NULL")
|
.desc("IS NOT NULL"),
|
||||||
)
|
)
|
||||||
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
||||||
.skip(parsimmon.optWhitespace);
|
.skip(parsimmon.optWhitespace);
|
||||||
@@ -159,8 +159,8 @@ const lang = parsimmon.createLanguage({
|
|||||||
parsimmon.regexp(/[a-zA-Z0-9_.*-]+/),
|
parsimmon.regexp(/[a-zA-Z0-9_.*-]+/),
|
||||||
r.Expression.wrap(
|
r.Expression.wrap(
|
||||||
parsimmon.string("{").skip(parsimmon.optWhitespace),
|
parsimmon.string("{").skip(parsimmon.optWhitespace),
|
||||||
parsimmon.string("}")
|
parsimmon.string("}"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.atLeast(1)
|
.atLeast(1)
|
||||||
.map((x) => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]])
|
.map((x) => ["PARAM", x.length > 1 ? ["||"].concat(x) : x[0]])
|
||||||
@@ -194,7 +194,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
return parsimmon
|
return parsimmon
|
||||||
.alt(
|
.alt(
|
||||||
parsimmon.regexp(/true/i).result(true).desc("TRUE"),
|
parsimmon.regexp(/true/i).result(true).desc("TRUE"),
|
||||||
parsimmon.regexp(/false/i).result(false).desc("FALSE")
|
parsimmon.regexp(/false/i).result(false).desc("FALSE"),
|
||||||
)
|
)
|
||||||
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
||||||
.skip(parsimmon.optWhitespace);
|
.skip(parsimmon.optWhitespace);
|
||||||
@@ -215,9 +215,9 @@ const lang = parsimmon.createLanguage({
|
|||||||
.desc("function"),
|
.desc("function"),
|
||||||
r.ExpressionList.wrap(
|
r.ExpressionList.wrap(
|
||||||
parsimmon.string("(").skip(parsimmon.optWhitespace),
|
parsimmon.string("(").skip(parsimmon.optWhitespace),
|
||||||
parsimmon.string(")").skip(parsimmon.optWhitespace)
|
parsimmon.string(")").skip(parsimmon.optWhitespace),
|
||||||
),
|
),
|
||||||
(f, args) => ["FUNC", f.toUpperCase()].concat(args)
|
(f, args) => ["FUNC", f.toUpperCase()].concat(args),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
WhenPair: function (r) {
|
WhenPair: function (r) {
|
||||||
@@ -233,7 +233,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
||||||
.skip(parsimmon.optWhitespace)
|
.skip(parsimmon.optWhitespace)
|
||||||
.desc("THEN")
|
.desc("THEN")
|
||||||
.then(r.Expression)
|
.then(r.Expression),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
CaseStatement: function (r) {
|
CaseStatement: function (r) {
|
||||||
@@ -256,10 +256,10 @@ const lang = parsimmon.createLanguage({
|
|||||||
.skip(
|
.skip(
|
||||||
parsimmon
|
parsimmon
|
||||||
.regex(/end/i)
|
.regex(/end/i)
|
||||||
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/))
|
.notFollowedBy(parsimmon.regexp(/[a-zA-Z0-9_]/)),
|
||||||
)
|
)
|
||||||
.skip(parsimmon.optWhitespace),
|
.skip(parsimmon.optWhitespace),
|
||||||
(...arr) => arr.flat(2)
|
(...arr) => arr.flat(2),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Value: function (r) {
|
Value: function (r) {
|
||||||
@@ -270,7 +270,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
r.StringValueSql,
|
r.StringValueSql,
|
||||||
r.StringValueJs,
|
r.StringValueJs,
|
||||||
r.FuncValue,
|
r.FuncValue,
|
||||||
r.CaseStatement
|
r.CaseStatement,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ValueExpression: function (r) {
|
ValueExpression: function (r) {
|
||||||
@@ -285,7 +285,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
.alt(
|
.alt(
|
||||||
parsimmon.string("*"),
|
parsimmon.string("*"),
|
||||||
parsimmon.string("/"),
|
parsimmon.string("/"),
|
||||||
parsimmon.string("%")
|
parsimmon.string("%"),
|
||||||
)
|
)
|
||||||
.skip(parsimmon.optWhitespace),
|
.skip(parsimmon.optWhitespace),
|
||||||
parsimmon.alt(
|
parsimmon.alt(
|
||||||
@@ -293,11 +293,11 @@ const lang = parsimmon.createLanguage({
|
|||||||
r.Parameter,
|
r.Parameter,
|
||||||
r.Expression.wrap(
|
r.Expression.wrap(
|
||||||
parsimmon.string("(").skip(parsimmon.optWhitespace),
|
parsimmon.string("(").skip(parsimmon.optWhitespace),
|
||||||
parsimmon.string(")").skip(parsimmon.optWhitespace)
|
parsimmon.string(")").skip(parsimmon.optWhitespace),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Comparison: function (r) {
|
Comparison: function (r) {
|
||||||
@@ -307,7 +307,7 @@ const lang = parsimmon.createLanguage({
|
|||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
r.ComparisonOperator,
|
r.ComparisonOperator,
|
||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
(p, o, v) => [o, p, v]
|
(p, o, v) => [o, p, v],
|
||||||
),
|
),
|
||||||
parsimmon.seqMap(
|
parsimmon.seqMap(
|
||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
@@ -317,28 +317,28 @@ const lang = parsimmon.createLanguage({
|
|||||||
.regexp(/escape/i)
|
.regexp(/escape/i)
|
||||||
.result("ESCAPE")
|
.result("ESCAPE")
|
||||||
.skip(parsimmon.whitespace)
|
.skip(parsimmon.whitespace)
|
||||||
.desc("ESCAPE")
|
.desc("ESCAPE"),
|
||||||
),
|
),
|
||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
(a, b, c, d) => [b, a, c, d]
|
(a, b, c, d) => [b, a, c, d],
|
||||||
),
|
),
|
||||||
parsimmon.seqMap(
|
parsimmon.seqMap(
|
||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
r.LikeOperator,
|
r.LikeOperator,
|
||||||
r.ValueExpression,
|
r.ValueExpression,
|
||||||
(a, b, c) => [b, a, c]
|
(a, b, c) => [b, a, c],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ExpressionList: function (r) {
|
ExpressionList: function (r) {
|
||||||
return r.Expression.sepBy(
|
return r.Expression.sepBy(
|
||||||
parsimmon.string(",").skip(parsimmon.optWhitespace)
|
parsimmon.string(",").skip(parsimmon.optWhitespace),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Expression: function (r) {
|
Expression: function (r) {
|
||||||
function unary(
|
function unary(
|
||||||
operatorsParser: parsimmon.Parser<string>,
|
operatorsParser: parsimmon.Parser<string>,
|
||||||
nextParser: parsimmon.Parser<Expression>
|
nextParser: parsimmon.Parser<Expression>,
|
||||||
): parsimmon.Parser<Expression> {
|
): parsimmon.Parser<Expression> {
|
||||||
return parsimmon.seq(operatorsParser, nextParser).or(nextParser);
|
return parsimmon.seq(operatorsParser, nextParser).or(nextParser);
|
||||||
}
|
}
|
||||||
@@ -347,8 +347,8 @@ const lang = parsimmon.createLanguage({
|
|||||||
r.OrOperator,
|
r.OrOperator,
|
||||||
binaryLeft(
|
binaryLeft(
|
||||||
r.AndOperator,
|
r.AndOperator,
|
||||||
unary(r.NotOperator, r.Comparison.or(r.ValueExpression))
|
unary(r.NotOperator, r.Comparison.or(r.ValueExpression)),
|
||||||
)
|
),
|
||||||
).trim(parsimmon.optWhitespace);
|
).trim(parsimmon.optWhitespace);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -400,7 +400,7 @@ export function stringify(exp: Expression, level = 0): string {
|
|||||||
`${exp[1]}(${exp
|
`${exp[1]}(${exp
|
||||||
.slice(2)
|
.slice(2)
|
||||||
.map((e) => stringify(e))
|
.map((e) => stringify(e))
|
||||||
.join(", ")})`
|
.join(", ")})`,
|
||||||
);
|
);
|
||||||
} else if (op === "PARAM") {
|
} else if (op === "PARAM") {
|
||||||
if (typeof exp[1] === "string") {
|
if (typeof exp[1] === "string") {
|
||||||
@@ -413,7 +413,7 @@ export function stringify(exp: Expression, level = 0): string {
|
|||||||
if (typeof p === "string") return p;
|
if (typeof p === "string") return p;
|
||||||
else return `{${stringify(p)}}`;
|
else return `{${stringify(p)}}`;
|
||||||
})
|
})
|
||||||
.join("")
|
.join(""),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return wrap(`{${stringify(exp[1])}}`);
|
return wrap(`{${stringify(exp[1])}}`);
|
||||||
@@ -425,15 +425,15 @@ export function stringify(exp: Expression, level = 0): string {
|
|||||||
return wrap(
|
return wrap(
|
||||||
`${stringify(exp[1], opLevels[op])} ${op} ${stringify(
|
`${stringify(exp[1], opLevels[op])} ${op} ${stringify(
|
||||||
exp[2],
|
exp[2],
|
||||||
opLevels[op]
|
opLevels[op],
|
||||||
)} ESCAPE ${stringify(exp[3], opLevels[op])}`
|
)} ESCAPE ${stringify(exp[3], opLevels[op])}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return wrap(
|
return wrap(
|
||||||
`${stringify(exp[1], opLevels[op])} ${op} ${stringify(
|
`${stringify(exp[1], opLevels[op])} ${op} ${stringify(
|
||||||
exp[2],
|
exp[2],
|
||||||
opLevels[op]
|
opLevels[op],
|
||||||
)}`
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (op === "CASE") {
|
} else if (op === "CASE") {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type Minterm = number[];
|
|||||||
|
|
||||||
export abstract class SynthContextBase<
|
export abstract class SynthContextBase<
|
||||||
T extends { toString: () => string } = unknown,
|
T extends { toString: () => string } = unknown,
|
||||||
U = unknown
|
U = unknown,
|
||||||
> {
|
> {
|
||||||
public variables = new Map<string, number>();
|
public variables = new Map<string, number>();
|
||||||
protected clauses = new Map<number, U>();
|
protected clauses = new Map<number, U>();
|
||||||
@@ -88,7 +88,7 @@ export abstract class Clause {
|
|||||||
isNullable(c: Clause.IsNull): boolean {
|
isNullable(c: Clause.IsNull): boolean {
|
||||||
if (!this._isNullable) {
|
if (!this._isNullable) {
|
||||||
this._isNullable = new Set(
|
this._isNullable = new Set(
|
||||||
[...this.getNullables()].map((n) => n.toString())
|
[...this.getNullables()].map((n) => n.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this._isNullable.has(c.toString());
|
return this._isNullable.has(c.toString());
|
||||||
@@ -243,14 +243,14 @@ export namespace Clause {
|
|||||||
const w = this.clauses[i].true(context);
|
const w = this.clauses[i].true(context);
|
||||||
const t = this.clauses[i + 1].true(context);
|
const t = this.clauses[i + 1].true(context);
|
||||||
minterms.push(
|
minterms.push(
|
||||||
...complement([...cumulative, ...complement(w), ...complement(t)])
|
...complement([...cumulative, ...complement(w), ...complement(t)]),
|
||||||
);
|
);
|
||||||
if (i < this.clauses.length - 2) {
|
if (i < this.clauses.length - 2) {
|
||||||
cumulative.push(
|
cumulative.push(
|
||||||
...complement([
|
...complement([
|
||||||
...this.clauses[i].false(context),
|
...this.clauses[i].false(context),
|
||||||
...this.clauses[i].null(context),
|
...this.clauses[i].null(context),
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,14 +263,14 @@ export namespace Clause {
|
|||||||
const w = this.clauses[i].true(context);
|
const w = this.clauses[i].true(context);
|
||||||
const t = this.clauses[i + 1].false(context);
|
const t = this.clauses[i + 1].false(context);
|
||||||
minterms.push(
|
minterms.push(
|
||||||
...complement([...cumulative, ...complement(w), ...complement(t)])
|
...complement([...cumulative, ...complement(w), ...complement(t)]),
|
||||||
);
|
);
|
||||||
if (i < this.clauses.length - 2) {
|
if (i < this.clauses.length - 2) {
|
||||||
cumulative.push(
|
cumulative.push(
|
||||||
...complement([
|
...complement([
|
||||||
...this.clauses[i].false(context),
|
...this.clauses[i].false(context),
|
||||||
...this.clauses[i].null(context),
|
...this.clauses[i].null(context),
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,13 +283,13 @@ export namespace Clause {
|
|||||||
const w = this.clauses[i].true(context);
|
const w = this.clauses[i].true(context);
|
||||||
const t = this.clauses[i + 1].null(context);
|
const t = this.clauses[i + 1].null(context);
|
||||||
minterms.push(
|
minterms.push(
|
||||||
...complement([...cumulative, ...complement(w), ...complement(t)])
|
...complement([...cumulative, ...complement(w), ...complement(t)]),
|
||||||
);
|
);
|
||||||
cumulative.push(
|
cumulative.push(
|
||||||
...complement([
|
...complement([
|
||||||
...this.clauses[i].false(context),
|
...this.clauses[i].false(context),
|
||||||
...this.clauses[i].null(context),
|
...this.clauses[i].null(context),
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
minterms.push(...complement([...cumulative]));
|
minterms.push(...complement([...cumulative]));
|
||||||
@@ -313,7 +313,7 @@ export namespace Clause {
|
|||||||
minterms.push(...cases.pop().when);
|
minterms.push(...cases.pop().when);
|
||||||
minterms = context.minimize(
|
minterms = context.minimize(
|
||||||
minterms,
|
minterms,
|
||||||
cases.flatMap((c) => c.when)
|
cases.flatMap((c) => c.when),
|
||||||
);
|
);
|
||||||
if (!minterms.length) continue;
|
if (!minterms.length) continue;
|
||||||
cases.push({ when: minterms, then });
|
cases.push({ when: minterms, then });
|
||||||
@@ -395,7 +395,7 @@ export namespace Clause {
|
|||||||
constructor(
|
constructor(
|
||||||
public lhs: Clause,
|
public lhs: Clause,
|
||||||
public op: ">" | "<" | "=",
|
public op: ">" | "<" | "=",
|
||||||
public rhs: boolean | number | string
|
public rhs: boolean | number | string,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -425,7 +425,11 @@ export namespace Clause {
|
|||||||
public readonly pattern: string[];
|
public readonly pattern: string[];
|
||||||
public readonly lhs: Clause;
|
public readonly lhs: Clause;
|
||||||
|
|
||||||
constructor(lhs: Clause, public rhs: string, public esc?: string) {
|
constructor(
|
||||||
|
lhs: Clause,
|
||||||
|
public rhs: string,
|
||||||
|
public esc?: string,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
const exp = lhs.expression();
|
const exp = lhs.expression();
|
||||||
let caseSensitive = true;
|
let caseSensitive = true;
|
||||||
@@ -515,7 +519,7 @@ export namespace Clause {
|
|||||||
clause = new Clause.Compare(
|
clause = new Clause.Compare(
|
||||||
e[1],
|
e[1],
|
||||||
op,
|
op,
|
||||||
rhs as boolean | number | string
|
rhs as boolean | number | string,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +539,7 @@ export namespace Clause {
|
|||||||
|
|
||||||
function groupBy<T, K>(
|
function groupBy<T, K>(
|
||||||
input: T[],
|
input: T[],
|
||||||
callback: (item: T) => K
|
callback: (item: T) => K,
|
||||||
): Iterable<[K, T[]]> {
|
): Iterable<[K, T[]]> {
|
||||||
const groups = new Map<K, T[]>();
|
const groups = new Map<K, T[]>();
|
||||||
for (const item of input) {
|
for (const item of input) {
|
||||||
@@ -567,12 +571,12 @@ export class SynthContext extends SynthContextBase<Clause, Clause> {
|
|||||||
|
|
||||||
const allClauses = [...whitelist].map((v) => this.getClause(v));
|
const allClauses = [...whitelist].map((v) => this.getClause(v));
|
||||||
const comparisons = allClauses.filter(
|
const comparisons = allClauses.filter(
|
||||||
(c) => c instanceof Clause.Compare
|
(c) => c instanceof Clause.Compare,
|
||||||
) as Clause.Compare[];
|
) as Clause.Compare[];
|
||||||
|
|
||||||
// Comparisons
|
// Comparisons
|
||||||
for (const [, clauses] of groupBy(comparisons, (c) =>
|
for (const [, clauses] of groupBy(comparisons, (c) =>
|
||||||
JSON.stringify(c.lhs)
|
JSON.stringify(c.lhs),
|
||||||
)) {
|
)) {
|
||||||
const lhs = clauses[0].lhs;
|
const lhs = clauses[0].lhs;
|
||||||
const values = new Set(clauses.map((c) => c.rhs));
|
const values = new Set(clauses.map((c) => c.rhs));
|
||||||
@@ -600,13 +604,13 @@ export class SynthContext extends SynthContextBase<Clause, Clause> {
|
|||||||
|
|
||||||
for (let j = 0; j < i; j++) {
|
for (let j = 0; j < i; j++) {
|
||||||
const eq2 = this.getVar(
|
const eq2 = this.getVar(
|
||||||
new Clause.Compare(lhs, "=", valuesSorted[j])
|
new Clause.Compare(lhs, "=", valuesSorted[j]),
|
||||||
);
|
);
|
||||||
const gt2 = this.getVar(
|
const gt2 = this.getVar(
|
||||||
new Clause.Compare(lhs, ">", valuesSorted[j])
|
new Clause.Compare(lhs, ">", valuesSorted[j]),
|
||||||
);
|
);
|
||||||
const lt2 = this.getVar(
|
const lt2 = this.getVar(
|
||||||
new Clause.Compare(lhs, "<", valuesSorted[j])
|
new Clause.Compare(lhs, "<", valuesSorted[j]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// This is the minimum clauses required if all relavent vars
|
// This is the minimum clauses required if all relavent vars
|
||||||
@@ -631,7 +635,7 @@ export class SynthContext extends SynthContextBase<Clause, Clause> {
|
|||||||
|
|
||||||
// LIKE
|
// LIKE
|
||||||
const likes = allClauses.filter(
|
const likes = allClauses.filter(
|
||||||
(c) => c instanceof Clause.Like
|
(c) => c instanceof Clause.Like,
|
||||||
) as Clause.Like[];
|
) as Clause.Like[];
|
||||||
|
|
||||||
for (const [, clauses] of groupBy(likes, (c) => JSON.stringify(c.lhs))) {
|
for (const [, clauses] of groupBy(likes, (c) => JSON.stringify(c.lhs))) {
|
||||||
@@ -734,7 +738,7 @@ export class SynthContext extends SynthContextBase<Clause, Clause> {
|
|||||||
for (const [k, v] of merged) {
|
for (const [k, v] of merged) {
|
||||||
if (v === 0b1010) continue loop;
|
if (v === 0b1010) continue loop;
|
||||||
const isNullVars = [...this.clauses.get(k).getNullables()].map((c) =>
|
const isNullVars = [...this.clauses.get(k).getNullables()].map((c) =>
|
||||||
this.getVar(c)
|
this.getVar(c),
|
||||||
);
|
);
|
||||||
const t = k << 2;
|
const t = k << 2;
|
||||||
if (v === 0b0101) {
|
if (v === 0b0101) {
|
||||||
@@ -858,7 +862,7 @@ export function minimize(expr: Expression, boolean = false): Expression {
|
|||||||
|
|
||||||
export function unionDiff(
|
export function unionDiff(
|
||||||
expr1: Expression,
|
expr1: Expression,
|
||||||
expr2: Expression
|
expr2: Expression,
|
||||||
): [Expression, Expression] {
|
): [Expression, Expression] {
|
||||||
expr2 = normalize(expr2);
|
expr2 = normalize(expr2);
|
||||||
|
|
||||||
@@ -887,7 +891,7 @@ export function unionDiff(
|
|||||||
complement([
|
complement([
|
||||||
...complement([...expr1NullMinterms, ...expr1FalseMinterms]),
|
...complement([...expr1NullMinterms, ...expr1FalseMinterms]),
|
||||||
...complement(expr2Minterms),
|
...complement(expr2Minterms),
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
return [context.toExpression(union), context.toExpression(diff)];
|
return [context.toExpression(union), context.toExpression(diff)];
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function likePatternToRegExp(pat: string, esc = "", flags = ""): RegExp {
|
|||||||
|
|
||||||
function compare(
|
function compare(
|
||||||
a: boolean | number | string,
|
a: boolean | number | string,
|
||||||
b: boolean | number | string
|
b: boolean | number | string,
|
||||||
): number {
|
): number {
|
||||||
if (typeof a === "boolean") a = +a;
|
if (typeof a === "boolean") a = +a;
|
||||||
if (typeof b === "boolean") b = +b;
|
if (typeof b === "boolean") b = +b;
|
||||||
@@ -287,19 +287,19 @@ export function evaluate(
|
|||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj: Record<string, unknown> | ((e: string) => Expression),
|
obj: Record<string, unknown> | ((e: string) => Expression),
|
||||||
now: number,
|
now: number,
|
||||||
cb?: (e: Expression) => Expression
|
cb?: (e: Expression) => Expression,
|
||||||
): string | number | boolean | null;
|
): string | number | boolean | null;
|
||||||
export function evaluate(
|
export function evaluate(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj?: Record<string, unknown> | ((e: string) => Expression),
|
obj?: Record<string, unknown> | ((e: string) => Expression),
|
||||||
now?: number,
|
now?: number,
|
||||||
cb?: (e: Expression) => Expression
|
cb?: (e: Expression) => Expression,
|
||||||
): Expression;
|
): Expression;
|
||||||
export function evaluate(
|
export function evaluate(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj?: Record<string, unknown> | ((e: string) => Expression),
|
obj?: Record<string, unknown> | ((e: string) => Expression),
|
||||||
now?: number,
|
now?: number,
|
||||||
cb?: (e: Expression) => Expression
|
cb?: (e: Expression) => Expression,
|
||||||
): Expression {
|
): Expression {
|
||||||
return map(exp, (e) => {
|
return map(exp, (e) => {
|
||||||
if (cb) e = cb(e);
|
if (cb) e = cb(e);
|
||||||
@@ -327,19 +327,19 @@ export async function evaluateAsync(
|
|||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj: Record<string, unknown>,
|
obj: Record<string, unknown>,
|
||||||
now: number,
|
now: number,
|
||||||
cb?: (e: Expression) => Promise<Expression>
|
cb?: (e: Expression) => Promise<Expression>,
|
||||||
): Promise<string | number | boolean | null>;
|
): Promise<string | number | boolean | null>;
|
||||||
export async function evaluateAsync(
|
export async function evaluateAsync(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj?: Record<string, unknown>,
|
obj?: Record<string, unknown>,
|
||||||
now?: number,
|
now?: number,
|
||||||
cb?: (e: Expression) => Promise<Expression>
|
cb?: (e: Expression) => Promise<Expression>,
|
||||||
): Promise<Expression>;
|
): Promise<Expression>;
|
||||||
export async function evaluateAsync(
|
export async function evaluateAsync(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
obj?: Record<string, unknown>,
|
obj?: Record<string, unknown>,
|
||||||
now?: number,
|
now?: number,
|
||||||
cb?: (e: Expression) => Promise<Expression>
|
cb?: (e: Expression) => Promise<Expression>,
|
||||||
): Promise<Expression> {
|
): Promise<Expression> {
|
||||||
return mapAsync(exp, async (e) => {
|
return mapAsync(exp, async (e) => {
|
||||||
if (cb) e = await cb(e);
|
if (cb) e = await cb(e);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default class PathSet {
|
|||||||
path: Path,
|
path: Path,
|
||||||
superset = false,
|
superset = false,
|
||||||
subset = false,
|
subset = false,
|
||||||
depth: number = path.length
|
depth: number = path.length,
|
||||||
): Path[] {
|
): Path[] {
|
||||||
if (path.alias) throw new Error("PathSet does not support aliased paths");
|
if (path.alias) throw new Error("PathSet does not support aliased paths");
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export default class PathSet {
|
|||||||
res = [...fragmentIndexSet1, ...fragmentIndexSet2];
|
res = [...fragmentIndexSet1, ...fragmentIndexSet2];
|
||||||
} else {
|
} else {
|
||||||
res = res.filter(
|
res = res.filter(
|
||||||
(r) => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r)
|
(r) => fragmentIndexSet1.has(r) || fragmentIndexSet2.has(r),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -57,7 +57,7 @@ export default class Path {
|
|||||||
|
|
||||||
protected static parseAliasValue(
|
protected static parseAliasValue(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
index: number
|
index: number,
|
||||||
): { index: number; value: string } {
|
): { index: number; value: string } {
|
||||||
let i = index;
|
let i = index;
|
||||||
while (charCodeAt(pattern, i) === CHAR_SPACE) ++i;
|
while (charCodeAt(pattern, i) === CHAR_SPACE) ++i;
|
||||||
@@ -89,7 +89,7 @@ export default class Path {
|
|||||||
|
|
||||||
protected static parseAliasPath(
|
protected static parseAliasPath(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
index: number
|
index: number,
|
||||||
): { index: number; path: Path } {
|
): { index: number; path: Path } {
|
||||||
let i = index;
|
let i = index;
|
||||||
while (charCodeAt(pattern, i) === CHAR_SPACE) ++i;
|
while (charCodeAt(pattern, i) === CHAR_SPACE) ++i;
|
||||||
@@ -103,7 +103,7 @@ export default class Path {
|
|||||||
|
|
||||||
protected static parseAlias(
|
protected static parseAlias(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
index: number
|
index: number,
|
||||||
): { index: number; alias: Alias } {
|
): { index: number; alias: Alias } {
|
||||||
const alias: Alias = [];
|
const alias: Alias = [];
|
||||||
let i = index;
|
let i = index;
|
||||||
@@ -136,7 +136,7 @@ export default class Path {
|
|||||||
|
|
||||||
protected static parsePath(
|
protected static parsePath(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
index: number
|
index: number,
|
||||||
): { index: number; segments: Segments } {
|
): { index: number; segments: Segments } {
|
||||||
const segments = [];
|
const segments = [];
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ export default class Path {
|
|||||||
if (Array.isArray(s)) {
|
if (Array.isArray(s)) {
|
||||||
alias |= 1 << i;
|
alias |= 1 << i;
|
||||||
const parts = s.map(
|
const parts = s.map(
|
||||||
(al) => `${al[0].toString()}:${JSON.stringify(al[1])}`
|
(al) => `${al[0].toString()}:${JSON.stringify(al[1])}`,
|
||||||
);
|
);
|
||||||
return `[${parts.join(",")}]`;
|
return `[${parts.join(",")}]`;
|
||||||
} else if (s === "*") {
|
} else if (s === "*") {
|
||||||
|
|||||||
+6
-6
@@ -15,7 +15,7 @@ const STRING_RESERVED = new Set([
|
|||||||
|
|
||||||
function isPrintable(str: string): boolean {
|
function isPrintable(str: string): boolean {
|
||||||
return !/[^\t\n\x20-\x7e\x85\u{a0}-\u{d7ff}\u{e000}-\u{fffd}\u{10000}-\u{10ffff}]/u.test(
|
return !/[^\t\n\x20-\x7e\x85\u{a0}-\u{d7ff}\u{e000}-\u{fffd}\u{10000}-\u{10ffff}]/u.test(
|
||||||
str
|
str,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ function stringifyString(str: string, res: string[], prefix1, prefix2): void {
|
|||||||
if (!isFolded) {
|
if (!isFolded) {
|
||||||
res.push(
|
res.push(
|
||||||
`${prefix1}|${idt}${chmp}`,
|
`${prefix1}|${idt}${chmp}`,
|
||||||
...lines.map((l) => (l ? prefix2 + l : l))
|
...lines.map((l) => (l ? prefix2 + l : l)),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ function stringifyAny(
|
|||||||
obj: unknown,
|
obj: unknown,
|
||||||
res: string[],
|
res: string[],
|
||||||
prefix1 = "",
|
prefix1 = "",
|
||||||
prefix2 = ""
|
prefix2 = "",
|
||||||
): void {
|
): void {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
res.push(`${prefix1}null`);
|
res.push(`${prefix1}null`);
|
||||||
@@ -183,7 +183,7 @@ function stringifyAny(
|
|||||||
entries[0][1],
|
entries[0][1],
|
||||||
res,
|
res,
|
||||||
prefix1 + `${stringifyKey(entries[0][0])}: `,
|
prefix1 + `${stringifyKey(entries[0][0])}: `,
|
||||||
prefix2 + INDENTATION
|
prefix2 + INDENTATION,
|
||||||
);
|
);
|
||||||
prefix1 = prefix2;
|
prefix1 = prefix2;
|
||||||
prefix2 = prefix2 + INDENTATION;
|
prefix2 = prefix2 + INDENTATION;
|
||||||
@@ -192,7 +192,7 @@ function stringifyAny(
|
|||||||
entries[i][1],
|
entries[i][1],
|
||||||
res,
|
res,
|
||||||
prefix1 + `${stringifyKey(entries[i][0])}: `,
|
prefix1 + `${stringifyKey(entries[i][0])}: `,
|
||||||
prefix2
|
prefix2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -204,7 +204,7 @@ function stringifyAny(
|
|||||||
entries[i][1],
|
entries[i][1],
|
||||||
res,
|
res,
|
||||||
prefix1 + `${stringifyKey(entries[i][0])}: `,
|
prefix1 + `${stringifyKey(entries[i][0])}: `,
|
||||||
prefix2
|
prefix2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -245,7 +245,7 @@ if (fsHostname) {
|
|||||||
const FS_SSL = !!allConfig["FS_SSL_CERT"];
|
const FS_SSL = !!allConfig["FS_SSL_CERT"];
|
||||||
setConfig(
|
setConfig(
|
||||||
"FS_URL_PREFIX",
|
"FS_URL_PREFIX",
|
||||||
(FS_SSL ? "https" : "http") + `://${fsHostname}:${FS_PORT}/`
|
(FS_SSL ? "https" : "http") + `://${fsHostname}:${FS_PORT}/`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ for (const [k, v] of Object.entries(options))
|
|||||||
|
|
||||||
export function get(
|
export function get(
|
||||||
optionName: string,
|
optionName: string,
|
||||||
deviceId?: string
|
deviceId?: string,
|
||||||
): string | number | boolean {
|
): string | number | boolean {
|
||||||
if (!deviceId) return allConfig[optionName];
|
if (!deviceId) return allConfig[optionName];
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import * as dgram from "dgram";
|
import * as dgram from "dgram";
|
||||||
import { URL } from "url";
|
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
import { evaluateAsync } from "./common/expression/util";
|
import { evaluateAsync } from "./common/expression/util";
|
||||||
import { Expression } from "./types";
|
import { Expression } from "./types";
|
||||||
@@ -29,7 +28,7 @@ import * as debug from "./debug";
|
|||||||
|
|
||||||
async function extractAuth(
|
async function extractAuth(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
dflt: any
|
dflt: any,
|
||||||
): Promise<[string, string, Expression]> {
|
): Promise<[string, string, Expression]> {
|
||||||
let username, password;
|
let username, password;
|
||||||
const _exp = await evaluateAsync(
|
const _exp = await evaluateAsync(
|
||||||
@@ -55,7 +54,7 @@ async function extractAuth(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return [username, password, _exp];
|
return [username, password, _exp];
|
||||||
}
|
}
|
||||||
@@ -64,7 +63,7 @@ function httpGet(
|
|||||||
url: URL,
|
url: URL,
|
||||||
options: http.RequestOptions,
|
options: http.RequestOptions,
|
||||||
_debug: boolean,
|
_debug: boolean,
|
||||||
deviceId: string
|
deviceId: string,
|
||||||
): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> {
|
): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const req = http
|
const req = http
|
||||||
@@ -94,7 +93,7 @@ export async function httpConnectionRequest(
|
|||||||
allowBasicAuth: boolean,
|
allowBasicAuth: boolean,
|
||||||
timeout: number,
|
timeout: number,
|
||||||
_debug: boolean,
|
_debug: boolean,
|
||||||
deviceId: string
|
deviceId: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const url = new URL(address);
|
const url = new URL(address);
|
||||||
if (url.protocol !== "http:")
|
if (url.protocol !== "http:")
|
||||||
@@ -120,7 +119,7 @@ export async function httpConnectionRequest(
|
|||||||
Authorization: auth.basic(username || "", password || ""),
|
Authorization: auth.basic(username || "", password || ""),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
} else if (authHeader["method"] === "Digest") {
|
} else if (authHeader["method"] === "Digest") {
|
||||||
opts = Object.assign(
|
opts = Object.assign(
|
||||||
@@ -132,11 +131,11 @@ export async function httpConnectionRequest(
|
|||||||
url.pathname + url.search,
|
url.pathname + url.search,
|
||||||
"GET",
|
"GET",
|
||||||
null,
|
null,
|
||||||
authHeader
|
authHeader,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return "Unrecognized auth method";
|
return "Unrecognized auth method";
|
||||||
@@ -171,7 +170,7 @@ export async function httpConnectionRequest(
|
|||||||
if (res.statusCode === 401 && res.headers["www-authenticate"]) {
|
if (res.statusCode === 401 && res.headers["www-authenticate"]) {
|
||||||
try {
|
try {
|
||||||
authHeader = auth.parseWwwAuthenticateHeader(
|
authHeader = auth.parseWwwAuthenticateHeader(
|
||||||
res.headers["www-authenticate"]
|
res.headers["www-authenticate"],
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return "Connection request error: Error parsing www-authenticate header";
|
return "Connection request error: Error parsing www-authenticate header";
|
||||||
@@ -191,7 +190,7 @@ export async function udpConnectionRequest(
|
|||||||
authExp: Expression,
|
authExp: Expression,
|
||||||
sourcePort = 0,
|
sourcePort = 0,
|
||||||
_debug: boolean,
|
_debug: boolean,
|
||||||
deviceId: string
|
deviceId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
|||||||
+63
-64
@@ -89,11 +89,11 @@ const stats = {
|
|||||||
|
|
||||||
async function authenticate(
|
async function authenticate(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
body: string
|
body: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const authExpression: Expression = localCache.getConfigExpression(
|
const authExpression: Expression = localCache.getConfigExpression(
|
||||||
sessionContext.cacheSnapshot,
|
sessionContext.cacheSnapshot,
|
||||||
"cwmp.auth"
|
"cwmp.auth",
|
||||||
);
|
);
|
||||||
if (authExpression == null) return true;
|
if (authExpression == null) return true;
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ async function authenticate(
|
|||||||
if (sessionContext.httpRequest.headers["authorization"]) {
|
if (sessionContext.httpRequest.headers["authorization"]) {
|
||||||
try {
|
try {
|
||||||
authentication = auth.parseAuthorizationHeader(
|
authentication = auth.parseAuthorizationHeader(
|
||||||
sessionContext.httpRequest.headers["authorization"]
|
sessionContext.httpRequest.headers["authorization"],
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
@@ -159,7 +159,7 @@ async function authenticate(
|
|||||||
authentication["qop"],
|
authentication["qop"],
|
||||||
authentication["body"],
|
authentication["body"],
|
||||||
authentication["cnonce"],
|
authentication["cnonce"],
|
||||||
authentication["nc"]
|
authentication["nc"],
|
||||||
);
|
);
|
||||||
return expected === authentication["response"];
|
return expected === authentication["response"];
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ async function authenticate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res && !Array.isArray(res)) return true;
|
if (res && !Array.isArray(res)) return true;
|
||||||
@@ -179,7 +179,7 @@ async function authenticate(
|
|||||||
async function writeResponse(
|
async function writeResponse(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
res,
|
res,
|
||||||
close = false
|
close = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Close connection after last request in session
|
// Close connection after last request in session
|
||||||
if (close) res.headers["Connection"] = "close";
|
if (close) res.headers["Connection"] = "close";
|
||||||
@@ -231,7 +231,7 @@ async function writeResponse(
|
|||||||
`cwmp_session_${sessionContext.deviceId}`,
|
`cwmp_session_${sessionContext.deviceId}`,
|
||||||
sessionContext.timeout * 1000 + 15000,
|
sessionContext.timeout * 1000 + 15000,
|
||||||
0,
|
0,
|
||||||
`cwmp_session_${sessionContext.sessionId}`
|
`cwmp_session_${sessionContext.sessionId}`,
|
||||||
);
|
);
|
||||||
if (!lockToken) throw new Error("Failed to extend lock");
|
if (!lockToken) throw new Error("Failed to extend lock");
|
||||||
}
|
}
|
||||||
@@ -242,14 +242,14 @@ function recordFault(
|
|||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
fault: Fault,
|
fault: Fault,
|
||||||
provisions,
|
provisions,
|
||||||
channels
|
channels,
|
||||||
): void;
|
): void;
|
||||||
function recordFault(sessionContext: SessionContext, fault: Fault): void;
|
function recordFault(sessionContext: SessionContext, fault: Fault): void;
|
||||||
function recordFault(
|
function recordFault(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
fault: Fault,
|
fault: Fault,
|
||||||
provisions?,
|
provisions?,
|
||||||
channels?
|
channels?,
|
||||||
): void {
|
): void {
|
||||||
if (!provisions) {
|
if (!provisions) {
|
||||||
provisions = sessionContext.provisions;
|
provisions = sessionContext.provisions;
|
||||||
@@ -267,7 +267,7 @@ function recordFault(
|
|||||||
: [];
|
: [];
|
||||||
faults[channel] = Object.assign(
|
faults[channel] = Object.assign(
|
||||||
{ provisions: provs, timestamp: sessionContext.timestamp },
|
{ provisions: provs, timestamp: sessionContext.timestamp },
|
||||||
fault
|
fault,
|
||||||
) as SessionFault;
|
) as SessionFault;
|
||||||
if (channel.startsWith("task_")) {
|
if (channel.startsWith("task_")) {
|
||||||
const taskId = channel.slice(5);
|
const taskId = channel.slice(5);
|
||||||
@@ -314,11 +314,11 @@ function recordFault(
|
|||||||
|
|
||||||
async function inform(
|
async function inform(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
rpc: SoapMessage
|
rpc: SoapMessage,
|
||||||
): Promise<{ code: number; headers: Record<string, string>; data: string }> {
|
): Promise<{ code: number; headers: Record<string, string>; data: string }> {
|
||||||
const acsResponse = await session.inform(
|
const acsResponse = await session.inform(
|
||||||
sessionContext,
|
sessionContext,
|
||||||
rpc.cpeRequest as InformRequest
|
rpc.cpeRequest as InformRequest,
|
||||||
);
|
);
|
||||||
|
|
||||||
const res = soap.response({
|
const res = soap.response({
|
||||||
@@ -332,13 +332,12 @@ async function inform(
|
|||||||
"cwmp.cookiesPath",
|
"cwmp.cookiesPath",
|
||||||
{},
|
{},
|
||||||
sessionContext.timestamp,
|
sessionContext.timestamp,
|
||||||
(e) => session.configContextCallback(sessionContext, e)
|
(e) => session.configContextCallback(sessionContext, e),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cookiesPath) {
|
if (cookiesPath) {
|
||||||
res.headers[
|
res.headers["Set-Cookie"] =
|
||||||
"Set-Cookie"
|
`session=${sessionContext.sessionId}; Path=${cookiesPath}`;
|
||||||
] = `session=${sessionContext.sessionId}; Path=${cookiesPath}`;
|
|
||||||
} else {
|
} else {
|
||||||
res.headers["Set-Cookie"] = `session=${sessionContext.sessionId}`;
|
res.headers["Set-Cookie"] = `session=${sessionContext.sessionId}`;
|
||||||
}
|
}
|
||||||
@@ -349,7 +348,7 @@ async function inform(
|
|||||||
async function transferComplete(sessionContext, rpc): Promise<void> {
|
async function transferComplete(sessionContext, rpc): Promise<void> {
|
||||||
const { acsResponse, operation, fault } = await session.transferComplete(
|
const { acsResponse, operation, fault } = await session.transferComplete(
|
||||||
sessionContext,
|
sessionContext,
|
||||||
rpc.cpeRequest
|
rpc.cpeRequest,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!operation) {
|
if (!operation) {
|
||||||
@@ -366,7 +365,7 @@ async function transferComplete(sessionContext, rpc): Promise<void> {
|
|||||||
sessionContext,
|
sessionContext,
|
||||||
fault,
|
fault,
|
||||||
operation.provisions,
|
operation.provisions,
|
||||||
operation.channels
|
operation.channels,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +423,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
"cwmp.retryDelay",
|
"cwmp.retryDelay",
|
||||||
{},
|
{},
|
||||||
sessionContext.timestamp,
|
sessionContext.timestamp,
|
||||||
(e) => session.configContextCallback(sessionContext, e)
|
(e) => session.configContextCallback(sessionContext, e),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sessionContext.faults) {
|
if (sessionContext.faults) {
|
||||||
@@ -452,7 +451,8 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
const deviceEvents = {};
|
const deviceEvents = {};
|
||||||
for (const p of deviceData.paths.find(Path.parse("Events.*"), false, true)) {
|
for (const p of deviceData.paths.find(Path.parse("Events.*"), false, true)) {
|
||||||
const attrs = deviceData.attributes.get(p);
|
const attrs = deviceData.attributes.get(p);
|
||||||
if (attrs?.value && attrs.value[1][0] >= sessionContext.timestamp)
|
const t = attrs?.value[1][0] as number;
|
||||||
|
if (t >= sessionContext.timestamp)
|
||||||
deviceEvents[p.segments[1] as string] = true;
|
deviceEvents[p.segments[1] as string] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
if (preset.schedule?.schedule) {
|
if (preset.schedule?.schedule) {
|
||||||
const r = scheduling.cron(
|
const r = scheduling.cron(
|
||||||
sessionContext.timestamp,
|
sessionContext.timestamp,
|
||||||
preset.schedule.schedule
|
preset.schedule.schedule,
|
||||||
);
|
);
|
||||||
if (!(r[0] + preset.schedule.duration > sessionContext.timestamp))
|
if (!(r[0] + preset.schedule.duration > sessionContext.timestamp))
|
||||||
continue;
|
continue;
|
||||||
@@ -487,7 +487,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
|
|
||||||
filteredPresets.push(preset);
|
filteredPresets.push(preset);
|
||||||
for (const k of extractParams(
|
for (const k of extractParams(
|
||||||
evaluate(preset.precondition, null, sessionContext.timestamp)
|
evaluate(preset.precondition, null, sessionContext.timestamp),
|
||||||
)) {
|
)) {
|
||||||
// Mark channel in case of fault during fetching precondition
|
// Mark channel in case of fault during fetching precondition
|
||||||
sessionContext.channels[preset.channel] = 0;
|
sessionContext.channels[preset.channel] = 0;
|
||||||
@@ -496,7 +496,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
for (const prov of preset.provisions) {
|
for (const prov of preset.provisions) {
|
||||||
for (const arg of prov.slice(1)) {
|
for (const arg of prov.slice(1)) {
|
||||||
for (const k of extractParams(
|
for (const k of extractParams(
|
||||||
evaluate(arg, null, sessionContext.timestamp)
|
evaluate(arg, null, sessionContext.timestamp),
|
||||||
)) {
|
)) {
|
||||||
// Mark channel in case of fault during fetching precondition
|
// Mark channel in case of fault during fetching precondition
|
||||||
sessionContext.channels[preset.channel] = 0;
|
sessionContext.channels[preset.channel] = 0;
|
||||||
@@ -538,7 +538,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
for (const p of filteredPresets) {
|
for (const p of filteredPresets) {
|
||||||
if (
|
if (
|
||||||
evaluate(p.precondition, {}, sessionContext.timestamp, (e) =>
|
evaluate(p.precondition, {}, sessionContext.timestamp, (e) =>
|
||||||
session.configContextCallback(sessionContext, e)
|
session.configContextCallback(sessionContext, e),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const provs = p.provisions.map((pp) => [
|
const provs = p.provisions.map((pp) => [
|
||||||
@@ -547,8 +547,8 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
.slice(1)
|
.slice(1)
|
||||||
.map((arg) =>
|
.map((arg) =>
|
||||||
evaluate(arg, {}, sessionContext.timestamp, (e) =>
|
evaluate(arg, {}, sessionContext.timestamp, (e) =>
|
||||||
session.configContextCallback(sessionContext, e)
|
session.configContextCallback(sessionContext, e),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
]) as [string, ...Expression[]][];
|
]) as [string, ...Expression[]][];
|
||||||
if (blackList[p.channel] === 2) {
|
if (blackList[p.channel] === 2) {
|
||||||
@@ -562,7 +562,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [channel, provisions] of Object.entries(
|
for (const [channel, provisions] of Object.entries(
|
||||||
appendProvisionsToFaults
|
appendProvisionsToFaults,
|
||||||
)) {
|
)) {
|
||||||
if (
|
if (
|
||||||
appendProvisions(sessionContext.faults[channel].provisions, provisions)
|
appendProvisions(sessionContext.faults[channel].provisions, provisions)
|
||||||
@@ -686,7 +686,7 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const task = sessionContext.tasks.find(
|
const task = sessionContext.tasks.find(
|
||||||
(t) => !sessionContext.faults[`task_${t._id}`]
|
(t) => !sessionContext.faults[`task_${t._id}`],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!task) return applyPresets(sessionContext);
|
if (!task) return applyPresets(sessionContext);
|
||||||
@@ -747,7 +747,7 @@ async function nextRpc(sessionContext: SessionContext): Promise<void> {
|
|||||||
session.addProvisions(
|
session.addProvisions(
|
||||||
sessionContext,
|
sessionContext,
|
||||||
`task_${task._id}`,
|
`task_${task._id}`,
|
||||||
task.provisions
|
task.provisions,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -784,8 +784,8 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
sessionContext.deviceId,
|
sessionContext.deviceId,
|
||||||
sessionContext.deviceData,
|
sessionContext.deviceData,
|
||||||
sessionContext.new,
|
sessionContext.new,
|
||||||
sessionContext.timestamp
|
sessionContext.timestamp,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sessionContext.operationsTouched) {
|
if (sessionContext.operationsTouched) {
|
||||||
@@ -795,8 +795,8 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
saveOperation(
|
saveOperation(
|
||||||
sessionContext.deviceId,
|
sessionContext.deviceId,
|
||||||
k,
|
k,
|
||||||
sessionContext.operations[k]
|
sessionContext.operations[k],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
promises.push(deleteOperation(sessionContext.deviceId, k));
|
promises.push(deleteOperation(sessionContext.deviceId, k));
|
||||||
@@ -806,7 +806,7 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
|
|
||||||
if (sessionContext.doneTasks?.length) {
|
if (sessionContext.doneTasks?.length) {
|
||||||
promises.push(
|
promises.push(
|
||||||
clearTasks(sessionContext.deviceId, sessionContext.doneTasks)
|
clearTasks(sessionContext.deviceId, sessionContext.doneTasks),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,7 +815,7 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
if (sessionContext.faults[k]) {
|
if (sessionContext.faults[k]) {
|
||||||
sessionContext.faults[k].retries = sessionContext.retries[k];
|
sessionContext.faults[k].retries = sessionContext.retries[k];
|
||||||
promises.push(
|
promises.push(
|
||||||
saveFault(sessionContext.deviceId, k, sessionContext.faults[k])
|
saveFault(sessionContext.deviceId, k, sessionContext.faults[k]),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
promises.push(deleteFault(sessionContext.deviceId, k));
|
promises.push(deleteFault(sessionContext.deviceId, k));
|
||||||
@@ -826,7 +826,7 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
await lock.releaseLock(
|
await lock.releaseLock(
|
||||||
`cwmp_session_${sessionContext.deviceId}`,
|
`cwmp_session_${sessionContext.deviceId}`,
|
||||||
`cwmp_session_${sessionContext.sessionId}`
|
`cwmp_session_${sessionContext.sessionId}`,
|
||||||
);
|
);
|
||||||
if (sessionContext.new) {
|
if (sessionContext.new) {
|
||||||
logger.accessInfo({
|
logger.accessInfo({
|
||||||
@@ -839,7 +839,7 @@ async function endSession(sessionContext: SessionContext): Promise<void> {
|
|||||||
async function sendAcsRequest(
|
async function sendAcsRequest(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
id?: string,
|
id?: string,
|
||||||
acsRequest?: AcsRequest
|
acsRequest?: AcsRequest,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!acsRequest)
|
if (!acsRequest)
|
||||||
return writeResponse(sessionContext, soap.response(null), true);
|
return writeResponse(sessionContext, soap.response(null), true);
|
||||||
@@ -918,7 +918,7 @@ export async function onConnection(socket: Socket): Promise<void> {
|
|||||||
await cache.set(
|
await cache.set(
|
||||||
`session_${sessionContext.sessionId}`,
|
`session_${sessionContext.sessionId}`,
|
||||||
await session.serialize(sessionContext),
|
await session.serialize(sessionContext),
|
||||||
Math.ceil(timeout / 1000) + 3
|
Math.ceil(timeout / 1000) + 3,
|
||||||
);
|
);
|
||||||
|
|
||||||
await setTimeoutPromise(timeout + 1000, false);
|
await setTimeoutPromise(timeout + 1000, false);
|
||||||
@@ -946,7 +946,7 @@ export async function onClientError(err: Error, socket: Socket): Promise<void> {
|
|||||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||||
return remoteAddress;
|
return remoteAddress;
|
||||||
return e;
|
return e;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (debugEnabled) debug.clientError(remoteAddress, err);
|
if (debugEnabled) debug.clientError(remoteAddress, err);
|
||||||
@@ -985,7 +985,7 @@ async function reportBadState(sessionContext: SessionContext): Promise<void> {
|
|||||||
|
|
||||||
async function responseUnauthorized(
|
async function responseUnauthorized(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
close: boolean
|
close: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const resHeaders = {};
|
const resHeaders = {};
|
||||||
if (close) {
|
if (close) {
|
||||||
@@ -1024,7 +1024,7 @@ async function processRequest(
|
|||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
rpc: SoapMessage,
|
rpc: SoapMessage,
|
||||||
parseWarnings: Record<string, unknown>[],
|
parseWarnings: Record<string, unknown>[],
|
||||||
body: string
|
body: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const w of parseWarnings) {
|
for (const w of parseWarnings) {
|
||||||
w.sessionContext = sessionContext;
|
w.sessionContext = sessionContext;
|
||||||
@@ -1042,7 +1042,7 @@ async function processRequest(
|
|||||||
"cwmp.debug",
|
"cwmp.debug",
|
||||||
{},
|
{},
|
||||||
sessionContext.timestamp,
|
sessionContext.timestamp,
|
||||||
(e) => session.configContextCallback(sessionContext, e)
|
(e) => session.configContextCallback(sessionContext, e),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!sessionContext.timeout) {
|
if (!sessionContext.timeout) {
|
||||||
@@ -1051,7 +1051,7 @@ async function processRequest(
|
|||||||
"cwmp.sessionTimeout",
|
"cwmp.sessionTimeout",
|
||||||
{},
|
{},
|
||||||
sessionContext.timestamp,
|
sessionContext.timestamp,
|
||||||
(e) => session.configContextCallback(sessionContext, e)
|
(e) => session.configContextCallback(sessionContext, e),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,7 +1061,7 @@ async function processRequest(
|
|||||||
debug.incomingHttpRequest(
|
debug.incomingHttpRequest(
|
||||||
sessionContext.httpRequest,
|
sessionContext.httpRequest,
|
||||||
sessionContext.deviceId,
|
sessionContext.deviceId,
|
||||||
body
|
body,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,7 +1080,7 @@ async function processRequest(
|
|||||||
`cwmp_session_${sessionContext.deviceId}`,
|
`cwmp_session_${sessionContext.deviceId}`,
|
||||||
sessionContext.timeout * 1000 + 15000,
|
sessionContext.timeout * 1000 + 15000,
|
||||||
0,
|
0,
|
||||||
`cwmp_session_${sessionContext.sessionId}`
|
`cwmp_session_${sessionContext.sessionId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!lockToken) {
|
if (!lockToken) {
|
||||||
@@ -1092,14 +1092,14 @@ async function processRequest(
|
|||||||
const _body = "CPE already in session";
|
const _body = "CPE already in session";
|
||||||
sessionContext.httpResponse.setHeader(
|
sessionContext.httpResponse.setHeader(
|
||||||
"Content-Length",
|
"Content-Length",
|
||||||
Buffer.byteLength(_body)
|
Buffer.byteLength(_body),
|
||||||
);
|
);
|
||||||
sessionContext.httpResponse.writeHead(400, { Connection: "close" });
|
sessionContext.httpResponse.writeHead(400, { Connection: "close" });
|
||||||
if (sessionContext.debug) {
|
if (sessionContext.debug) {
|
||||||
debug.outgoingHttpResponse(
|
debug.outgoingHttpResponse(
|
||||||
sessionContext.httpResponse,
|
sessionContext.httpResponse,
|
||||||
sessionContext.deviceId,
|
sessionContext.deviceId,
|
||||||
_body
|
_body,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
sessionContext.httpResponse.end(_body);
|
sessionContext.httpResponse.end(_body);
|
||||||
@@ -1122,7 +1122,7 @@ async function processRequest(
|
|||||||
debug.incomingHttpRequest(
|
debug.incomingHttpRequest(
|
||||||
sessionContext.httpRequest,
|
sessionContext.httpRequest,
|
||||||
sessionContext.deviceId,
|
sessionContext.deviceId,
|
||||||
body
|
body,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,7 +1180,7 @@ async function processRequest(
|
|||||||
const fault = await session.rpcResponse(
|
const fault = await session.rpcResponse(
|
||||||
sessionContext,
|
sessionContext,
|
||||||
rpc.id,
|
rpc.id,
|
||||||
rpc.cpeResponse
|
rpc.cpeResponse,
|
||||||
);
|
);
|
||||||
if (fault) {
|
if (fault) {
|
||||||
recordFault(sessionContext, fault);
|
recordFault(sessionContext, fault);
|
||||||
@@ -1242,9 +1242,8 @@ async function processRequest(
|
|||||||
if (sessionContext.state !== 1) return reportBadState(sessionContext);
|
if (sessionContext.state !== 1) return reportBadState(sessionContext);
|
||||||
|
|
||||||
sessionContext.state = 2;
|
sessionContext.state = 2;
|
||||||
const { faults, operations } = await session.timeoutOperations(
|
const { faults, operations } =
|
||||||
sessionContext
|
await session.timeoutOperations(sessionContext);
|
||||||
);
|
|
||||||
|
|
||||||
for (const [i, f] of faults.entries()) {
|
for (const [i, f] of faults.entries()) {
|
||||||
for (const [k, v] of Object.entries(operations[i].retries))
|
for (const [k, v] of Object.entries(operations[i].retries))
|
||||||
@@ -1254,7 +1253,7 @@ async function processRequest(
|
|||||||
sessionContext,
|
sessionContext,
|
||||||
f,
|
f,
|
||||||
operations[i].provisions,
|
operations[i].provisions,
|
||||||
operations[i].channels
|
operations[i].channels,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1264,7 +1263,7 @@ async function processRequest(
|
|||||||
|
|
||||||
export async function listener(
|
export async function listener(
|
||||||
httpRequest: IncomingMessage,
|
httpRequest: IncomingMessage,
|
||||||
httpResponse: ServerResponse
|
httpResponse: ServerResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
stats.concurrentRequests += 1;
|
stats.concurrentRequests += 1;
|
||||||
try {
|
try {
|
||||||
@@ -1282,7 +1281,7 @@ async function clientError(
|
|||||||
httpResponse: ServerResponse,
|
httpResponse: ServerResponse,
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
body: string,
|
body: string,
|
||||||
msg: string
|
msg: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let debugEnabled = false;
|
let debugEnabled = false;
|
||||||
let deviceId: string = null;
|
let deviceId: string = null;
|
||||||
@@ -1303,7 +1302,7 @@ async function clientError(
|
|||||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||||
return getRequestOrigin(httpRequest).remoteAddress;
|
return getRequestOrigin(httpRequest).remoteAddress;
|
||||||
return e;
|
return e;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1330,7 +1329,7 @@ function decodeString(buffer: Buffer, charset: string): string {
|
|||||||
|
|
||||||
async function listenerAsync(
|
async function listenerAsync(
|
||||||
httpRequest: IncomingMessage,
|
httpRequest: IncomingMessage,
|
||||||
httpResponse: ServerResponse
|
httpResponse: ServerResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
stats.totalRequests += 1;
|
stats.totalRequests += 1;
|
||||||
|
|
||||||
@@ -1419,7 +1418,7 @@ async function listenerAsync(
|
|||||||
httpResponse,
|
httpResponse,
|
||||||
sessionContext,
|
sessionContext,
|
||||||
body.toString(),
|
body.toString(),
|
||||||
"Invalid session"
|
"Invalid session",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1427,7 +1426,7 @@ async function listenerAsync(
|
|||||||
let charset: string;
|
let charset: string;
|
||||||
if (httpRequest.headers["content-type"]) {
|
if (httpRequest.headers["content-type"]) {
|
||||||
const m = httpRequest.headers["content-type"].match(
|
const m = httpRequest.headers["content-type"].match(
|
||||||
/charset=['"]?([^'"\s]+)/i
|
/charset=['"]?([^'"\s]+)/i,
|
||||||
);
|
);
|
||||||
if (m) charset = m[1].toLowerCase();
|
if (m) charset = m[1].toLowerCase();
|
||||||
}
|
}
|
||||||
@@ -1465,7 +1464,7 @@ async function listenerAsync(
|
|||||||
httpResponse,
|
httpResponse,
|
||||||
sessionContext,
|
sessionContext,
|
||||||
body.toString(),
|
body.toString(),
|
||||||
msg
|
msg,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1498,7 +1497,7 @@ async function listenerAsync(
|
|||||||
httpResponse,
|
httpResponse,
|
||||||
sessionContext,
|
sessionContext,
|
||||||
bodyStr,
|
bodyStr,
|
||||||
err.message
|
err.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1531,7 +1530,7 @@ async function listenerAsync(
|
|||||||
httpResponse,
|
httpResponse,
|
||||||
null,
|
null,
|
||||||
bodyStr,
|
bodyStr,
|
||||||
"Invalid session"
|
"Invalid session",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1552,7 +1551,7 @@ async function listenerAsync(
|
|||||||
const _sessionContext = session.init(
|
const _sessionContext = session.init(
|
||||||
deviceId,
|
deviceId,
|
||||||
rpc.cwmpVersion,
|
rpc.cwmpVersion,
|
||||||
rpc.sessionTimeout
|
rpc.sessionTimeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
_sessionContext.cacheSnapshot = cacheSnapshot;
|
_sessionContext.cacheSnapshot = cacheSnapshot;
|
||||||
@@ -1584,7 +1583,7 @@ async function listenerAsync(
|
|||||||
|
|
||||||
const parameters = await fetchDevice(
|
const parameters = await fetchDevice(
|
||||||
_sessionContext.deviceId,
|
_sessionContext.deviceId,
|
||||||
_sessionContext.timestamp
|
_sessionContext.timestamp,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (parameters) {
|
if (parameters) {
|
||||||
|
|||||||
+20
-17
@@ -22,7 +22,7 @@ function compareAccessLists(list1: string[], list2: string[]): boolean {
|
|||||||
|
|
||||||
export async function fetchDevice(
|
export async function fetchDevice(
|
||||||
id: string,
|
id: string,
|
||||||
timestamp: number
|
timestamp: number,
|
||||||
): Promise<[Path, number, Attributes?][]> {
|
): Promise<[Path, number, Attributes?][]> {
|
||||||
const res: [Path, number, Attributes?][] = [
|
const res: [Path, number, Attributes?][] = [
|
||||||
[
|
[
|
||||||
@@ -44,7 +44,7 @@ export async function fetchDevice(
|
|||||||
obj,
|
obj,
|
||||||
path: string,
|
path: string,
|
||||||
pathLength: number,
|
pathLength: number,
|
||||||
ts: number
|
ts: number,
|
||||||
): void {
|
): void {
|
||||||
if (obj["_timestamp"]) obj["_timestamp"] = +obj["_timestamp"];
|
if (obj["_timestamp"]) obj["_timestamp"] = +obj["_timestamp"];
|
||||||
if (obj["_attributesTimestamp"])
|
if (obj["_attributesTimestamp"])
|
||||||
@@ -243,7 +243,7 @@ export async function saveDevice(
|
|||||||
deviceId: string,
|
deviceId: string,
|
||||||
deviceData: DeviceData,
|
deviceData: DeviceData,
|
||||||
isNew: boolean,
|
isNew: boolean,
|
||||||
sessionTimestamp: number
|
sessionTimestamp: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const update = { $set: {}, $unset: {}, $addToSet: {}, $pull: {} };
|
const update = { $set: {}, $unset: {}, $addToSet: {}, $pull: {} };
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ export async function saveDevice(
|
|||||||
if (!update["$addToSet"]["_tags"])
|
if (!update["$addToSet"]["_tags"])
|
||||||
update["$addToSet"]["_tags"] = { $each: [] };
|
update["$addToSet"]["_tags"] = { $each: [] };
|
||||||
update["$addToSet"]["_tags"]["$each"].push(
|
update["$addToSet"]["_tags"]["$each"].push(
|
||||||
decodeTag(path.segments[1] as string)
|
decodeTag(path.segments[1] as string),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (!update["$pull"]["_tags"]) {
|
if (!update["$pull"]["_tags"]) {
|
||||||
@@ -366,7 +366,7 @@ export async function saveDevice(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
update["$pull"]["_tags"]["$in"].push(
|
update["$pull"]["_tags"]["$in"].push(
|
||||||
decodeTag(path.segments[1] as string)
|
decodeTag(path.segments[1] as string),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,10 @@ export async function saveDevice(
|
|||||||
if (pathStr.endsWith(INVALID_PATH_SUFFIX)) {
|
if (pathStr.endsWith(INVALID_PATH_SUFFIX)) {
|
||||||
const splits = pathStr.split(".");
|
const splits = pathStr.split(".");
|
||||||
splits[splits.length - 1] = decodeTag(
|
splits[splits.length - 1] = decodeTag(
|
||||||
splits[splits.length - 1].slice(0, 0 - INVALID_PATH_SUFFIX.length)
|
splits[splits.length - 1].slice(
|
||||||
|
0,
|
||||||
|
0 - INVALID_PATH_SUFFIX.length,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
pathStr = splits.join(".");
|
pathStr = splits.join(".");
|
||||||
}
|
}
|
||||||
@@ -401,7 +404,7 @@ export async function saveDevice(
|
|||||||
Number.isInteger(value2 as number)
|
Number.isInteger(value2 as number)
|
||||||
) {
|
) {
|
||||||
update["$set"][path.toString() + "._value"] = new Date(
|
update["$set"][path.toString() + "._value"] = new Date(
|
||||||
value2 as number
|
value2 as number,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
update["$set"][path.toString() + "._value"] = value2;
|
update["$set"][path.toString() + "._value"] = value2;
|
||||||
@@ -413,7 +416,7 @@ export async function saveDevice(
|
|||||||
|
|
||||||
if (valueTimestamp2 !== valueTimestamp1) {
|
if (valueTimestamp2 !== valueTimestamp1) {
|
||||||
update["$set"][path.toString() + "._timestamp"] = new Date(
|
update["$set"][path.toString() + "._timestamp"] = new Date(
|
||||||
valueTimestamp2
|
valueTimestamp2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,7 +536,7 @@ export async function saveDevice(
|
|||||||
update,
|
update,
|
||||||
{
|
{
|
||||||
upsert: isNew,
|
upsert: isNew,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!result.matchedCount && !result.upsertedCount)
|
if (!result.matchedCount && !result.upsertedCount)
|
||||||
@@ -546,7 +549,7 @@ export async function saveDevice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getFaults(
|
export async function getFaults(
|
||||||
deviceId: string
|
deviceId: string,
|
||||||
): Promise<{ [channel: string]: SessionFault }> {
|
): Promise<{ [channel: string]: SessionFault }> {
|
||||||
const res = await collections.faults
|
const res = await collections.faults
|
||||||
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
|
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
|
||||||
@@ -573,7 +576,7 @@ export async function getFaults(
|
|||||||
export async function saveFault(
|
export async function saveFault(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
channel: string,
|
channel: string,
|
||||||
fault: SessionFault
|
fault: SessionFault,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const id = `${deviceId}:${channel}`;
|
const id = `${deviceId}:${channel}`;
|
||||||
const f: MongoTypes.Fault = {
|
const f: MongoTypes.Fault = {
|
||||||
@@ -593,14 +596,14 @@ export async function saveFault(
|
|||||||
|
|
||||||
export async function deleteFault(
|
export async function deleteFault(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
channel: string
|
channel: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await collections.faults.deleteOne({ _id: `${deviceId}:${channel}` });
|
await collections.faults.deleteOne({ _id: `${deviceId}:${channel}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDueTasks(
|
export async function getDueTasks(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
timestamp: number
|
timestamp: number,
|
||||||
): Promise<[Task[], number]> {
|
): Promise<[Task[], number]> {
|
||||||
const cur = collections.tasks
|
const cur = collections.tasks
|
||||||
.find({ device: deviceId })
|
.find({ device: deviceId })
|
||||||
@@ -664,7 +667,7 @@ export async function getDueTasks(
|
|||||||
|
|
||||||
export async function clearTasks(
|
export async function clearTasks(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
taskIds: string[]
|
taskIds: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await collections.tasks.deleteMany({
|
await collections.tasks.deleteMany({
|
||||||
_id: { $in: taskIds.map((id) => new ObjectId(id)) },
|
_id: { $in: taskIds.map((id) => new ObjectId(id)) },
|
||||||
@@ -672,7 +675,7 @@ export async function clearTasks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getOperations(
|
export async function getOperations(
|
||||||
deviceId: string
|
deviceId: string,
|
||||||
): Promise<{ [commandKey: string]: Operation }> {
|
): Promise<{ [commandKey: string]: Operation }> {
|
||||||
const res = await collections.operations
|
const res = await collections.operations
|
||||||
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
|
.find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
|
||||||
@@ -704,7 +707,7 @@ export async function getOperations(
|
|||||||
export async function saveOperation(
|
export async function saveOperation(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
commandKey: string,
|
commandKey: string,
|
||||||
operation: Operation
|
operation: Operation,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const id = `${deviceId}:${commandKey}`;
|
const id = `${deviceId}:${commandKey}`;
|
||||||
const o: MongoTypes.Operation = {
|
const o: MongoTypes.Operation = {
|
||||||
@@ -723,7 +726,7 @@ export async function saveOperation(
|
|||||||
|
|
||||||
export async function deleteOperation(
|
export async function deleteOperation(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
commandKey: string
|
commandKey: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await collections.operations.deleteOne({ _id: `${deviceId}:${commandKey}` });
|
await collections.operations.deleteOne({ _id: `${deviceId}:${commandKey}` });
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ interface Snapshot {
|
|||||||
function flattenObject<T extends Record<keyof T, unknown>>(
|
function flattenObject<T extends Record<keyof T, unknown>>(
|
||||||
src: T,
|
src: T,
|
||||||
prefix = "",
|
prefix = "",
|
||||||
dst = {} as T
|
dst = {} as T,
|
||||||
): T {
|
): T {
|
||||||
for (const k of Object.keys(src)) {
|
for (const k of Object.keys(src)) {
|
||||||
const v = src[k];
|
const v = src[k];
|
||||||
@@ -99,7 +99,7 @@ async function fetchPresets(): Promise<[string, Preset[]]> {
|
|||||||
precondition = parse(preset["precondition"]);
|
precondition = parse(preset["precondition"]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
precondition = convertOldPrecondition(
|
precondition = convertOldPrecondition(
|
||||||
JSON.parse(preset["precondition"])
|
JSON.parse(preset["precondition"]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ async function fetchPresets(): Promise<[string, Preset[]]> {
|
|||||||
default: {
|
default: {
|
||||||
const exhaustiveCheck: never = c;
|
const exhaustiveCheck: never = c;
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown configuration type ${exhaustiveCheck["type"]}`
|
`Unknown configuration type ${exhaustiveCheck["type"]}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ async function fetchProvisions(): Promise<[string, Provisions]> {
|
|||||||
.digest("hex");
|
.digest("hex");
|
||||||
provisions[r._id].script = new vm.Script(
|
provisions[r._id].script = new vm.Script(
|
||||||
`"use strict";(function(){\n${r.script}\n})();`,
|
`"use strict";(function(){\n${r.script}\n})();`,
|
||||||
{ filename: r._id, lineOffset: -1 }
|
{ filename: r._id, lineOffset: -1 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ async function fetchVirtualParameters(): Promise<[string, VirtualParameters]> {
|
|||||||
.digest("hex");
|
.digest("hex");
|
||||||
virtualParameters[r._id].script = new vm.Script(
|
virtualParameters[r._id].script = new vm.Script(
|
||||||
`"use strict";(function(){\n${r.script}\n})();`,
|
`"use strict";(function(){\n${r.script}\n})();`,
|
||||||
{ filename: r._id, lineOffset: -1 }
|
{ filename: r._id, lineOffset: -1 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ export function getConfig(
|
|||||||
key: string,
|
key: string,
|
||||||
context: Record<string, unknown>,
|
context: Record<string, unknown>,
|
||||||
now: number,
|
now: number,
|
||||||
cb?: (e: Expression) => Expression
|
cb?: (e: Expression) => Expression,
|
||||||
): string | number | boolean | null {
|
): string | number | boolean | null {
|
||||||
const snapshot = localCache.get(snapshotKey);
|
const snapshot = localCache.get(snapshotKey);
|
||||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||||
@@ -350,7 +350,7 @@ export function getConfig(
|
|||||||
|
|
||||||
export function getConfigExpression(
|
export function getConfigExpression(
|
||||||
snapshotKey: string,
|
snapshotKey: string,
|
||||||
key: string
|
key: string,
|
||||||
): Expression {
|
): Expression {
|
||||||
const snapshot = localCache.get(snapshotKey);
|
const snapshot = localCache.get(snapshotKey);
|
||||||
return snapshot.config[key];
|
return snapshot.config[key];
|
||||||
|
|||||||
+25
-22
@@ -131,7 +131,7 @@ function roundOid(oid: string, roundUp: boolean): string {
|
|||||||
|
|
||||||
function groupBy<T, K>(
|
function groupBy<T, K>(
|
||||||
input: T[],
|
input: T[],
|
||||||
callback: (item: T) => K
|
callback: (item: T) => K,
|
||||||
): Iterable<[K, T[]]> {
|
): Iterable<[K, T[]]> {
|
||||||
const groups = new Map<K, T[]>();
|
const groups = new Map<K, T[]>();
|
||||||
for (const item of input) {
|
for (const item of input) {
|
||||||
@@ -155,7 +155,7 @@ abstract class MongoClause {
|
|||||||
class MongoClauseArray extends MongoClause {
|
class MongoClauseArray extends MongoClause {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly parameter: string,
|
public readonly parameter: string,
|
||||||
public readonly value: string
|
public readonly value: string,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ class MongoClauseCompare<T> extends MongoClause {
|
|||||||
public readonly parameter: string,
|
public readonly parameter: string,
|
||||||
public readonly op: "$eq" | "$gt" | "$lt" | "$gte" | "$lte",
|
public readonly op: "$eq" | "$gt" | "$lt" | "$gte" | "$lte",
|
||||||
public readonly value: T,
|
public readonly value: T,
|
||||||
public readonly type: "" | "bool" | "number" | "string" | "date" | "oid"
|
public readonly type: "" | "bool" | "number" | "string" | "date" | "oid",
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,10 @@ class MongoClauseCompare<T> extends MongoClause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MongoClauseType extends MongoClause {
|
class MongoClauseType extends MongoClause {
|
||||||
constructor(public readonly parameter: string, public readonly type: string) {
|
constructor(
|
||||||
|
public readonly parameter: string,
|
||||||
|
public readonly type: string,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +208,7 @@ class MongoClauseLike extends MongoClause {
|
|||||||
public readonly parameter: string,
|
public readonly parameter: string,
|
||||||
pat: string,
|
pat: string,
|
||||||
esc: string,
|
esc: string,
|
||||||
public readonly caseSensitive: boolean
|
public readonly caseSensitive: boolean,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.pattern = parseLikePattern(pat, esc);
|
this.pattern = parseLikePattern(pat, esc);
|
||||||
@@ -357,8 +360,8 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
param,
|
param,
|
||||||
"$gte",
|
"$gte",
|
||||||
"000000000000000000000000",
|
"000000000000000000000000",
|
||||||
"oid"
|
"oid",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,30 +420,30 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
|
|
||||||
for (const [parameter, clauses2] of groupBy(clauses, (c) => c.parameter)) {
|
for (const [parameter, clauses2] of groupBy(clauses, (c) => c.parameter)) {
|
||||||
const comparisons = clauses2.filter(
|
const comparisons = clauses2.filter(
|
||||||
(c) => c instanceof MongoClauseCompare && c.value !== null
|
(c) => c instanceof MongoClauseCompare && c.value !== null,
|
||||||
) as MongoClauseCompare<unknown>[];
|
) as MongoClauseCompare<unknown>[];
|
||||||
|
|
||||||
for (const [type, clauses3] of groupBy(comparisons, (c) => c.type)) {
|
for (const [type, clauses3] of groupBy(comparisons, (c) => c.type)) {
|
||||||
const isType = this.getVar(new MongoClauseType(parameter, type));
|
const isType = this.getVar(new MongoClauseType(parameter, type));
|
||||||
const values = new Set(clauses3.map((c) => c.value));
|
const values = new Set(clauses3.map((c) => c.value));
|
||||||
const valuesSorted = Array.from(values).sort((a, b) =>
|
const valuesSorted = Array.from(values).sort((a, b) =>
|
||||||
a > b ? 1 : -1
|
a > b ? 1 : -1,
|
||||||
);
|
);
|
||||||
for (const [i, v] of valuesSorted.entries()) {
|
for (const [i, v] of valuesSorted.entries()) {
|
||||||
const eq = this.getVar(
|
const eq = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$eq", v, type)
|
new MongoClauseCompare(parameter, "$eq", v, type),
|
||||||
);
|
);
|
||||||
const gt = this.getVar(
|
const gt = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$gt", v, type)
|
new MongoClauseCompare(parameter, "$gt", v, type),
|
||||||
);
|
);
|
||||||
const lt = this.getVar(
|
const lt = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$lt", v, type)
|
new MongoClauseCompare(parameter, "$lt", v, type),
|
||||||
);
|
);
|
||||||
const gte = this.getVar(
|
const gte = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$gte", v, type)
|
new MongoClauseCompare(parameter, "$gte", v, type),
|
||||||
);
|
);
|
||||||
const lte = this.getVar(
|
const lte = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$lte", v, type)
|
new MongoClauseCompare(parameter, "$lte", v, type),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (type === "bool") {
|
if (type === "bool") {
|
||||||
@@ -472,16 +475,16 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
parameter,
|
parameter,
|
||||||
"$gt",
|
"$gt",
|
||||||
valuesSorted[i - 1],
|
valuesSorted[i - 1],
|
||||||
type
|
type,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const lte2 = this.getVar(
|
const lte2 = this.getVar(
|
||||||
new MongoClauseCompare(
|
new MongoClauseCompare(
|
||||||
parameter,
|
parameter,
|
||||||
"$lte",
|
"$lte",
|
||||||
valuesSorted[i - 1],
|
valuesSorted[i - 1],
|
||||||
type
|
type,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
dcSet.push([(gt2 << 1) ^ 0, (gte << 1) ^ 1]);
|
dcSet.push([(gt2 << 1) ^ 0, (gte << 1) ^ 1]);
|
||||||
dcSet.push([(gt2 << 1) ^ 0, (lte2 << 1) ^ 0, (lt << 1) ^ 1]);
|
dcSet.push([(gt2 << 1) ^ 0, (lte2 << 1) ^ 0, (lt << 1) ^ 1]);
|
||||||
@@ -493,7 +496,7 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const likes = clauses2.filter(
|
const likes = clauses2.filter(
|
||||||
(c) => c instanceof MongoClauseLike
|
(c) => c instanceof MongoClauseLike,
|
||||||
) as MongoClauseLike[];
|
) as MongoClauseLike[];
|
||||||
if (likes.length) {
|
if (likes.length) {
|
||||||
const isType = this.getVar(new MongoClauseType(parameter, "string"));
|
const isType = this.getVar(new MongoClauseType(parameter, "string"));
|
||||||
@@ -535,11 +538,11 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isNull = this.getVar(
|
const isNull = this.getVar(
|
||||||
new MongoClauseCompare(parameter, "$eq", null, "")
|
new MongoClauseCompare(parameter, "$eq", null, ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
const types = getTypes(parameter, this.collection).map((t) =>
|
const types = getTypes(parameter, this.collection).map((t) =>
|
||||||
this.getVar(new MongoClauseType(parameter, t))
|
this.getVar(new MongoClauseType(parameter, t)),
|
||||||
);
|
);
|
||||||
|
|
||||||
dcSet.push([(isNull << 1) ^ 0, ...types.map((t) => (t << 1) ^ 0)]);
|
dcSet.push([(isNull << 1) ^ 0, ...types.map((t) => (t << 1) ^ 0)]);
|
||||||
@@ -621,7 +624,7 @@ class MongoSynthContext extends SynthContextBase<Clause, MongoClause> {
|
|||||||
|
|
||||||
export function toMongoQuery(
|
export function toMongoQuery(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
resource: string
|
resource: string,
|
||||||
): Filter<unknown> | false {
|
): Filter<unknown> | false {
|
||||||
exp = normalize(exp);
|
exp = normalize(exp);
|
||||||
const clause = Clause.fromExpression(normalize(exp));
|
const clause = Clause.fromExpression(normalize(exp));
|
||||||
|
|||||||
+6
-6
@@ -41,7 +41,7 @@ function getConnectionTimestamp(connection: Socket): Date {
|
|||||||
export function incomingHttpRequest(
|
export function incomingHttpRequest(
|
||||||
httpRequest: IncomingMessage,
|
httpRequest: IncomingMessage,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
body: string
|
body: string,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -70,7 +70,7 @@ export function incomingHttpRequest(
|
|||||||
export function outgoingHttpResponse(
|
export function outgoingHttpResponse(
|
||||||
httpResponse: ServerResponse,
|
httpResponse: ServerResponse,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
body: string
|
body: string,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -99,7 +99,7 @@ export function outgoingHttpRequest(
|
|||||||
deviceId: string,
|
deviceId: string,
|
||||||
method: "GET" | "PUT" | "POST" | "DELETE",
|
method: "GET" | "PUT" | "POST" | "DELETE",
|
||||||
url: URL,
|
url: URL,
|
||||||
body: string
|
body: string,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -129,7 +129,7 @@ export function outgoingHttpRequestError(
|
|||||||
deviceId: string,
|
deviceId: string,
|
||||||
method: "GET" | "PUT" | "POST" | "DELETE",
|
method: "GET" | "PUT" | "POST" | "DELETE",
|
||||||
url: URL,
|
url: URL,
|
||||||
err: Error
|
err: Error,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -156,7 +156,7 @@ export function outgoingHttpRequestError(
|
|||||||
export function incomingHttpResponse(
|
export function incomingHttpResponse(
|
||||||
httpResponse: IncomingMessage,
|
httpResponse: IncomingMessage,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
body: string
|
body: string,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -183,7 +183,7 @@ export function outgoingUdpMessage(
|
|||||||
remoteAddress: string,
|
remoteAddress: string,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
remotePort: number,
|
remotePort: number,
|
||||||
body: string
|
body: string,
|
||||||
): void {
|
): void {
|
||||||
if (!DEBUG_FILE) return;
|
if (!DEBUG_FILE) return;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const MAX_DEPTH = +config.get("MAX_DEPTH");
|
|||||||
export function refresh(
|
export function refresh(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
(provision.length !== 2 || typeof provision[1] !== "string") &&
|
(provision.length !== 2 || typeof provision[1] !== "string") &&
|
||||||
@@ -84,7 +84,7 @@ export function refresh(
|
|||||||
export function value(
|
export function value(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
provision.length < 3 ||
|
provision.length < 3 ||
|
||||||
@@ -127,7 +127,7 @@ export function value(
|
|||||||
export function tag(
|
export function tag(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
provision.length !== 3 ||
|
provision.length !== 3 ||
|
||||||
@@ -151,7 +151,7 @@ export function tag(
|
|||||||
export function reboot(
|
export function reboot(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (provision.length !== 1) throw new Error("Invalid arguments");
|
if (provision.length !== 1) throw new Error("Invalid arguments");
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ export function reboot(
|
|||||||
export function reset(
|
export function reset(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (provision.length !== 1) throw new Error("Invalid arguments");
|
if (provision.length !== 1) throw new Error("Invalid arguments");
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ export function reset(
|
|||||||
export function download(
|
export function download(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[]
|
declarations: Declaration[],
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
(provision.length !== 3 ||
|
(provision.length !== 3 ||
|
||||||
@@ -234,7 +234,7 @@ export function instances(
|
|||||||
provision: (string | number | boolean)[],
|
provision: (string | number | boolean)[],
|
||||||
declarations: Declaration[],
|
declarations: Declaration[],
|
||||||
startRevision: number,
|
startRevision: number,
|
||||||
endRevision: number
|
endRevision: number,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (provision.length !== 3 || typeof provision[1] !== "string")
|
if (provision.length !== 3 || typeof provision[1] !== "string")
|
||||||
throw new Error("Invalid arguments");
|
throw new Error("Invalid arguments");
|
||||||
@@ -260,7 +260,7 @@ export function instances(
|
|||||||
const unpacked = device.unpack(
|
const unpacked = device.unpack(
|
||||||
sessionContext.deviceData,
|
sessionContext.deviceData,
|
||||||
path,
|
path,
|
||||||
startRevision + 1
|
startRevision + 1,
|
||||||
);
|
);
|
||||||
count = Math.max(0, unpacked.length + count);
|
count = Math.max(0, unpacked.length + count);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -43,7 +43,7 @@ function parseBool(v): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeParameterValue(
|
export function sanitizeParameterValue(
|
||||||
parameterValue: [string | number | boolean, string]
|
parameterValue: [string | number | boolean, string],
|
||||||
): [string | number | boolean, string] {
|
): [string | number | boolean, string] {
|
||||||
if (parameterValue[0] != null) {
|
if (parameterValue[0] != null) {
|
||||||
switch (parameterValue[1]) {
|
switch (parameterValue[1]) {
|
||||||
@@ -87,7 +87,7 @@ export function sanitizeParameterValue(
|
|||||||
export function getAliasDeclarations(
|
export function getAliasDeclarations(
|
||||||
path: Path,
|
path: Path,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
attrGet = null
|
attrGet = null,
|
||||||
): Declaration[] {
|
): Declaration[] {
|
||||||
const stripped = path.stripAlias();
|
const stripped = path.stripAlias();
|
||||||
let decs: Declaration[] = [
|
let decs: Declaration[] = [
|
||||||
@@ -109,7 +109,7 @@ export function getAliasDeclarations(
|
|||||||
decs = decs.concat(
|
decs = decs.concat(
|
||||||
getAliasDeclarations(parent.concat(p), timestamp, {
|
getAliasDeclarations(parent.concat(p), timestamp, {
|
||||||
value: timestamp,
|
value: timestamp,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ export function getAliasDeclarations(
|
|||||||
export function unpack(
|
export function unpack(
|
||||||
deviceData: DeviceData,
|
deviceData: DeviceData,
|
||||||
path: Path,
|
path: Path,
|
||||||
revision?: number
|
revision?: number,
|
||||||
): Path[] {
|
): Path[] {
|
||||||
let allMatches = [] as Path[];
|
let allMatches = [] as Path[];
|
||||||
if (!path.alias) {
|
if (!path.alias) {
|
||||||
@@ -194,7 +194,7 @@ export function clear(
|
|||||||
path: Path,
|
path: Path,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
attributes: AttributeTimestamps,
|
attributes: AttributeTimestamps,
|
||||||
changeFlags = 0
|
changeFlags = 0,
|
||||||
): void {
|
): void {
|
||||||
const changeTrackers = {};
|
const changeTrackers = {};
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ export function clear(
|
|||||||
path,
|
path,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
descendantsTimestamp ? 99 : path.length
|
descendantsTimestamp ? 99 : path.length,
|
||||||
)) {
|
)) {
|
||||||
const tracker = deviceData.trackers.get(p);
|
const tracker = deviceData.trackers.get(p);
|
||||||
for (const k in tracker) changeTrackers[k] |= tracker[k];
|
for (const k in tracker) changeTrackers[k] |= tracker[k];
|
||||||
@@ -274,7 +274,7 @@ export function set(
|
|||||||
path: Path,
|
path: Path,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
toClear?: Clear[]
|
toClear?: Clear[],
|
||||||
): Clear[] {
|
): Clear[] {
|
||||||
path = deviceData.paths.add(path);
|
path = deviceData.paths.add(path);
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ export function set(
|
|||||||
else if (
|
else if (
|
||||||
!compareEquality(
|
!compareEquality(
|
||||||
attributes[attrName][1],
|
attributes[attrName][1],
|
||||||
currentAttributes[attrName][1]
|
currentAttributes[attrName][1],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
changeFlags |= CHANGE_FLAGS[attrName];
|
changeFlags |= CHANGE_FLAGS[attrName];
|
||||||
@@ -334,7 +334,7 @@ export function set(
|
|||||||
path.slice(0, path.length - 1),
|
path.slice(0, path.length - 1),
|
||||||
timestamp,
|
timestamp,
|
||||||
{ object: [timestamp, 1] },
|
{ object: [timestamp, 1] },
|
||||||
toClear
|
toClear,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ export function track(
|
|||||||
deviceData: DeviceData,
|
deviceData: DeviceData,
|
||||||
path: Path,
|
path: Path,
|
||||||
marker: string,
|
marker: string,
|
||||||
attributes?: string[]
|
attributes?: string[],
|
||||||
): void {
|
): void {
|
||||||
path = deviceData.paths.add(path);
|
path = deviceData.paths.add(path);
|
||||||
let f = 1;
|
let f = 1;
|
||||||
@@ -396,7 +396,7 @@ export function track(
|
|||||||
|
|
||||||
export function clearTrackers(
|
export function clearTrackers(
|
||||||
deviceData: DeviceData,
|
deviceData: DeviceData,
|
||||||
tracker: string | string[]
|
tracker: string | string[],
|
||||||
): void {
|
): void {
|
||||||
if (Array.isArray(tracker)) {
|
if (Array.isArray(tracker)) {
|
||||||
for (const v of deviceData.trackers.values())
|
for (const v of deviceData.trackers.values())
|
||||||
|
|||||||
+1
-1
@@ -123,6 +123,6 @@ export async function killAll(): Promise<void> {
|
|||||||
Object.entries(processes).map(([k, p]) => {
|
Object.entries(processes).map(([k, p]) => {
|
||||||
delete processes[k];
|
delete processes[k];
|
||||||
return kill(p);
|
return kill(p);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ const getFile = memoize(
|
|||||||
async (
|
async (
|
||||||
etag: string,
|
etag: string,
|
||||||
size: number,
|
size: number,
|
||||||
filename: string
|
filename: string,
|
||||||
): Promise<Iterable<Buffer>> => {
|
): Promise<Iterable<Buffer>> => {
|
||||||
const chunks: Buffer[] = [];
|
const chunks: Buffer[] = [];
|
||||||
// Using for-await over the download stream can throw ERR_STREAM_PREMATURE_CLOSE
|
// Using for-await over the download stream can throw ERR_STREAM_PREMATURE_CLOSE
|
||||||
@@ -41,7 +41,7 @@ const getFile = memoize(
|
|||||||
new PassThrough(),
|
new PassThrough(),
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
for await (const chunk of downloadStream) chunks.push(chunk);
|
for await (const chunk of downloadStream) chunks.push(chunk);
|
||||||
// Node 12-14 don't throw error when stream is closed prematurely.
|
// Node 12-14 don't throw error when stream is closed prematurely.
|
||||||
@@ -49,13 +49,13 @@ const getFile = memoize(
|
|||||||
if (size !== chunks.reduce((a, b) => a + b.length, 0))
|
if (size !== chunks.reduce((a, b) => a + b.length, 0))
|
||||||
throw new Error("File size mismatch");
|
throw new Error("File size mismatch");
|
||||||
return chunks;
|
return chunks;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
async function* partialContent(
|
async function* partialContent(
|
||||||
chunks: Iterable<Buffer>,
|
chunks: Iterable<Buffer>,
|
||||||
start: number,
|
start: number,
|
||||||
end: number
|
end: number,
|
||||||
): AsyncIterable<Buffer> {
|
): AsyncIterable<Buffer> {
|
||||||
let bytesToSkip = start;
|
let bytesToSkip = start;
|
||||||
let bytesToRead = end - start;
|
let bytesToRead = end - start;
|
||||||
@@ -100,7 +100,7 @@ function matchEtag(etag: string, header: string): boolean {
|
|||||||
|
|
||||||
export async function listener(
|
export async function listener(
|
||||||
request: IncomingMessage,
|
request: IncomingMessage,
|
||||||
response: ServerResponse
|
response: ServerResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (request.method !== "GET" && request.method !== "HEAD") {
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
||||||
response.writeHead(405, { Allow: "GET, HEAD" });
|
response.writeHead(405, { Allow: "GET, HEAD" });
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const UNDISCOVERED_DEPTH = 7;
|
|||||||
// point has been discovered.
|
// point has been discovered.
|
||||||
export function estimateGpnCount(
|
export function estimateGpnCount(
|
||||||
gpnPatterns: [Path, number][],
|
gpnPatterns: [Path, number][],
|
||||||
depth = 0
|
depth = 0,
|
||||||
): number {
|
): number {
|
||||||
const children: { [segment: string]: [Path, number][] } = {};
|
const children: { [segment: string]: [Path, number][] } = {};
|
||||||
const wildcardChildren: [Path, number][] = [];
|
const wildcardChildren: [Path, number][] = [];
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ export class LocalCache<T> {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cacheKey: string,
|
private cacheKey: string,
|
||||||
private callback: () => Promise<[string, T]>
|
private callback: () => Promise<[string, T]>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getRevision(): Promise<string> {
|
async getRevision(): Promise<string> {
|
||||||
|
|||||||
+3
-3
@@ -6,7 +6,7 @@ export async function acquireLock(
|
|||||||
lockName: string,
|
lockName: string,
|
||||||
ttl: number,
|
ttl: number,
|
||||||
timeout = 0,
|
timeout = 0,
|
||||||
token = Math.random().toString(36).slice(2)
|
token = Math.random().toString(36).slice(2),
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -18,7 +18,7 @@ export async function acquireLock(
|
|||||||
},
|
},
|
||||||
$currentDate: { timestamp: true },
|
$currentDate: { timestamp: true },
|
||||||
},
|
},
|
||||||
{ upsert: true, returnDocument: "after" }
|
{ upsert: true, returnDocument: "after" },
|
||||||
);
|
);
|
||||||
if (Math.abs(r.value.timestamp.getTime() - now) > CLOCK_SKEW_TOLERANCE)
|
if (Math.abs(r.value.timestamp.getTime() - now) > CLOCK_SKEW_TOLERANCE)
|
||||||
throw new Error("Database clock skew too great");
|
throw new Error("Database clock skew too great");
|
||||||
@@ -35,7 +35,7 @@ export async function acquireLock(
|
|||||||
|
|
||||||
export async function releaseLock(
|
export async function releaseLock(
|
||||||
lockName: string,
|
lockName: string,
|
||||||
token: string
|
token: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const res = await collections.locks.deleteOne({
|
const res = await collections.locks.deleteOne({
|
||||||
_id: lockName,
|
_id: lockName,
|
||||||
|
|||||||
+5
-5
@@ -156,13 +156,13 @@ export function close(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function flatten(
|
export function flatten(
|
||||||
details: Record<string, unknown>
|
details: Record<string, unknown>,
|
||||||
): Record<string, unknown> {
|
): Record<string, unknown> {
|
||||||
if (details.sessionContext) {
|
if (details.sessionContext) {
|
||||||
const sessionContext = details.sessionContext as SessionContext;
|
const sessionContext = details.sessionContext as SessionContext;
|
||||||
details.deviceId = sessionContext.deviceId;
|
details.deviceId = sessionContext.deviceId;
|
||||||
details.remoteAddress = getRequestOrigin(
|
details.remoteAddress = getRequestOrigin(
|
||||||
sessionContext.httpRequest
|
sessionContext.httpRequest,
|
||||||
).remoteAddress;
|
).remoteAddress;
|
||||||
delete details.sessionContext;
|
delete details.sessionContext;
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ export function flatten(
|
|||||||
// For genieacs-ui
|
// For genieacs-ui
|
||||||
if (details.context) {
|
if (details.context) {
|
||||||
details.remoteAddress = getRequestOrigin(
|
details.remoteAddress = getRequestOrigin(
|
||||||
details.context["req"]
|
details.context["req"],
|
||||||
).remoteAddress;
|
).remoteAddress;
|
||||||
if (details.context["state"].user)
|
if (details.context["state"].user)
|
||||||
details.user = details.context["state"].user.username;
|
details.user = details.context["state"].user.username;
|
||||||
@@ -235,7 +235,7 @@ export function flatten(
|
|||||||
|
|
||||||
function formatJson(
|
function formatJson(
|
||||||
details: Record<string, unknown>,
|
details: Record<string, unknown>,
|
||||||
systemd: boolean
|
systemd: boolean,
|
||||||
): string {
|
): string {
|
||||||
if (systemd) {
|
if (systemd) {
|
||||||
let severity = "";
|
let severity = "";
|
||||||
@@ -251,7 +251,7 @@ function formatJson(
|
|||||||
|
|
||||||
function formatSimple(
|
function formatSimple(
|
||||||
details: Record<string, unknown>,
|
details: Record<string, unknown>,
|
||||||
systemd: boolean
|
systemd: boolean,
|
||||||
): string {
|
): string {
|
||||||
const skip = {
|
const skip = {
|
||||||
user: true,
|
user: true,
|
||||||
|
|||||||
+18
-18
@@ -65,14 +65,14 @@ async function getBody(request: IncomingMessage): Promise<Buffer> {
|
|||||||
|
|
||||||
export async function listener(
|
export async function listener(
|
||||||
request: IncomingMessage,
|
request: IncomingMessage,
|
||||||
response: ServerResponse
|
response: ServerResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
response.setHeader("GenieACS-Version", VERSION);
|
response.setHeader("GenieACS-Version", VERSION);
|
||||||
|
|
||||||
const origin = getRequestOrigin(request);
|
const origin = getRequestOrigin(request);
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
request.url,
|
request.url,
|
||||||
(origin.encrypted ? "https://" : "http://") + origin.host
|
(origin.encrypted ? "https://" : "http://") + origin.host,
|
||||||
);
|
);
|
||||||
|
|
||||||
const body = await getBody(request).catch(() => null);
|
const body = await getBody(request).catch(() => null);
|
||||||
@@ -83,7 +83,7 @@ export async function listener(
|
|||||||
Object.assign({}, Object.fromEntries(url.searchParams), {
|
Object.assign({}, Object.fromEntries(url.searchParams), {
|
||||||
remoteAddress: origin.remoteAddress,
|
remoteAddress: origin.remoteAddress,
|
||||||
message: `${request.method} ${url.pathname}`,
|
message: `${request.method} ${url.pathname}`,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
return handler(request, response, url, body);
|
return handler(request, response, url, body);
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ async function handler(
|
|||||||
request: IncomingMessage,
|
request: IncomingMessage,
|
||||||
response: ServerResponse,
|
response: ServerResponse,
|
||||||
url: URL,
|
url: URL,
|
||||||
body: Buffer
|
body: Buffer,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (PRESETS_REGEX.test(url.pathname)) {
|
if (PRESETS_REGEX.test(url.pathname)) {
|
||||||
const presetName = decodeURIComponent(PRESETS_REGEX.exec(url.pathname)[1]);
|
const presetName = decodeURIComponent(PRESETS_REGEX.exec(url.pathname)[1]);
|
||||||
@@ -150,7 +150,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
} else if (PROVISIONS_REGEX.test(url.pathname)) {
|
} else if (PROVISIONS_REGEX.test(url.pathname)) {
|
||||||
const provisionName = decodeURIComponent(
|
const provisionName = decodeURIComponent(
|
||||||
PROVISIONS_REGEX.exec(url.pathname)[1]
|
PROVISIONS_REGEX.exec(url.pathname)[1],
|
||||||
);
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const object = {
|
const object = {
|
||||||
@@ -183,7 +183,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
} else if (VIRTUAL_PARAMETERS_REGEX.test(url.pathname)) {
|
} else if (VIRTUAL_PARAMETERS_REGEX.test(url.pathname)) {
|
||||||
const virtualParameterName = decodeURIComponent(
|
const virtualParameterName = decodeURIComponent(
|
||||||
VIRTUAL_PARAMETERS_REGEX.exec(url.pathname)[1]
|
VIRTUAL_PARAMETERS_REGEX.exec(url.pathname)[1],
|
||||||
);
|
);
|
||||||
if (request.method === "PUT") {
|
if (request.method === "PUT") {
|
||||||
const object = {
|
const object = {
|
||||||
@@ -202,7 +202,7 @@ async function handler(
|
|||||||
await collections.virtualParameters.replaceOne(
|
await collections.virtualParameters.replaceOne(
|
||||||
{ _id: virtualParameterName },
|
{ _id: virtualParameterName },
|
||||||
object,
|
object,
|
||||||
{ upsert: true }
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
await cache.del("cwmp-local-cache-hash");
|
await cache.del("cwmp-local-cache-hash");
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
@@ -225,7 +225,7 @@ async function handler(
|
|||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
const updateRes = await collections.devices.updateOne(
|
const updateRes = await collections.devices.updateOne(
|
||||||
{ _id: deviceId },
|
{ _id: deviceId },
|
||||||
{ $addToSet: { _tags: tag } }
|
{ $addToSet: { _tags: tag } },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updateRes.matchedCount) {
|
if (!updateRes.matchedCount) {
|
||||||
@@ -239,7 +239,7 @@ async function handler(
|
|||||||
} else if (request.method === "DELETE") {
|
} else if (request.method === "DELETE") {
|
||||||
const updateRes = await collections.devices.updateOne(
|
const updateRes = await collections.devices.updateOne(
|
||||||
{ _id: deviceId },
|
{ _id: deviceId },
|
||||||
{ $pull: { _tags: tag } }
|
{ $pull: { _tags: tag } },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updateRes.matchedCount) {
|
if (!updateRes.matchedCount) {
|
||||||
@@ -277,7 +277,7 @@ async function handler(
|
|||||||
} else if (DEVICE_TASKS_REGEX.test(url.pathname)) {
|
} else if (DEVICE_TASKS_REGEX.test(url.pathname)) {
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
const deviceId = decodeURIComponent(
|
const deviceId = decodeURIComponent(
|
||||||
DEVICE_TASKS_REGEX.exec(url.pathname)[1]
|
DEVICE_TASKS_REGEX.exec(url.pathname)[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
const conReq = url.searchParams.has("connection_request");
|
const conReq = url.searchParams.has("connection_request");
|
||||||
@@ -314,7 +314,7 @@ async function handler(
|
|||||||
} else {
|
} else {
|
||||||
const status = await apiFunctions.connectionRequest(
|
const status = await apiFunctions.connectionRequest(
|
||||||
deviceId,
|
deviceId,
|
||||||
flattenDevice(dev)
|
flattenDevice(dev),
|
||||||
);
|
);
|
||||||
if (status) {
|
if (status) {
|
||||||
response.writeHead(504, status);
|
response.writeHead(504, status);
|
||||||
@@ -394,7 +394,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
},
|
||||||
) as number;
|
) as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,14 +403,14 @@ async function handler(
|
|||||||
const sessionStarted = await apiFunctions.awaitSessionStart(
|
const sessionStarted = await apiFunctions.awaitSessionStart(
|
||||||
deviceId,
|
deviceId,
|
||||||
lastInform,
|
lastInform,
|
||||||
onlineThreshold
|
onlineThreshold,
|
||||||
);
|
);
|
||||||
if (!sessionStarted) {
|
if (!sessionStarted) {
|
||||||
status = "Task queued but not processed";
|
status = "Task queued but not processed";
|
||||||
} else {
|
} else {
|
||||||
const sessionEnded = await apiFunctions.awaitSessionEnd(
|
const sessionEnded = await apiFunctions.awaitSessionEnd(
|
||||||
deviceId,
|
deviceId,
|
||||||
120000
|
120000,
|
||||||
);
|
);
|
||||||
if (!sessionEnded) {
|
if (!sessionEnded) {
|
||||||
status = "Task queued but not processed";
|
status = "Task queued but not processed";
|
||||||
@@ -445,7 +445,7 @@ async function handler(
|
|||||||
if (request.method === "DELETE") {
|
if (request.method === "DELETE") {
|
||||||
const task = await collections.tasks.findOne(
|
const task = await collections.tasks.findOne(
|
||||||
{ _id: new ObjectId(taskId) },
|
{ _id: new ObjectId(taskId) },
|
||||||
{ projection: { device: 1 } }
|
{ projection: { device: 1 } },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
@@ -481,7 +481,7 @@ async function handler(
|
|||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
const task = await collections.tasks.findOne(
|
const task = await collections.tasks.findOne(
|
||||||
{ _id: new ObjectId(taskId) },
|
{ _id: new ObjectId(taskId) },
|
||||||
{ projection: { device: 1 } }
|
{ projection: { device: 1 } },
|
||||||
);
|
);
|
||||||
|
|
||||||
const deviceId = task.device;
|
const deviceId = task.device;
|
||||||
@@ -530,7 +530,7 @@ async function handler(
|
|||||||
filename,
|
filename,
|
||||||
{
|
{
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
uploadStream.on("error", reject);
|
uploadStream.on("error", reject);
|
||||||
@@ -589,7 +589,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deviceId = decodeURIComponent(
|
const deviceId = decodeURIComponent(
|
||||||
DELETE_DEVICE_REGEX.exec(url.pathname)[1]
|
DELETE_DEVICE_REGEX.exec(url.pathname)[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ export function parsePing(osPlatform: string, stdout: string): PingResult {
|
|||||||
|
|
||||||
export function ping(
|
export function ping(
|
||||||
host: string,
|
host: string,
|
||||||
callback: (err: Error, res?: PingResult, stdout?: string) => void
|
callback: (err: Error, res?: PingResult, stdout?: string) => void,
|
||||||
): void {
|
): void {
|
||||||
// Validate input to prevent possible remote code execution
|
// Validate input to prevent possible remote code execution
|
||||||
// Credit to Alex Hordijk for reporting this vulnerability
|
// Credit to Alex Hordijk for reporting this vulnerability
|
||||||
|
|||||||
+2
-2
@@ -118,7 +118,7 @@ function permute(param, val): any[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function expand(
|
export function expand(
|
||||||
query: Record<string, unknown>
|
query: Record<string, unknown>,
|
||||||
): Record<string, unknown> {
|
): Record<string, unknown> {
|
||||||
const newQuery = {};
|
const newQuery = {};
|
||||||
for (const [k, v] of Object.entries(query)) {
|
for (const [k, v] of Object.entries(query)) {
|
||||||
@@ -147,7 +147,7 @@ export function expand(
|
|||||||
|
|
||||||
export function sanitizeQueryTypes(
|
export function sanitizeQueryTypes(
|
||||||
query: Record<string, unknown>,
|
query: Record<string, unknown>,
|
||||||
types: Record<string, (v: unknown) => unknown>
|
types: Record<string, (v: unknown) => unknown>,
|
||||||
): Record<string, unknown> {
|
): Record<string, unknown> {
|
||||||
for (const [k, v] of Object.entries(query)) {
|
for (const [k, v] of Object.entries(query)) {
|
||||||
if (k[0] === "$") {
|
if (k[0] === "$") {
|
||||||
|
|||||||
+14
-14
@@ -62,7 +62,7 @@ function runExtension(sessionContext, key, extCall): Promise<Fault> {
|
|||||||
resolve(fault);
|
resolve(fault);
|
||||||
})
|
})
|
||||||
.catch(reject);
|
.catch(reject);
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ class SandboxDate {
|
|||||||
number?,
|
number?,
|
||||||
number?,
|
number?,
|
||||||
number?,
|
number?,
|
||||||
number?
|
number?,
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
if (argumentList.length) return new Date(...argumentList);
|
if (argumentList.length) return new Date(...argumentList);
|
||||||
@@ -143,7 +143,7 @@ class ParameterWrapper {
|
|||||||
unpacked = device.unpack(
|
unpacked = device.unpack(
|
||||||
state.sessionContext.deviceData,
|
state.sessionContext.deviceData,
|
||||||
path,
|
path,
|
||||||
state.revision
|
state.revision,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ class ParameterWrapper {
|
|||||||
|
|
||||||
const attr = state.sessionContext.deviceData.attributes.get(
|
const attr = state.sessionContext.deviceData.attributes.get(
|
||||||
unpacked[0],
|
unpacked[0],
|
||||||
state.revision
|
state.revision,
|
||||||
)[attrName];
|
)[attrName];
|
||||||
|
|
||||||
if (!attr) return UNDEFINED;
|
if (!attr) return UNDEFINED;
|
||||||
@@ -170,7 +170,7 @@ class ParameterWrapper {
|
|||||||
unpacked = device.unpack(
|
unpacked = device.unpack(
|
||||||
state.sessionContext.deviceData,
|
state.sessionContext.deviceData,
|
||||||
path,
|
path,
|
||||||
state.revision
|
state.revision,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ class ParameterWrapper {
|
|||||||
unpacked = device.unpack(
|
unpacked = device.unpack(
|
||||||
state.sessionContext.deviceData,
|
state.sessionContext.deviceData,
|
||||||
path,
|
path,
|
||||||
state.revision
|
state.revision,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ class ParameterWrapper {
|
|||||||
unpacked = device.unpack(
|
unpacked = device.unpack(
|
||||||
state.sessionContext.deviceData,
|
state.sessionContext.deviceData,
|
||||||
path,
|
path,
|
||||||
state.revision
|
state.revision,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ class ParameterWrapper {
|
|||||||
function declare(
|
function declare(
|
||||||
path: string,
|
path: string,
|
||||||
timestamps: { [attr: string]: number },
|
timestamps: { [attr: string]: number },
|
||||||
values: { [attr: string]: any }
|
values: { [attr: string]: any },
|
||||||
): ParameterWrapper {
|
): ParameterWrapper {
|
||||||
state.uncommitted = true;
|
state.uncommitted = true;
|
||||||
if (!timestamps) timestamps = {};
|
if (!timestamps) timestamps = {};
|
||||||
@@ -286,7 +286,7 @@ function commit(): void {
|
|||||||
throw COMMIT;
|
throw COMMIT;
|
||||||
} else if (state.revision > state.maxRevision + 1) {
|
} else if (state.revision > state.maxRevision + 1) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Declare function should not be called from within a try/catch block"
|
"Declare function should not be called from within a try/catch block",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,12 +351,12 @@ function errorToFault(err: Error): Fault {
|
|||||||
fault.detail["stack"] = err.stack;
|
fault.detail["stack"] = err.stack;
|
||||||
// Trim the stack trace at the self-executing anonymous wrapper function
|
// Trim the stack trace at the self-executing anonymous wrapper function
|
||||||
const stackTrimIndex = fault.detail["stack"].match(
|
const stackTrimIndex = fault.detail["stack"].match(
|
||||||
/\s+at\s[^\s]+\s+at\s[^\s]+\s\(vm\.js.+\)/
|
/\s+at\s[^\s]+\s+at\s[^\s]+\s\(vm\.js.+\)/,
|
||||||
);
|
);
|
||||||
if (stackTrimIndex) {
|
if (stackTrimIndex) {
|
||||||
fault.detail["stack"] = fault.detail["stack"].slice(
|
fault.detail["stack"] = fault.detail["stack"].slice(
|
||||||
0,
|
0,
|
||||||
stackTrimIndex.index
|
stackTrimIndex.index,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,7 +370,7 @@ export async function run(
|
|||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
startRevision: number,
|
startRevision: number,
|
||||||
maxRevision: number,
|
maxRevision: number,
|
||||||
extCounter = 0
|
extCounter = 0,
|
||||||
): Promise<ScriptResult> {
|
): Promise<ScriptResult> {
|
||||||
state = {
|
state = {
|
||||||
sessionContext: sessionContext,
|
sessionContext: sessionContext,
|
||||||
@@ -415,7 +415,7 @@ export async function run(
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.entries(_state.extensions).map(async ([k, v]) => {
|
Object.entries(_state.extensions).map(async ([k, v]) => {
|
||||||
fault = (await runExtension(_state.sessionContext, k, v)) || fault;
|
fault = (await runExtension(_state.sessionContext, k, v)) || fault;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fault) {
|
if (fault) {
|
||||||
@@ -435,7 +435,7 @@ export async function run(
|
|||||||
sessionContext,
|
sessionContext,
|
||||||
startRevision,
|
startRevision,
|
||||||
maxRevision,
|
maxRevision,
|
||||||
extCounter - _state.extCounter
|
extCounter - _state.extCounter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -37,7 +37,7 @@ export function variance(deviceId: string, vrnc: number): number {
|
|||||||
export function interval(
|
export function interval(
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
intrvl: number,
|
intrvl: number,
|
||||||
offset = 0
|
offset = 0,
|
||||||
): number {
|
): number {
|
||||||
return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset;
|
return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset;
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ export function parseCron(cronExp: string): any {
|
|||||||
export function cron(
|
export function cron(
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
schedule: unknown,
|
schedule: unknown,
|
||||||
offset = 0
|
offset = 0,
|
||||||
): number[] {
|
): number[] {
|
||||||
// TODO later.js doesn't throw erorr if expression is invalid!
|
// TODO later.js doesn't throw erorr if expression is invalid!
|
||||||
const ret = [0, 0];
|
const ret = [0, 0];
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ type Promisify<T extends (...args: any) => any> = (
|
|||||||
|
|
||||||
export function start(
|
export function start(
|
||||||
options: ServerOptions,
|
options: ServerOptions,
|
||||||
_listener: Promisify<http.RequestListener>
|
_listener: Promisify<http.RequestListener>,
|
||||||
): void {
|
): void {
|
||||||
listener = (req, res) => {
|
listener = (req, res) => {
|
||||||
if (stopping) res.setHeader("Connection", "close");
|
if (stopping) res.setHeader("Connection", "close");
|
||||||
|
|||||||
+151
-148
File diff suppressed because it is too large.
Load diff
+18
-18
@@ -158,7 +158,7 @@ const getValueType = memoize((str: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function parameterValueList(
|
function parameterValueList(
|
||||||
xml: Element
|
xml: Element,
|
||||||
): [Path, string | number | boolean, string][] {
|
): [Path, string | number | boolean, string][] {
|
||||||
return xml.children
|
return xml.children
|
||||||
.map<[Path, string | number | boolean, string]>((e) => {
|
.map<[Path, string | number | boolean, string]>((e) => {
|
||||||
@@ -291,7 +291,7 @@ function GetParameterNamesResponse(xml): GetParameterNamesResponse {
|
|||||||
return {
|
return {
|
||||||
name: "GetParameterNamesResponse",
|
name: "GetParameterNamesResponse",
|
||||||
parameterList: parameterInfoList(
|
parameterList: parameterInfoList(
|
||||||
xml.children.find((n) => n.localName === "ParameterList")
|
xml.children.find((n) => n.localName === "ParameterList"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ function GetParameterValuesResponse(xml: Element): GetParameterValuesResponse {
|
|||||||
return {
|
return {
|
||||||
name: "GetParameterValuesResponse",
|
name: "GetParameterValuesResponse",
|
||||||
parameterList: parameterValueList(
|
parameterList: parameterValueList(
|
||||||
xml.children.find((n) => n.localName === "ParameterList")
|
xml.children.find((n) => n.localName === "ParameterList"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -322,12 +322,12 @@ function GetParameterAttributes(methodRequest): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function GetParameterAttributesResponse(
|
function GetParameterAttributesResponse(
|
||||||
xml: Element
|
xml: Element,
|
||||||
): GetParameterAttributesResponse {
|
): GetParameterAttributesResponse {
|
||||||
return {
|
return {
|
||||||
name: "GetParameterAttributesResponse",
|
name: "GetParameterAttributesResponse",
|
||||||
parameterList: parameterAttributeList(
|
parameterList: parameterAttributeList(
|
||||||
xml.children.find((n) => n.localName === "ParameterList")
|
xml.children.find((n) => n.localName === "ParameterList"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -509,19 +509,19 @@ function Download(methodRequest): string {
|
|||||||
}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
|
}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
|
||||||
methodRequest.url
|
methodRequest.url
|
||||||
}</URL><Username>${encodeEntities(
|
}</URL><Username>${encodeEntities(
|
||||||
methodRequest.username || ""
|
methodRequest.username || "",
|
||||||
)}</Username><Password>${encodeEntities(
|
)}</Username><Password>${encodeEntities(
|
||||||
methodRequest.password || ""
|
methodRequest.password || "",
|
||||||
)}</Password><FileSize>${
|
)}</Password><FileSize>${
|
||||||
methodRequest.fileSize || "0"
|
methodRequest.fileSize || "0"
|
||||||
}</FileSize><TargetFileName>${encodeEntities(
|
}</FileSize><TargetFileName>${encodeEntities(
|
||||||
methodRequest.targetFileName || ""
|
methodRequest.targetFileName || "",
|
||||||
)}</TargetFileName><DelaySeconds>${
|
)}</TargetFileName><DelaySeconds>${
|
||||||
methodRequest.delaySeconds || "0"
|
methodRequest.delaySeconds || "0"
|
||||||
}</DelaySeconds><SuccessURL>${encodeEntities(
|
}</DelaySeconds><SuccessURL>${encodeEntities(
|
||||||
methodRequest.successUrl || ""
|
methodRequest.successUrl || "",
|
||||||
)}</SuccessURL><FailureURL>${encodeEntities(
|
)}</SuccessURL><FailureURL>${encodeEntities(
|
||||||
methodRequest.failureUrl || ""
|
methodRequest.failureUrl || "",
|
||||||
)}</FailureURL></cwmp:Download>`;
|
)}</FailureURL></cwmp:Download>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,13 +732,13 @@ function RequestDownloadResponse(): string {
|
|||||||
|
|
||||||
function AcsFault(f: CpeFault): string {
|
function AcsFault(f: CpeFault): string {
|
||||||
return `<soap-env:Body:Fault><faultcode>${encodeEntities(
|
return `<soap-env:Body:Fault><faultcode>${encodeEntities(
|
||||||
f.faultCode
|
f.faultCode,
|
||||||
)}</faultcode><faultstring>${encodeEntities(
|
)}</faultcode><faultstring>${encodeEntities(
|
||||||
f.faultString
|
f.faultString,
|
||||||
)}</faultstring><detail><cwmp:Fault><FaultCode>${encodeEntities(
|
)}</faultstring><detail><cwmp:Fault><FaultCode>${encodeEntities(
|
||||||
f.detail.faultCode
|
f.detail.faultCode,
|
||||||
)}</FaultCode><FaultString>${encodeEntities(
|
)}</FaultCode><FaultString>${encodeEntities(
|
||||||
f.detail.faultString
|
f.detail.faultString,
|
||||||
)}</FaultString></cwmp:Fault></detail></soap-env:Body:Fault>`;
|
)}</FaultString></cwmp:Fault></detail></soap-env:Body:Fault>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,7 +839,7 @@ function fault(xml: Element): CpeFault {
|
|||||||
|
|
||||||
export function request(
|
export function request(
|
||||||
body: string,
|
body: string,
|
||||||
warn: Record<string, unknown>[]
|
warn: Record<string, unknown>[],
|
||||||
): SoapMessage {
|
): SoapMessage {
|
||||||
warnings = warn;
|
warnings = warn;
|
||||||
|
|
||||||
@@ -897,7 +897,7 @@ export function request(
|
|||||||
const attrs = memoizedParseAttrs(e.attrs);
|
const attrs = memoizedParseAttrs(e.attrs);
|
||||||
const attr = namespace
|
const attr = namespace
|
||||||
? attrs.find(
|
? 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");
|
||||||
|
|
||||||
@@ -1030,7 +1030,7 @@ export function response(rpc: {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown method response type ${
|
`Unknown method response type ${
|
||||||
(rpc.acsResponse as AcsResponse).name
|
(rpc.acsResponse as AcsResponse).name
|
||||||
}`
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (rpc.acsRequest) {
|
} else if (rpc.acsRequest) {
|
||||||
@@ -1067,7 +1067,7 @@ export function response(rpc: {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown method request ${(rpc.acsRequest as AcsRequest).name}`
|
`Unknown method request ${(rpc.acsRequest as AcsRequest).name}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (rpc.acsFault) {
|
} else if (rpc.acsFault) {
|
||||||
|
|||||||
+3
-3
@@ -97,7 +97,7 @@ export type VirtualParameterDeclaration = [
|
|||||||
value?: [string | number | boolean, string?];
|
value?: [string | number | boolean, string?];
|
||||||
notification?: number;
|
notification?: number;
|
||||||
accessList?: string[];
|
accessList?: string[];
|
||||||
}?
|
}?,
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface SyncState {
|
export interface SyncState {
|
||||||
@@ -139,7 +139,7 @@ export interface SessionContext {
|
|||||||
AttributeTimestamps,
|
AttributeTimestamps,
|
||||||
AttributeValues,
|
AttributeValues,
|
||||||
AttributeTimestamps,
|
AttributeTimestamps,
|
||||||
AttributeValues
|
AttributeValues,
|
||||||
][][];
|
][][];
|
||||||
revisions: number[];
|
revisions: number[];
|
||||||
rpcCount: number;
|
rpcCount: number;
|
||||||
@@ -455,7 +455,7 @@ export type Clear = [
|
|||||||
notification?: number;
|
notification?: number;
|
||||||
accessList?: number;
|
accessList?: number;
|
||||||
}?,
|
}?,
|
||||||
number?
|
number?,
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface Preset {
|
export interface Preset {
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ const getAuthorizer = memoize(
|
|||||||
const roles: string[] = JSON.parse(rolesStr);
|
const roles: string[] = JSON.parse(rolesStr);
|
||||||
const allPermissions = localCache.getPermissions(snapshot);
|
const allPermissions = localCache.getPermissions(snapshot);
|
||||||
const permissionSets: PermissionSet[] = roles.map((r) =>
|
const permissionSets: PermissionSet[] = roles.map((r) =>
|
||||||
Object.values(allPermissions[r] || {})
|
Object.values(allPermissions[r] || {}),
|
||||||
);
|
);
|
||||||
return new Authorizer(permissionSets);
|
return new Authorizer(permissionSets);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
koa.on("error", (err, ctx) => {
|
koa.on("error", (err, ctx) => {
|
||||||
@@ -90,7 +90,7 @@ koa.use(
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
koa.use(async (ctx, next) => {
|
koa.use(async (ctx, next) => {
|
||||||
@@ -110,7 +110,7 @@ koa.use(async (ctx, next) => {
|
|||||||
|
|
||||||
ctx.state.authorizer = getAuthorizer(
|
ctx.state.authorizer = getAuthorizer(
|
||||||
ctx.state.configSnapshot,
|
ctx.state.configSnapshot,
|
||||||
JSON.stringify(roles)
|
JSON.stringify(roles),
|
||||||
);
|
);
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
@@ -245,7 +245,7 @@ router.get("/", async (ctx) => {
|
|||||||
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
|
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
|
||||||
window.genieacsVersion = ${JSON.stringify(VERSION)};
|
window.genieacsVersion = ${JSON.stringify(VERSION)};
|
||||||
window.username = ${JSON.stringify(
|
window.username = ${JSON.stringify(
|
||||||
ctx.state.user ? ctx.state.user.username : ""
|
ctx.state.user ? ctx.state.user.username : "",
|
||||||
)};
|
)};
|
||||||
window.permissionSets = ${JSON.stringify(permissionSets)};
|
window.permissionSets = ${JSON.stringify(permissionSets)};
|
||||||
</script>
|
</script>
|
||||||
@@ -269,7 +269,7 @@ koa.use(
|
|||||||
[constants.BROTLI_PARAM_QUALITY]: 5,
|
[constants.BROTLI_PARAM_QUALITY]: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
koa.use(router.routes());
|
koa.use(router.routes());
|
||||||
|
|||||||
+9
-9
@@ -104,12 +104,12 @@ router.get(`/devices/:id.csv`, async (ctx) => {
|
|||||||
ctx.attachment(
|
ctx.attachment(
|
||||||
`device-${ctx.params.id}-${new Date()
|
`device-${ctx.params.id}-${new Date()
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.replace(/[:.]/g, "")}.csv`
|
.replace(/[:.]/g, "")}.csv`,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.body = new PassThrough();
|
ctx.body = new PassThrough();
|
||||||
ctx.body.write(
|
ctx.body.write(
|
||||||
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp,Notification,Notification timestamp,Access list,Access list timestamp\n"
|
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp,Notification,Notification timestamp,Access list,Access list timestamp\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const k of Object.keys(device).sort()) {
|
for (const k of Object.keys(device).sort()) {
|
||||||
@@ -259,7 +259,7 @@ for (const [resource, flags] of Object.entries(resources)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const columns: Record<string, Expression> = JSON.parse(
|
const columns: Record<string, Expression> = JSON.parse(
|
||||||
singleParam(ctx.request.query.columns)
|
singleParam(ctx.request.query.columns),
|
||||||
);
|
);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
@@ -282,11 +282,11 @@ for (const [resource, flags] of Object.entries(resources)) {
|
|||||||
ctx.body = new PassThrough();
|
ctx.body = new PassThrough();
|
||||||
ctx.type = "text/csv";
|
ctx.type = "text/csv";
|
||||||
ctx.attachment(
|
ctx.attachment(
|
||||||
`${resource}-${new Date(now).toISOString().replace(/[:.]/g, "")}.csv`
|
`${resource}-${new Date(now).toISOString().replace(/[:.]/g, "")}.csv`,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.body.write(
|
ctx.body.write(
|
||||||
Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) + "\n"
|
Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) + "\n",
|
||||||
);
|
);
|
||||||
for await (const obj of db.query(resource, filter, options)) {
|
for await (const obj of db.query(resource, filter, options)) {
|
||||||
const arr = Object.values(columns).map((exp) => {
|
const arr = Object.values(columns).map((exp) => {
|
||||||
@@ -633,7 +633,7 @@ router.post("/devices/:id/tasks", async (ctx) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
},
|
||||||
) as number;
|
) as number;
|
||||||
|
|
||||||
const lastInform = device["Events.Inform"].value[0] as number;
|
const lastInform = device["Events.Inform"].value[0] as number;
|
||||||
@@ -643,7 +643,7 @@ router.post("/devices/:id/tasks", async (ctx) => {
|
|||||||
const sessionStarted = await apiFunctions.awaitSessionStart(
|
const sessionStarted = await apiFunctions.awaitSessionStart(
|
||||||
deviceId,
|
deviceId,
|
||||||
lastInform,
|
lastInform,
|
||||||
onlineThreshold
|
onlineThreshold,
|
||||||
);
|
);
|
||||||
if (!sessionStarted) {
|
if (!sessionStarted) {
|
||||||
status = "No contact from CPE";
|
status = "No contact from CPE";
|
||||||
@@ -655,7 +655,7 @@ router.post("/devices/:id/tasks", async (ctx) => {
|
|||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
const promises = statuses.map((t) =>
|
const promises = statuses.map((t) =>
|
||||||
collections.faults.count({ _id: `${deviceId}:task_${t._id}` })
|
collections.faults.count({ _id: `${deviceId}:task_${t._id}` }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const res = await Promise.all(promises);
|
const res = await Promise.all(promises);
|
||||||
@@ -746,7 +746,7 @@ router.put("/users/:id/password", async (ctx) => {
|
|||||||
!(await apiFunctions.authLocal(
|
!(await apiFunctions.authLocal(
|
||||||
ctx.state.configSnapshot,
|
ctx.state.configSnapshot,
|
||||||
username,
|
username,
|
||||||
ctx.request.body.authPassword
|
ctx.request.body.authPassword,
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
|
|||||||
+20
-20
@@ -30,7 +30,7 @@ import { Script } from "vm";
|
|||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
|
|
||||||
function processDeviceProjection(
|
function processDeviceProjection(
|
||||||
projection: Record<string, 1>
|
projection: Record<string, 1>,
|
||||||
): Record<string, 1> {
|
): Record<string, 1> {
|
||||||
if (!projection) return projection;
|
if (!projection) return projection;
|
||||||
const p = {};
|
const p = {};
|
||||||
@@ -58,7 +58,7 @@ function processDeviceProjection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processDeviceSort(
|
function processDeviceSort(
|
||||||
sort: Record<string, number>
|
sort: Record<string, number>,
|
||||||
): Record<string, number> {
|
): Record<string, number> {
|
||||||
if (!sort) return sort;
|
if (!sort) return sort;
|
||||||
const s = {};
|
const s = {};
|
||||||
@@ -93,7 +93,7 @@ interface FlatAttributes {
|
|||||||
accessListTimestamp?: number;
|
accessListTimestamp?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FlatDevice {
|
export interface FlatDevice {
|
||||||
[param: string]: FlatAttributes;
|
[param: string]: FlatAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export function flattenDevice(device: Record<string, unknown>): FlatDevice {
|
|||||||
input,
|
input,
|
||||||
root: string,
|
root: string,
|
||||||
output: FlatDevice,
|
output: FlatDevice,
|
||||||
timestamp: number
|
timestamp: number,
|
||||||
): void {
|
): void {
|
||||||
for (const [name, tree] of Object.entries(input)) {
|
for (const [name, tree] of Object.entries(input)) {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
@@ -272,7 +272,7 @@ function flattenTask(task: unknown): Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function flattenPreset(
|
function flattenPreset(
|
||||||
preset: Record<string, unknown>
|
preset: Record<string, unknown>,
|
||||||
): Record<string, unknown> {
|
): Record<string, unknown> {
|
||||||
const p = Object.assign({}, preset);
|
const p = Object.assign({}, preset);
|
||||||
if (p.precondition) {
|
if (p.precondition) {
|
||||||
@@ -281,7 +281,7 @@ function flattenPreset(
|
|||||||
parse(p.precondition as string);
|
parse(p.precondition as string);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
p.precondition = convertOldPrecondition(
|
p.precondition = convertOldPrecondition(
|
||||||
JSON.parse(p.precondition as string)
|
JSON.parse(p.precondition as string),
|
||||||
);
|
);
|
||||||
p.precondition = (p.precondition as string).length
|
p.precondition = (p.precondition as string).length
|
||||||
? stringify(p.precondition as Expression)
|
? stringify(p.precondition as Expression)
|
||||||
@@ -377,7 +377,7 @@ interface QueryOptions {
|
|||||||
export async function* query(
|
export async function* query(
|
||||||
resource: string,
|
resource: string,
|
||||||
filter: Expression,
|
filter: Expression,
|
||||||
options?: QueryOptions
|
options?: QueryOptions,
|
||||||
): AsyncGenerator<any, void, undefined> {
|
): AsyncGenerator<any, void, undefined> {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
filter = evaluate(filter, null, Date.now());
|
filter = evaluate(filter, null, Date.now());
|
||||||
@@ -407,7 +407,7 @@ export async function* query(
|
|||||||
.reduce(
|
.reduce(
|
||||||
(obj, [k, v]) =>
|
(obj, [k, v]) =>
|
||||||
Object.assign(obj, { [k]: Math.min(Math.max(v, -1), 1) }),
|
Object.assign(obj, { [k]: Math.min(Math.max(v, -1), 1) }),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resource === "devices") s = processDeviceSort(s);
|
if (resource === "devices") s = processDeviceSort(s);
|
||||||
@@ -435,7 +435,7 @@ export function count(resource: string, filter: Expression): Promise<number> {
|
|||||||
|
|
||||||
export async function updateDeviceTags(
|
export async function updateDeviceTags(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
tags: Record<string, boolean>
|
tags: Record<string, boolean>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const add = [];
|
const add = [];
|
||||||
const pull = [];
|
const pull = [];
|
||||||
@@ -455,7 +455,7 @@ export async function updateDeviceTags(
|
|||||||
|
|
||||||
export async function putPreset(
|
export async function putPreset(
|
||||||
id: string,
|
id: string,
|
||||||
object: Record<string, unknown>
|
object: Record<string, unknown>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const p = preProcessPreset(object);
|
const p = preProcessPreset(object);
|
||||||
await collections.presets.replaceOne({ _id: id }, p, { upsert: true });
|
await collections.presets.replaceOne({ _id: id }, p, { upsert: true });
|
||||||
@@ -467,7 +467,7 @@ export async function deletePreset(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function putProvision(
|
export async function putProvision(
|
||||||
id: string,
|
id: string,
|
||||||
object: { script: string }
|
object: { script: string },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!object.script) object.script = "";
|
if (!object.script) object.script = "";
|
||||||
try {
|
try {
|
||||||
@@ -478,7 +478,7 @@ export async function putProvision(
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.stack?.startsWith(`${id}:`)) {
|
if (err.stack?.startsWith(`${id}:`)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`)
|
new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
@@ -494,7 +494,7 @@ export async function deleteProvision(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function putVirtualParameter(
|
export async function putVirtualParameter(
|
||||||
id: string,
|
id: string,
|
||||||
object: { script: string }
|
object: { script: string },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!object.script) object.script = "";
|
if (!object.script) object.script = "";
|
||||||
try {
|
try {
|
||||||
@@ -505,7 +505,7 @@ export async function putVirtualParameter(
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.stack?.startsWith(`${id}:`)) {
|
if (err.stack?.startsWith(`${id}:`)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`)
|
new Error(`${err.name} at ${err.stack.split("\n", 1)[0]}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
@@ -521,7 +521,7 @@ export async function deleteVirtualParameter(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function putConfig(
|
export async function putConfig(
|
||||||
id: string,
|
id: string,
|
||||||
object: WithoutId<MongoTypes.Config>
|
object: WithoutId<MongoTypes.Config>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await collections.config.replaceOne({ _id: id }, object, { upsert: true });
|
await collections.config.replaceOne({ _id: id }, object, { upsert: true });
|
||||||
}
|
}
|
||||||
@@ -532,7 +532,7 @@ export async function deleteConfig(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function putPermission(
|
export async function putPermission(
|
||||||
id: string,
|
id: string,
|
||||||
object: WithoutId<MongoTypes.Permission>
|
object: WithoutId<MongoTypes.Permission>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await collections.permissions.replaceOne({ _id: id }, object, {
|
await collections.permissions.replaceOne({ _id: id }, object, {
|
||||||
upsert: true,
|
upsert: true,
|
||||||
@@ -545,13 +545,13 @@ export async function deletePermission(id: string): Promise<void> {
|
|||||||
|
|
||||||
export async function putUser(
|
export async function putUser(
|
||||||
id: string,
|
id: string,
|
||||||
object: Partial<WithoutId<MongoTypes.User>>
|
object: Partial<WithoutId<MongoTypes.User>>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// update instead of replace to keep the password if not set by user
|
// update instead of replace to keep the password if not set by user
|
||||||
await collections.users.updateOne(
|
await collections.users.updateOne(
|
||||||
{ _id: id },
|
{ _id: id },
|
||||||
{ $set: object },
|
{ $set: object },
|
||||||
{ upsert: true }
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +566,7 @@ export function downloadFile(filename: string): Readable {
|
|||||||
export function putFile(
|
export function putFile(
|
||||||
filename: string,
|
filename: string,
|
||||||
metadata: Record<string, string>,
|
metadata: Record<string, string>,
|
||||||
contentStream: Readable
|
contentStream: Readable,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const uploadStream = filesBucket.openUploadStreamWithId(
|
const uploadStream = filesBucket.openUploadStreamWithId(
|
||||||
@@ -574,7 +574,7 @@ export function putFile(
|
|||||||
filename,
|
filename,
|
||||||
{
|
{
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let readableEnded = false;
|
let readableEnded = false;
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function getConfig(
|
|||||||
key: string,
|
key: string,
|
||||||
context: Record<string, unknown>,
|
context: Record<string, unknown>,
|
||||||
now: number,
|
now: number,
|
||||||
cb?: (e: Expression) => Expression
|
cb?: (e: Expression) => Expression,
|
||||||
): string | number | boolean | null {
|
): string | number | boolean | null {
|
||||||
const snapshot = localCache.get(revision);
|
const snapshot = localCache.get(revision);
|
||||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||||
|
|||||||
+3
-3
@@ -20,7 +20,7 @@
|
|||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
export function generateDeviceId(
|
export function generateDeviceId(
|
||||||
deviceIdStruct: Record<string, string>
|
deviceIdStruct: Record<string, string>,
|
||||||
): string {
|
): string {
|
||||||
// Escapes everything except alphanumerics and underscore
|
// Escapes everything except alphanumerics and underscore
|
||||||
function esc(str): string {
|
function esc(str): string {
|
||||||
@@ -54,7 +54,7 @@ export function encodeTag(tag: string): string {
|
|||||||
return encodeURIComponent(tag)
|
return encodeURIComponent(tag)
|
||||||
.replace(
|
.replace(
|
||||||
/[!~*'().]/g,
|
/[!~*'().]/g,
|
||||||
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
|
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
|
||||||
)
|
)
|
||||||
.replace(/0x(?=[0-9A-Z]{2})/g, "0%78")
|
.replace(/0x(?=[0-9A-Z]{2})/g, "0%78")
|
||||||
.replace(/%/g, "0x");
|
.replace(/%/g, "0x");
|
||||||
@@ -67,7 +67,7 @@ export function decodeTag(tag: string): string {
|
|||||||
export function once(
|
export function once(
|
||||||
emitter: EventEmitter,
|
emitter: EventEmitter,
|
||||||
event: string,
|
event: string,
|
||||||
timeout: number
|
timeout: number,
|
||||||
): Promise<unknown[]> {
|
): Promise<unknown[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export default class VersionedMap<K, V> {
|
|||||||
const del = revisionsObj.delete || 0;
|
const del = revisionsObj.delete || 0;
|
||||||
const mutations = Object.keys(revisionsObj).reduce(
|
const mutations = Object.keys(revisionsObj).reduce(
|
||||||
(acc, cur) => (cur === "delete" ? acc : acc | (1 << +cur)),
|
(acc, cur) => (cur === "delete" ? acc : acc | (1 << +cur)),
|
||||||
del
|
del,
|
||||||
);
|
);
|
||||||
|
|
||||||
const revisions = [];
|
const revisions = [];
|
||||||
|
|||||||
Generated
+2600
-3072
File diff suppressed because it is too large.
Load diff
+13
-16
@@ -35,28 +35,28 @@
|
|||||||
"seedrandom": "^3.0.5"
|
"seedrandom": "^3.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/codemirror": "^5.60.5",
|
"@types/codemirror": "^5.60.15",
|
||||||
"@types/jsonwebtoken": "^8.5.8",
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
"@types/koa": "^2.13.5",
|
"@types/koa": "^2.13.5",
|
||||||
"@types/koa-compress": "^4.0.3",
|
"@types/koa-compress": "^4.0.3",
|
||||||
"@types/koa-router": "^7.4.4",
|
"@types/koa-router": "^7.4.4",
|
||||||
"@types/koa-static": "^4.0.2",
|
"@types/koa-static": "^4.0.2",
|
||||||
"@types/mithril": "^2.0.11",
|
"@types/mithril": "^2.2.6",
|
||||||
"@types/node": "^18.7.9",
|
"@types/node": "^20.10.4",
|
||||||
"@types/parsimmon": "^1.10.6",
|
"@types/parsimmon": "^1.10.9",
|
||||||
"@types/seedrandom": "^3.0.2",
|
"@types/seedrandom": "^3.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||||
"@typescript-eslint/parser": "^5.33.1",
|
"@typescript-eslint/parser": "^6.14.0",
|
||||||
"ava": "^3.15.0",
|
"ava": "^3.15.0",
|
||||||
"codemirror": "^5.65.8",
|
"codemirror": "^5.65.16",
|
||||||
"esbuild": "^0.19.9",
|
"esbuild": "^0.19.9",
|
||||||
"eslint": "^8.22.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"mithril": "^2.2.2",
|
"mithril": "^2.2.2",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^3.1.1",
|
||||||
"sql.js": "^1.7.0",
|
"sql.js": "^1.7.0",
|
||||||
"svgo": "^2.8.0",
|
"svgo": "^2.8.0",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^5.3.3",
|
||||||
"yaml": "^1.10.2"
|
"yaml": "^1.10.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -64,10 +64,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "esbuild --log-level=warning --bundle --platform=node --target=node12 --packages=external --outdir=test test/*.ts && ava --verbose --color && rm test/*.js",
|
"test": "esbuild --log-level=warning --bundle --platform=node --target=node12 --packages=external --outdir=test test/*.ts && ava --verbose --color && rm test/*.js",
|
||||||
"lint-eslint": "eslint --fix \"bin/*.ts\" \"lib/**/*.ts\" \"ui/**/*.ts\" \"test/**/*.ts\" \"build/**/*.ts\"",
|
"lint": "esbuild build/lint.ts --bundle --platform=node --target=node16 --packages=external | node",
|
||||||
"lint-prettier": "prettier --prose-wrap always --write .",
|
|
||||||
"lint-tsc": "tsc --noEmit",
|
|
||||||
"lint": "npm run lint-eslint && npm run lint-prettier && npm run lint-tsc",
|
|
||||||
"build": "esbuild build/build.ts --bundle --platform=node --target=node12 --packages=external | node"
|
"build": "esbuild build/build.ts --bundle --platform=node --target=node12 --packages=external | node"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -27,7 +27,7 @@ ava("digest", (t) => {
|
|||||||
uri,
|
uri,
|
||||||
method,
|
method,
|
||||||
body,
|
body,
|
||||||
wwwAuthHeader
|
wwwAuthHeader,
|
||||||
);
|
);
|
||||||
const authHeader = auth.parseAuthorizationHeader(solution);
|
const authHeader = auth.parseAuthorizationHeader(solution);
|
||||||
t.is(
|
t.is(
|
||||||
@@ -42,8 +42,8 @@ ava("digest", (t) => {
|
|||||||
authHeader["qop"],
|
authHeader["qop"],
|
||||||
body,
|
body,
|
||||||
authHeader["cnonce"],
|
authHeader["cnonce"],
|
||||||
authHeader["nc"]
|
authHeader["nc"],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
+1
-1
@@ -55,7 +55,7 @@ ava("convertOldPrecondition", (t) => {
|
|||||||
for (const test of tests) {
|
for (const test of tests) {
|
||||||
t.is(
|
t.is(
|
||||||
stringify(convertOldPrecondition(test[0] as Record<string, unknown>)),
|
stringify(convertOldPrecondition(test[0] as Record<string, unknown>)),
|
||||||
test[1]
|
test[1],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -85,7 +85,7 @@ ava("unpack", (t) => {
|
|||||||
let unpacked: Path[];
|
let unpacked: Path[];
|
||||||
unpacked = device.unpack(
|
unpacked = device.unpack(
|
||||||
deviceData,
|
deviceData,
|
||||||
Path.parse("a.[b:b,c:c].a.[b:b1,c:c1].a")
|
Path.parse("a.[b:b,c:c].a.[b:b1,c:c1].a"),
|
||||||
);
|
);
|
||||||
t.is(unpacked.length, 2);
|
t.is(unpacked.length, 2);
|
||||||
t.is(unpacked[0].toString(), "a.1.a.1.a");
|
t.is(unpacked[0].toString(), "a.1.a.1.a");
|
||||||
|
|||||||
+6
-6
@@ -38,31 +38,31 @@ ava("find", (t) => {
|
|||||||
|
|
||||||
t.deepEqual(
|
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"]
|
["a"],
|
||||||
);
|
);
|
||||||
|
|
||||||
t.deepEqual(
|
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", "*.*"]
|
["a", "a.*", "a.a", "*.a", "*.*"],
|
||||||
);
|
);
|
||||||
|
|
||||||
t.deepEqual(
|
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.*"]
|
["a.*"],
|
||||||
);
|
);
|
||||||
|
|
||||||
t.deepEqual(
|
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"]
|
["a.*", "a.a"],
|
||||||
);
|
);
|
||||||
|
|
||||||
t.deepEqual(
|
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.*", "*.*"]
|
["a.*", "*.*"],
|
||||||
);
|
);
|
||||||
|
|
||||||
t.deepEqual(
|
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", "*.*"]
|
["a.*", "a.a", "*.a", "*.*"],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
+4
-4
@@ -21,7 +21,7 @@ test.before(async (t) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
db.run(
|
db.run(
|
||||||
"CREATE TABLE test (id INTEGER PRIMARY KEY, string STRING, decimal DECIMAL(4,2))"
|
"CREATE TABLE test (id INTEGER PRIMARY KEY, string STRING, decimal DECIMAL(4,2))",
|
||||||
);
|
);
|
||||||
|
|
||||||
const stmt = db.prepare("INSERT INTO test (string, decimal) VALUES (?, ?)");
|
const stmt = db.prepare("INSERT INTO test (string, decimal) VALUES (?, ?)");
|
||||||
@@ -72,16 +72,16 @@ test("minimize", async (t) => {
|
|||||||
STRING_VALUES.filter((s) => s),
|
STRING_VALUES.filter((s) => s),
|
||||||
STRING_VALUES.filter((s) => s),
|
STRING_VALUES.filter((s) => s),
|
||||||
[">", "=", "<"],
|
[">", "=", "<"],
|
||||||
["<>", ">="]
|
["<>", ">="],
|
||||||
)) {
|
)) {
|
||||||
cases.push(
|
cases.push(
|
||||||
`string ${op1} "${s1}" OR string ='${s2}' OR NOT string ${op2} '${s3}'`
|
`string ${op1} "${s1}" OR string ='${s2}' OR NOT string ${op2} '${s3}'`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [s1, s2] of getPermutations(
|
for (const [s1, s2] of getPermutations(
|
||||||
STRING_VALUES.filter((s) => s),
|
STRING_VALUES.filter((s) => s),
|
||||||
STRING_VALUES.filter((s) => s)
|
STRING_VALUES.filter((s) => s),
|
||||||
))
|
))
|
||||||
cases.push(`string > "${s1}" AND string < '${s2}'`);
|
cases.push(`string > "${s1}" AND string < '${s2}'`);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -36,6 +36,6 @@ ava("generateDeviceId", (t) => {
|
|||||||
ava("escapeRegExp", (t) => {
|
ava("escapeRegExp", (t) => {
|
||||||
t.is(
|
t.is(
|
||||||
common.escapeRegExp("\\ ^ $ * + ? . ( ) | { } [ ]"),
|
common.escapeRegExp("\\ ^ $ * + ? . ( ) | { } [ ]"),
|
||||||
"\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]"
|
"\\\\ \\^ \\$ \\* \\+ \\? \\. \\( \\) \\| \\{ \\} \\[ \\]",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
+2
-2
@@ -7,7 +7,7 @@ import {
|
|||||||
|
|
||||||
ava("parseXmlDeclaration", (t) => {
|
ava("parseXmlDeclaration", (t) => {
|
||||||
const buf = Buffer.from(
|
const buf = Buffer.from(
|
||||||
'<?xml version="1.0" encoding="UTF-8"?>\n<soap-env:Envelope />'
|
'<?xml version="1.0" encoding="UTF-8"?>\n<soap-env:Envelope />',
|
||||||
);
|
);
|
||||||
const attrs = parseXmlDeclaration(buf);
|
const attrs = parseXmlDeclaration(buf);
|
||||||
t.deepEqual(attrs, [
|
t.deepEqual(attrs, [
|
||||||
@@ -29,7 +29,7 @@ ava("parseXmlDeclaration", (t) => {
|
|||||||
ava("decodeEntities", (t) => {
|
ava("decodeEntities", (t) => {
|
||||||
t.is(
|
t.is(
|
||||||
decodeEntities("&&<>"'>§��;"),
|
decodeEntities("&&<>"'>§��;"),
|
||||||
"&&<>\"'>§𠮷;"
|
"&&<>\"'>§𠮷;",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["ES2022", "dom"],
|
"lib": ["ES2022", "dom"],
|
||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "bundler",
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
|||||||
+18
-14
@@ -31,8 +31,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["presets"] },
|
{ class: active["presets"] },
|
||||||
m("a", { href: "#!/admin/presets" }, "Presets")
|
m("a", { href: "#!/admin/presets" }, "Presets"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["provisions"] },
|
{ class: active["provisions"] },
|
||||||
m("a", { href: "#!/admin/provisions" }, "Provisions")
|
m("a", { href: "#!/admin/provisions" }, "Provisions"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +51,12 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["virtualParameters"] },
|
{ class: active["virtualParameters"] },
|
||||||
m("a", { href: "#!/admin/virtualParameters" }, "Virtual Parameters")
|
m(
|
||||||
)
|
"a",
|
||||||
|
{ href: "#!/admin/virtualParameters" },
|
||||||
|
"Virtual Parameters",
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,8 +65,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["files"] },
|
{ class: active["files"] },
|
||||||
m("a", { href: "#!/admin/files" }, "Files")
|
m("a", { href: "#!/admin/files" }, "Files"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +75,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["config"] },
|
{ class: active["config"] },
|
||||||
m("a", { href: "#!/admin/config" }, "Config")
|
m("a", { href: "#!/admin/config" }, "Config"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +85,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["permissions"] },
|
{ class: active["permissions"] },
|
||||||
m("a", { href: "#!/admin/permissions" }, "Permissions")
|
m("a", { href: "#!/admin/permissions" }, "Permissions"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,8 +95,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["users"] },
|
{ class: active["users"] },
|
||||||
m("a", { href: "#!/admin/users" }, "Users")
|
m("a", { href: "#!/admin/users" }, "Users"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ m.route(document.body, "/overview", {
|
|||||||
"/admin/provisions": pagify("provisions", provisionsPage),
|
"/admin/provisions": pagify("provisions", provisionsPage),
|
||||||
"/admin/virtualParameters": pagify(
|
"/admin/virtualParameters": pagify(
|
||||||
"virtualParameters",
|
"virtualParameters",
|
||||||
virtualParametersPage
|
virtualParametersPage,
|
||||||
),
|
),
|
||||||
"/admin/files": pagify("files", filesPage),
|
"/admin/files": pagify("files", filesPage),
|
||||||
"/admin/config": pagify("config", configPage),
|
"/admin/config": pagify("config", configPage),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
type AutocompleteCallback = (
|
type AutocompleteCallback = (
|
||||||
value: string,
|
value: string,
|
||||||
callback: (suggestions: { value: string; tip?: string }[]) => void
|
callback: (suggestions: { value: string; tip?: string }[]) => void,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export default class Autocomplete {
|
export default class Autocomplete {
|
||||||
@@ -180,14 +180,14 @@ export default class Autocomplete {
|
|||||||
if (selectedElement) {
|
if (selectedElement) {
|
||||||
this.container.scrollTop = Math.min(
|
this.container.scrollTop = Math.min(
|
||||||
this.container.scrollTop,
|
this.container.scrollTop,
|
||||||
selectedElement.offsetTop
|
selectedElement.offsetTop,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.container.scrollTop = Math.max(
|
this.container.scrollTop = Math.max(
|
||||||
this.container.scrollTop,
|
this.container.scrollTop,
|
||||||
selectedElement.offsetTop +
|
selectedElement.offsetTop +
|
||||||
selectedElement.scrollHeight -
|
selectedElement.scrollHeight -
|
||||||
this.container.clientHeight
|
this.container.clientHeight,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
oncreate: (_vnode) => {
|
oncreate: (_vnode) => {
|
||||||
(_vnode.dom as HTMLSelectElement).focus();
|
(_vnode.dom as HTMLSelectElement).focus();
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -75,8 +75,8 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
oninput: (e) => {
|
oninput: (e) => {
|
||||||
vnode.state[f] = e.target.value;
|
vnode.state[f] = e.target.value;
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
{
|
{
|
||||||
type: "submit",
|
type: "submit",
|
||||||
},
|
},
|
||||||
"Change password"
|
"Change password",
|
||||||
) as VnodeDOM;
|
) as VnodeDOM;
|
||||||
|
|
||||||
form.push(m(".actions-bar", submit));
|
form.push(m(".actions-bar", submit));
|
||||||
@@ -109,19 +109,19 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
) {
|
) {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
"Password confirm doesn't match new password"
|
"Password confirm doesn't match new password",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
(submit.dom as HTMLFormElement).disabled = true;
|
(submit.dom as HTMLFormElement).disabled = true;
|
||||||
changePassword(
|
changePassword(
|
||||||
vnode.state["username"],
|
vnode.state["username"],
|
||||||
vnode.state["newPassword"],
|
vnode.state["newPassword"],
|
||||||
vnode.state["authPassword"]
|
vnode.state["authPassword"],
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
"Password updated successfully"
|
"Password updated successfully",
|
||||||
);
|
);
|
||||||
if (onPasswordChange) onPasswordChange();
|
if (onPasswordChange) onPasswordChange();
|
||||||
(submit.dom as HTMLFormElement).disabled = false;
|
(submit.dom as HTMLFormElement).disabled = false;
|
||||||
@@ -133,7 +133,7 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
form
|
form,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const component: ClosureComponent<Attrs> = () => {
|
|||||||
if (vnode.attrs.onSubmit) vnode.attrs.onSubmit(_vnode.dom);
|
if (vnode.attrs.onSubmit) vnode.attrs.onSubmit(_vnode.dom);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (vnode.attrs.onChange) {
|
if (vnode.attrs.onChange) {
|
||||||
|
|||||||
+6
-5
@@ -63,10 +63,11 @@ const vnodeContext = new WeakMap<Vnode, Attributes>();
|
|||||||
|
|
||||||
interface MC extends Static {
|
interface MC extends Static {
|
||||||
context: {
|
context: {
|
||||||
(ctx: Attributes, selector: string, ...children: Children[]): Vnode<
|
(
|
||||||
any,
|
ctx: Attributes,
|
||||||
any
|
selector: string,
|
||||||
>;
|
...children: Children[]
|
||||||
|
): Vnode<any, any>;
|
||||||
(
|
(
|
||||||
ctx: Attributes,
|
ctx: Attributes,
|
||||||
selector: string,
|
selector: string,
|
||||||
@@ -150,7 +151,7 @@ export function contextifyComponent(component: ComponentTypes): ComponentTypes {
|
|||||||
title: "Click to print stack trace to console",
|
title: "Click to print stack trace to console",
|
||||||
onclick: () => console.error(err),
|
onclick: () => console.error(err),
|
||||||
},
|
},
|
||||||
"Error!"
|
"Error!",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,14 +17,16 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClosureComponent, Component } from "mithril";
|
import { ClosureComponent } from "mithril";
|
||||||
import { m } from "../components";
|
import { m } from "../components";
|
||||||
import * as taskQueue from "../task-queue";
|
import * as taskQueue from "../task-queue";
|
||||||
import { parse } from "../../lib/common/expression/parser";
|
import { parse } from "../../lib/common/expression/parser";
|
||||||
import memoize from "../../lib/common/memoize";
|
import memoize from "../../lib/common/memoize";
|
||||||
import { getIcon } from "../icons";
|
import { getIcon } from "../icons";
|
||||||
import { evaluateExpression } from "../store";
|
import { QueryResponse, evaluateExpression } from "../store";
|
||||||
import debounce from "../../lib/common/debounce";
|
import debounce from "../../lib/common/debounce";
|
||||||
|
import { Expression } from "../../lib/types";
|
||||||
|
import { FlatDevice } from "../../lib/ui/db";
|
||||||
|
|
||||||
const memoizedParse = memoize(parse);
|
const memoizedParse = memoize(parse);
|
||||||
|
|
||||||
@@ -52,7 +54,13 @@ function orderKeysByDepth(device: Record<string, unknown>): string[][] {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
const component: ClosureComponent = (): Component => {
|
interface Attrs {
|
||||||
|
device: FlatDevice;
|
||||||
|
limit: Expression;
|
||||||
|
deviceQuery: QueryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: ClosureComponent<Attrs> = () => {
|
||||||
let queryString: string;
|
let queryString: string;
|
||||||
const formQueryString = debounce((args: string[]) => {
|
const formQueryString = debounce((args: string[]) => {
|
||||||
queryString = args[args.length - 1];
|
queryString = args[args.length - 1];
|
||||||
@@ -61,9 +69,10 @@ const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
const device = vnode.attrs["device"];
|
const device = vnode.attrs.device;
|
||||||
|
|
||||||
const limit = evaluateExpression(vnode.attrs["limit"], device) || 100;
|
const limit =
|
||||||
|
(evaluateExpression(vnode.attrs.limit, device) as number) || 100;
|
||||||
|
|
||||||
const search = m("input", {
|
const search = m("input", {
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -108,8 +117,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
val.push(
|
val.push(
|
||||||
m(
|
m(
|
||||||
"parameter",
|
"parameter",
|
||||||
Object.assign({ device: device, parameter: memoizedParse(k) })
|
Object.assign({ device: device, parameter: memoizedParse(k) }),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (p.object && p.writable) {
|
} else if (p.object && p.writable) {
|
||||||
if (instanceRegex.test(k)) {
|
if (instanceRegex.test(k)) {
|
||||||
@@ -121,13 +130,13 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "deleteObject",
|
name: "deleteObject",
|
||||||
device: device["DeviceID.ID"].value[0],
|
device: device["DeviceID.ID"].value[0] as string,
|
||||||
objectName: k,
|
objectName: k,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("delete-instance")
|
getIcon("delete-instance"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
val.push(
|
val.push(
|
||||||
@@ -138,13 +147,13 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "addObject",
|
name: "addObject",
|
||||||
device: device["DeviceID.ID"].value[0],
|
device: device["DeviceID.ID"].value[0] as string,
|
||||||
objectName: k,
|
objectName: k,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("add-instance")
|
getIcon("add-instance"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,38 +166,38 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "getParameterValues",
|
name: "getParameterValues",
|
||||||
device: device["DeviceID.ID"].value[0],
|
device: device["DeviceID.ID"].value[0] as string,
|
||||||
parameterNames: [k],
|
parameterNames: [k],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("refresh")
|
getIcon("refresh"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
"tr",
|
"tr",
|
||||||
attrs,
|
attrs,
|
||||||
m("td.left", m("long-text", { text: k })),
|
m("td.left", m("long-text", { text: k })),
|
||||||
m("td.right", val)
|
m("td.right", val),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [vnode.attrs["deviceQuery"]] },
|
{ queries: [vnode.attrs.deviceQuery] },
|
||||||
m(
|
m(
|
||||||
".all-parameters",
|
".all-parameters",
|
||||||
m(
|
m(
|
||||||
"a.download-csv",
|
"a.download-csv",
|
||||||
{
|
{
|
||||||
href: `api/devices/${encodeURIComponent(
|
href: `api/devices/${encodeURIComponent(
|
||||||
device["DeviceID.ID"].value[0]
|
device["DeviceID.ID"].value[0],
|
||||||
)}.csv`,
|
)}.csv`,
|
||||||
download: "",
|
download: "",
|
||||||
style: "float: right;",
|
style: "float: right;",
|
||||||
},
|
},
|
||||||
"Download"
|
"Download",
|
||||||
),
|
),
|
||||||
search,
|
search,
|
||||||
m(
|
m(
|
||||||
@@ -196,10 +205,10 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m("table", m("tbody", rows)),
|
m("table", m("tbody", rows)),
|
||||||
m(
|
m(
|
||||||
"m",
|
"m",
|
||||||
`Displaying ${filteredKeys.length} out of ${count} parameters.`
|
`Displaying ${filteredKeys.length} out of ${count} parameters.`,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,18 +17,19 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Attributes, ClosureComponent, Component } from "mithril";
|
import { Attributes, ClosureComponent } from "mithril";
|
||||||
import { map } from "../../lib/common/expression/parser";
|
import { map } from "../../lib/common/expression/parser";
|
||||||
import memoize from "../../lib/common/memoize";
|
import memoize from "../../lib/common/memoize";
|
||||||
import { Expression } from "../../lib/types";
|
import { Expression } from "../../lib/types";
|
||||||
import { m } from "../components";
|
import { m } from "../components";
|
||||||
import { evaluateExpression, getTimestamp } from "../store";
|
import { evaluateExpression, getTimestamp } from "../store";
|
||||||
|
import { FlatDevice } from "../../lib/ui/db";
|
||||||
|
|
||||||
const evaluateAttributes = memoize(
|
const evaluateAttributes = memoize(
|
||||||
(
|
(
|
||||||
attrs: Record<string, Expression>,
|
attrs: Record<string, Expression>,
|
||||||
obj: Record<string, unknown>,
|
obj: Record<string, unknown>,
|
||||||
now: number // eslint-disable-line @typescript-eslint/no-unused-vars
|
now: number, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
): Attributes => {
|
): Attributes => {
|
||||||
const res: Attributes = {};
|
const res: Attributes = {};
|
||||||
for (const [k, v] of Object.entries(attrs)) {
|
for (const [k, v] of Object.entries(attrs)) {
|
||||||
@@ -47,19 +48,24 @@ const evaluateAttributes = memoize(
|
|||||||
res[k] = evaluateExpression(vv, obj);
|
res[k] = evaluateExpression(vv, obj);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const component: ClosureComponent = (): Component => {
|
interface Attrs {
|
||||||
|
device: FlatDevice;
|
||||||
|
filter: Expression;
|
||||||
|
components: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: ClosureComponent<Attrs> = () => {
|
||||||
return {
|
return {
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
const device = vnode.attrs["device"];
|
const device = vnode.attrs.device;
|
||||||
if ("filter" in vnode.attrs) {
|
if ("filter" in vnode.attrs) {
|
||||||
if (!evaluateExpression(vnode.attrs["filter"], device || {}))
|
if (!evaluateExpression(vnode.attrs.filter, device || {})) return null;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = Object.values(vnode.attrs["components"]).map((c) => {
|
const children = Object.values(vnode.attrs.components).map((c) => {
|
||||||
if (Array.isArray(c)) c = evaluateExpression(c, device || {});
|
if (Array.isArray(c)) c = evaluateExpression(c, device || {});
|
||||||
if (typeof c !== "object") return `${c}`;
|
if (typeof c !== "object") return `${c}`;
|
||||||
const type = evaluateExpression(c["type"], device || {});
|
const type = evaluateExpression(c["type"], device || {});
|
||||||
@@ -79,7 +85,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
attrs = evaluateAttributes(
|
attrs = evaluateAttributes(
|
||||||
el["attributes"],
|
el["attributes"],
|
||||||
device || {},
|
device || {},
|
||||||
getTimestamp()
|
getTimestamp(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Reboot"
|
"Reboot",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -58,8 +58,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Reset"
|
"Reset",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -74,8 +74,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Push file"
|
"Push file",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -98,8 +98,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Delete"
|
"Delete",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return m(".actions-bar", buttons);
|
return m(".actions-bar", buttons);
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("remove")
|
getIcon("remove"),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -86,19 +86,19 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (rows.length) {
|
if (rows.length) {
|
||||||
tbody = m(
|
tbody = m(
|
||||||
"tbody",
|
"tbody",
|
||||||
rows.map((r) => m("tr", r))
|
rows.map((r) => m("tr", r)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tbody = m(
|
tbody = m(
|
||||||
"tbody",
|
"tbody",
|
||||||
m("tr.empty", m("td", { colspan: headers.length }, "No faults"))
|
m("tr.empty", m("td", { colspan: headers.length }, "No faults")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [faults] },
|
{ queries: [faults] },
|
||||||
m("table.table", thead, tbody)
|
m("table.table", thead, tbody),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
return m(
|
return m(
|
||||||
"a",
|
"a",
|
||||||
{ href: `#!/devices/${encodeURIComponent(deviceId)}` },
|
{ href: `#!/devices/${encodeURIComponent(deviceId)}` },
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return children;
|
return children;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
cy: "0.5em",
|
cy: "0.5em",
|
||||||
r: "0.4em",
|
r: "0.4em",
|
||||||
fill: slice["color"],
|
fill: slice["color"],
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
return m("span.overview-dot", dot, `${slice["label"]}`);
|
return m("span.overview-dot", dot, `${slice["label"]}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,24 +17,35 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClosureComponent, Component, VnodeDOM } from "mithril";
|
import { ClosureComponent, VnodeDOM } from "mithril";
|
||||||
import { m } from "../components";
|
import { m } from "../components";
|
||||||
import { evaluateExpression } from "../store";
|
import { QueryResponse, evaluateExpression } from "../store";
|
||||||
|
import { FlatDevice } from "../../lib/ui/db";
|
||||||
|
import { Expression } from "../../lib/types";
|
||||||
|
|
||||||
const component: ClosureComponent = (): Component => {
|
interface Attrs {
|
||||||
|
device: FlatDevice;
|
||||||
|
parameters: Record<
|
||||||
|
string,
|
||||||
|
{ type?: Expression; label: string; parameter: Expression }
|
||||||
|
>;
|
||||||
|
deviceQuery: QueryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: ClosureComponent<Attrs> = () => {
|
||||||
return {
|
return {
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
const device = vnode.attrs["device"];
|
const device = vnode.attrs.device;
|
||||||
|
|
||||||
const rows = Object.values(vnode.attrs["parameters"]).map((parameter) => {
|
const rows = Object.values(vnode.attrs.parameters).map((parameter) => {
|
||||||
const type = evaluateExpression(parameter["type"], device);
|
const type = evaluateExpression(parameter.type, device);
|
||||||
const p = m.context(
|
const p = m.context(
|
||||||
{
|
{
|
||||||
device: device,
|
device: device,
|
||||||
parameter: parameter["parameter"],
|
parameter: parameter.parameter,
|
||||||
},
|
},
|
||||||
(type as string) || "parameter",
|
(type as string) || "parameter",
|
||||||
parameter
|
parameter,
|
||||||
);
|
);
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
@@ -52,14 +63,14 @@ const component: ClosureComponent = (): Component => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
m("th", parameter["label"]),
|
m("th", parameter["label"]),
|
||||||
m("td", p)
|
m("td", p),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [vnode.attrs["deviceQuery"]] },
|
{ queries: [vnode.attrs.deviceQuery] },
|
||||||
m("table.parameter-list", rows)
|
m("table.parameter-list", rows),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,24 +17,35 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClosureComponent, Component } from "mithril";
|
import { ClosureComponent } from "mithril";
|
||||||
import { m } from "../components";
|
import { m } from "../components";
|
||||||
import * as taskQueue from "../task-queue";
|
import * as taskQueue from "../task-queue";
|
||||||
import { evaluateExpression } from "../store";
|
import { QueryResponse, evaluateExpression } from "../store";
|
||||||
import * as expressionParser from "../../lib/common/expression/parser";
|
import * as expressionParser from "../../lib/common/expression/parser";
|
||||||
import { getIcon } from "../icons";
|
import { getIcon } from "../icons";
|
||||||
|
import { FlatDevice } from "../../lib/ui/db";
|
||||||
|
import { Expression } from "../../lib/types";
|
||||||
|
|
||||||
const component: ClosureComponent = (): Component => {
|
interface Attrs {
|
||||||
|
device: FlatDevice;
|
||||||
|
parameter: Expression;
|
||||||
|
label: Expression;
|
||||||
|
childParameters: Record<string, { label: string; parameter: Expression }>;
|
||||||
|
filter?: Expression;
|
||||||
|
deviceQuery: QueryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: ClosureComponent<Attrs> = () => {
|
||||||
return {
|
return {
|
||||||
oninit: (vnode) => {
|
oninit: (vnode) => {
|
||||||
const obj = vnode.attrs["parameter"];
|
const obj = vnode.attrs.parameter;
|
||||||
if (!Array.isArray(obj) || obj[0] !== "PARAM")
|
if (!Array.isArray(obj) || obj[0] !== "PARAM")
|
||||||
throw new Error("Object must be a parameter path");
|
throw new Error("Object must be a parameter path");
|
||||||
vnode.state["object"] = obj[1];
|
vnode.state["object"] = obj[1];
|
||||||
vnode.state["parameters"] = Object.values(vnode.attrs["childParameters"]);
|
vnode.state["parameters"] = Object.values(vnode.attrs.childParameters);
|
||||||
},
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
const device = vnode.attrs["device"];
|
const device = vnode.attrs.device;
|
||||||
const object = evaluateExpression(vnode.state["object"], device);
|
const object = evaluateExpression(vnode.state["object"], device);
|
||||||
const parameters = vnode.state["parameters"];
|
const parameters = vnode.state["parameters"];
|
||||||
|
|
||||||
@@ -56,7 +67,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
const rows = [];
|
const rows = [];
|
||||||
for (const i of instances) {
|
for (const i of instances) {
|
||||||
let filter = "filter" in vnode.attrs ? vnode.attrs["filter"] : true;
|
let filter = "filter" in vnode.attrs ? vnode.attrs.filter : true;
|
||||||
|
|
||||||
filter = expressionParser.map(filter, (e) => {
|
filter = expressionParser.map(filter, (e) => {
|
||||||
if (Array.isArray(e) && e[0] === "PARAM")
|
if (Array.isArray(e) && e[0] === "PARAM")
|
||||||
@@ -84,8 +95,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
device: device,
|
device: device,
|
||||||
parameter: param,
|
parameter: param,
|
||||||
label: null,
|
label: null,
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,14 +111,14 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "deleteObject",
|
name: "deleteObject",
|
||||||
device: device["DeviceID.ID"].value[0],
|
device: device["DeviceID.ID"].value[0] as string,
|
||||||
objectName: i,
|
objectName: i,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("delete-instance")
|
getIcon("delete-instance"),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
rows.push(m("tr", row));
|
rows.push(m("tr", row));
|
||||||
@@ -115,7 +126,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
rows.push(
|
rows.push(
|
||||||
m("tr.empty", m("td", { colspan: headers.length }, "No instances"))
|
m("tr.empty", m("td", { colspan: headers.length }, "No instances")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,29 +144,29 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "addObject",
|
name: "addObject",
|
||||||
device: device["DeviceID.ID"].value[0],
|
device: device["DeviceID.ID"].value[0] as string,
|
||||||
objectName: object,
|
objectName: object,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("add-instance")
|
getIcon("add-instance"),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let label;
|
let label;
|
||||||
|
|
||||||
const l = evaluateExpression(vnode.attrs["label"], device);
|
const l = evaluateExpression(vnode.attrs.label, device);
|
||||||
if (l != null) label = m("h2", l);
|
if (l != null) label = m("h2", l);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
label,
|
label,
|
||||||
m(
|
m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [vnode.attrs["deviceQuery"]] },
|
{ queries: [vnode.attrs.deviceQuery] },
|
||||||
m("table.table", thead, m("tbody", rows))
|
m("table.table", thead, m("tbody", rows)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
const { value, timestamp, parameter } = evaluateParam(
|
const { value, timestamp, parameter } = evaluateParam(
|
||||||
vnode.attrs["parameter"],
|
vnode.attrs["parameter"],
|
||||||
device,
|
device,
|
||||||
store.getTimestamp() + store.getClockSkew()
|
store.getTimestamp() + store.getClockSkew(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
@@ -103,7 +103,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("edit")
|
getIcon("edit"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
el,
|
el,
|
||||||
edit
|
edit,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,19 +62,19 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (connectionRequestStatus !== "OK") {
|
if (connectionRequestStatus !== "OK") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: ${connectionRequestStatus}`
|
`${deviceId}: ${connectionRequestStatus}`,
|
||||||
);
|
);
|
||||||
} else if (tasks2[0].status === "stale") {
|
} else if (tasks2[0].status === "stale") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: No contact from device`
|
`${deviceId}: No contact from device`,
|
||||||
);
|
);
|
||||||
} else if (tasks2[0].status === "fault") {
|
} else if (tasks2[0].status === "fault") {
|
||||||
notifications.push("error", `${deviceId}: Refresh faulted`);
|
notifications.push("error", `${deviceId}: Refresh faulted`);
|
||||||
} else {
|
} else {
|
||||||
notifications.push("success", `${deviceId}: Summoned`);
|
notifications.push("success", `${deviceId}: Summoned`);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
e.target.disabled = false;
|
e.target.disabled = false;
|
||||||
@@ -86,7 +86,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Summon"
|
"Summon",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+22
-15
@@ -17,20 +17,27 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClosureComponent, Component } from "mithril";
|
import { ClosureComponent } from "mithril";
|
||||||
import { m } from "../components";
|
import { m } from "../components";
|
||||||
import * as notifications from "../notifications";
|
import * as notifications from "../notifications";
|
||||||
import * as store from "../store";
|
import * as store from "../store";
|
||||||
import { getIcon } from "../icons";
|
import { getIcon } from "../icons";
|
||||||
import { decodeTag } from "../../lib/util";
|
import { decodeTag } from "../../lib/util";
|
||||||
|
import { Expression } from "../../lib/types";
|
||||||
|
import { FlatDevice } from "../../lib/ui/db";
|
||||||
|
|
||||||
const component: ClosureComponent = (): Component => {
|
interface Attrs {
|
||||||
|
device: FlatDevice;
|
||||||
|
writable?: Expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: ClosureComponent<Attrs> = () => {
|
||||||
return {
|
return {
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
const device = vnode.attrs["device"];
|
const device = vnode.attrs.device;
|
||||||
let writable = true;
|
let writable = true;
|
||||||
if ("writable" in vnode.attrs)
|
if ("writable" in vnode.attrs)
|
||||||
writable = !!store.evaluateExpression(vnode.attrs["writable"], device);
|
writable = !!store.evaluateExpression(vnode.attrs.writable, device);
|
||||||
|
|
||||||
const tags = [];
|
const tags = [];
|
||||||
for (const p of Object.keys(device))
|
for (const p of Object.keys(device))
|
||||||
@@ -41,7 +48,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (!writable) {
|
if (!writable) {
|
||||||
return m(
|
return m(
|
||||||
".tags",
|
".tags",
|
||||||
tags.map((t) => m("span.tag", t))
|
tags.map((t) => m("span.tag", t)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +63,14 @@ const component: ClosureComponent = (): Component => {
|
|||||||
{
|
{
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
const deviceId = device["DeviceID.ID"].value[0];
|
const deviceId = device["DeviceID.ID"].value[0] as string;
|
||||||
store
|
store
|
||||||
.updateTags(deviceId, { [tag]: false })
|
.updateTags(deviceId, { [tag]: false })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
e.target.disabled = false;
|
e.target.disabled = false;
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`${deviceId}: Tags updated`
|
`${deviceId}: Tags updated`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
})
|
})
|
||||||
@@ -71,14 +78,14 @@ const component: ClosureComponent = (): Component => {
|
|||||||
e.target.disabled = false;
|
e.target.disabled = false;
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: ${err.message}`
|
`${deviceId}: ${err.message}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("remove")
|
getIcon("remove"),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"span.tag.writable",
|
"span.tag.writable",
|
||||||
@@ -88,7 +95,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
{
|
{
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
const deviceId = device["DeviceID.ID"].value[0];
|
const deviceId = device["DeviceID.ID"].value[0] as string;
|
||||||
const tag = prompt(`Enter tag to assign to device:`);
|
const tag = prompt(`Enter tag to assign to device:`);
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
e.target.disabled = false;
|
e.target.disabled = false;
|
||||||
@@ -108,9 +115,9 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("add")
|
getIcon("add"),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export function structureConfig(config: Config[]): any {
|
|||||||
|
|
||||||
export function diffConfig(
|
export function diffConfig(
|
||||||
current: Record<string, unknown>,
|
current: Record<string, unknown>,
|
||||||
target: Record<string, unknown>
|
target: Record<string, unknown>,
|
||||||
): Diff {
|
): Diff {
|
||||||
const diff = {
|
const diff = {
|
||||||
add: [],
|
add: [],
|
||||||
|
|||||||
+24
-24
@@ -74,7 +74,7 @@ function putActionHandler(action, _object, isNew?): Promise<ValidationErrors> {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`Config ${exists ? "updated" : "created"}`
|
`Config ${exists ? "updated" : "created"}`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -112,7 +112,7 @@ function escapeRegExp(str): string {
|
|||||||
export function init(): Promise<Record<string, unknown>> {
|
export function init(): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("config", 2)) {
|
if (!window.authorizer.hasAccess("config", 2)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -173,19 +173,19 @@ function renderTable(confsResponse, searchString): Children {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formData
|
formData,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => comp;
|
cb = () => comp;
|
||||||
overlay.open(
|
overlay.open(
|
||||||
cb,
|
cb,
|
||||||
() =>
|
() =>
|
||||||
!comp.state["current"]["modified"] ||
|
!comp.state["current"]["modified"] ||
|
||||||
confirm("You have unsaved changes. Close anyway?")
|
confirm("You have unsaved changes. Close anyway?"),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("edit")
|
getIcon("edit"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const del = m(
|
const del = m(
|
||||||
@@ -200,7 +200,7 @@ function renderTable(confsResponse, searchString): Children {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("remove")
|
getIcon("remove"),
|
||||||
);
|
);
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
@@ -210,9 +210,9 @@ function renderTable(confsResponse, searchString): Children {
|
|||||||
m("td.left", m("long-text", { text: conf._id })),
|
m("td.left", m("long-text", { text: conf._id })),
|
||||||
m(
|
m(
|
||||||
"td.right",
|
"td.right",
|
||||||
m("span", [m("long-text", { text: `${conf.value}` }), edit, del])
|
m("span", [m("long-text", { text: `${conf.value}` }), edit, del]),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,19 +276,19 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formData
|
formData,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => comp;
|
cb = () => comp;
|
||||||
overlay.open(
|
overlay.open(
|
||||||
cb,
|
cb,
|
||||||
() =>
|
() =>
|
||||||
!comp.state["current"]["modified"] ||
|
!comp.state["current"]["modified"] ||
|
||||||
confirm("You have unsaved changes. Close anyway?")
|
confirm("You have unsaved changes. Close anyway?"),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"New config"
|
"New config",
|
||||||
);
|
);
|
||||||
|
|
||||||
const subsData = [
|
const subsData = [
|
||||||
@@ -332,8 +332,8 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
"success",
|
"success",
|
||||||
`${sub.name.replace(
|
`${sub.name.replace(
|
||||||
/^[a-z]/,
|
/^[a-z]/,
|
||||||
sub.name[0].toUpperCase()
|
sub.name[0].toUpperCase(),
|
||||||
)} config updated`
|
)} config updated`,
|
||||||
);
|
);
|
||||||
overlay.close(cb);
|
overlay.close(cb);
|
||||||
}
|
}
|
||||||
@@ -345,20 +345,20 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
overlay.close(cb);
|
overlay.close(cb);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
attrs
|
attrs,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => comp;
|
cb = () => comp;
|
||||||
overlay.open(
|
overlay.open(
|
||||||
cb,
|
cb,
|
||||||
() =>
|
() =>
|
||||||
!comp.state["modified"] ||
|
!comp.state["modified"] ||
|
||||||
confirm("You have unsaved changes. Close anyway?")
|
confirm("You have unsaved changes. Close anyway?"),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`Edit ${sub.name}`
|
`Edit ${sub.name}`,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,10 +374,10 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
".parameter-list",
|
".parameter-list",
|
||||||
{ style: "height: 400px" },
|
{ style: "height: 400px" },
|
||||||
renderTable(confs, vnode.state["searchString"])
|
renderTable(confs, vnode.state["searchString"]),
|
||||||
),
|
),
|
||||||
m(".actions-bar", [newConfig].concat(subs))
|
m(".actions-bar", [newConfig].concat(subs)),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
+18
-6
@@ -54,7 +54,9 @@ textarea {
|
|||||||
a {
|
a {
|
||||||
color: var(--color4);
|
color: var(--color4);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color var(--fade), color var(--fade);
|
transition:
|
||||||
|
background-color var(--fade),
|
||||||
|
color var(--fade);
|
||||||
&:visited {
|
&:visited {
|
||||||
color: var(--color4);
|
color: var(--color4);
|
||||||
}
|
}
|
||||||
@@ -122,7 +124,9 @@ body {
|
|||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
box-shadow: 0 0 10px #ccc;
|
box-shadow: 0 0 10px #ccc;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height var(--fade), opacity var(--fade);
|
transition:
|
||||||
|
height var(--fade),
|
||||||
|
opacity var(--fade);
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
padding: 0.3em;
|
padding: 0.3em;
|
||||||
@@ -228,7 +232,9 @@ body {
|
|||||||
border: 1px solid var(--color1);
|
border: 1px solid var(--color1);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 0 10px #ccc;
|
box-shadow: 0 0 10px #ccc;
|
||||||
transition: opacity var(--fade), top var(--fade);
|
transition:
|
||||||
|
opacity var(--fade),
|
||||||
|
top var(--fade);
|
||||||
|
|
||||||
&.error {
|
&.error {
|
||||||
background-color: hsl(var(--status-red) / 0.9);
|
background-color: hsl(var(--status-red) / 0.9);
|
||||||
@@ -437,7 +443,9 @@ button {
|
|||||||
border: 0;
|
border: 0;
|
||||||
color: var(--color4);
|
color: var(--color4);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: background-color var(--fade), color var(--fade);
|
transition:
|
||||||
|
background-color var(--fade),
|
||||||
|
color var(--fade);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
@@ -468,7 +476,9 @@ input {
|
|||||||
border: 1px solid var(--color1);
|
border: 1px solid var(--color1);
|
||||||
background-color: var(--color3);
|
background-color: var(--color3);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: border-color var(--fade), background-color var(--fade);
|
transition:
|
||||||
|
border-color var(--fade),
|
||||||
|
background-color var(--fade);
|
||||||
&.error {
|
&.error {
|
||||||
background-color: #efcbcf;
|
background-color: #efcbcf;
|
||||||
border-color: #e6b2b7;
|
border-color: #e6b2b7;
|
||||||
@@ -670,7 +680,9 @@ span.tag {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
transition: opacity var(--fade), top var(--fade);
|
transition:
|
||||||
|
opacity var(--fade),
|
||||||
|
top var(--fade);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ export function getDatalistId(options: string[]): string {
|
|||||||
const n = m(
|
const n = m(
|
||||||
"datalist",
|
"datalist",
|
||||||
{ id },
|
{ id },
|
||||||
options.map((o) => m("option", { value: o }))
|
options.map((o) => m("option", { value: o })),
|
||||||
);
|
);
|
||||||
elements.set(id, n);
|
elements.set(id, n);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -23,11 +23,11 @@ import config from "./config";
|
|||||||
import * as store from "./store";
|
import * as store from "./store";
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("devices", 2)) {
|
if (!window.authorizer.hasAccess("devices", 2)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
return m(
|
return m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [dev] },
|
{ queries: [dev] },
|
||||||
m("div", { style: "height: 100px;" })
|
m("div", { style: "height: 100px;" }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
for (const c of Object.values(conf)) {
|
for (const c of Object.values(conf)) {
|
||||||
cmps.push(
|
cmps.push(
|
||||||
m.context({ device: dev.value[0], deviceQuery: dev }, c["type"], c)
|
m.context({ device: dev.value[0], deviceQuery: dev }, c["type"], c),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-18
@@ -61,7 +61,7 @@ const unpackSmartQuery = memoize((query) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!window.authorizer.hasAccess("devices", 2))
|
if (!window.authorizer.hasAccess("devices", 2))
|
||||||
@@ -97,8 +97,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
queueTask(...tasks);
|
queueTask(...tasks);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Reboot"
|
"Reboot",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -115,8 +115,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
queueTask(...tasks);
|
queueTask(...tasks);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Reset"
|
"Reset",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -132,8 +132,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Push file"
|
"Push file",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -163,8 +163,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
if (--counter === 0) store.setTimestamp(Date.now());
|
if (--counter === 0) store.setTimestamp(Date.now());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Delete"
|
"Delete",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -195,8 +195,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
if (--counter === 0) store.setTimestamp(Date.now());
|
if (--counter === 0) store.setTimestamp(Date.now());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Tag"
|
"Tag",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@@ -208,7 +208,7 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
const ids = Array.from(selected);
|
const ids = Array.from(selected);
|
||||||
const tag = prompt(
|
const tag = prompt(
|
||||||
`Enter tag to unassign from ${ids.length} devices:`
|
`Enter tag to unassign from ${ids.length} devices:`,
|
||||||
);
|
);
|
||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
|
|
||||||
@@ -229,8 +229,8 @@ function renderActions(selected: Set<string>): Children {
|
|||||||
if (--counter === 0) store.setTimestamp(Date.now());
|
if (--counter === 0) store.setTimestamp(Date.now());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Untag"
|
"Untag",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
@@ -270,7 +270,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
const _sort = {};
|
const _sort = {};
|
||||||
for (const index of sortedAttrs) {
|
for (const index of sortedAttrs) {
|
||||||
const param = memoizedGetSortable(
|
const param = memoizedGetSortable(
|
||||||
attributes[Math.abs(index) - 1].parameter
|
attributes[Math.abs(index) - 1].parameter,
|
||||||
);
|
);
|
||||||
_sort[param] = Math.sign(index);
|
_sort[param] = Math.sign(index);
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
return m.context(
|
return m.context(
|
||||||
{ device: device, parameter: attr.parameter },
|
{ device: device, parameter: attr.parameter },
|
||||||
attr.type || "parameter",
|
attr.type || "parameter",
|
||||||
attr
|
attr,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -314,10 +314,10 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
href: `#!/devices/${encodeURIComponent(
|
href: `#!/devices/${encodeURIComponent(
|
||||||
device["DeviceID.ID"].value[0]
|
device["DeviceID.ID"].value[0],
|
||||||
)}`,
|
)}`,
|
||||||
},
|
},
|
||||||
"Show"
|
"Show",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+51
-51
@@ -73,7 +73,7 @@ function renderStagingSpv(task: StageTask, queueFunc, cancelFunc): Children {
|
|||||||
[
|
[
|
||||||
m("option", { value: "true" }, "true"),
|
m("option", { value: "true" }, "true"),
|
||||||
m("option", { value: "false" }, "false"),
|
m("option", { value: "false" }, "false"),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const type = task.parameterValues[0][2];
|
const type = task.parameterValues[0][2];
|
||||||
@@ -134,8 +134,8 @@ function renderStagingDownload(task: StageTask): Children {
|
|||||||
m(
|
m(
|
||||||
"option",
|
"option",
|
||||||
{ disabled: !t, value: t, selected: (task.fileType || "") === t },
|
{ disabled: !t, value: t, selected: (task.fileType || "") === t },
|
||||||
t
|
t,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const filesList = [""]
|
const filesList = [""]
|
||||||
@@ -145,16 +145,16 @@ function renderStagingDownload(task: StageTask): Children {
|
|||||||
(f) =>
|
(f) =>
|
||||||
(!f["metadata.oui"] || f["metadata.oui"] === oui) &&
|
(!f["metadata.oui"] || f["metadata.oui"] === oui) &&
|
||||||
(!f["metadata.productClass"] ||
|
(!f["metadata.productClass"] ||
|
||||||
f["metadata.productClass"] === productClass)
|
f["metadata.productClass"] === productClass),
|
||||||
)
|
)
|
||||||
.map((f) => f._id)
|
.map((f) => f._id),
|
||||||
)
|
)
|
||||||
.map((f) =>
|
.map((f) =>
|
||||||
m(
|
m(
|
||||||
"option",
|
"option",
|
||||||
{ disabled: !f, value: f, selected: (task.fileName || "") === f },
|
{ disabled: !f, value: f, selected: (task.fileName || "") === f },
|
||||||
f
|
f,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -172,7 +172,7 @@ function renderStagingDownload(task: StageTask): Children {
|
|||||||
disabled: files.fulfilling,
|
disabled: files.fulfilling,
|
||||||
style: "width: 350px",
|
style: "width: 350px",
|
||||||
},
|
},
|
||||||
filesList
|
filesList,
|
||||||
),
|
),
|
||||||
" as ",
|
" as ",
|
||||||
m(
|
m(
|
||||||
@@ -182,7 +182,7 @@ function renderStagingDownload(task: StageTask): Children {
|
|||||||
task.fileType = e.target.value;
|
task.fileType = e.target.value;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
typesList
|
typesList,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -211,12 +211,12 @@ function renderStaging(staging: Set<StageTask>): Child[] {
|
|||||||
const queue = m(
|
const queue = m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
{ title: "Queue task", onclick: queueFunc, disabled: invalid.has(s) },
|
{ title: "Queue task", onclick: queueFunc, disabled: invalid.has(s) },
|
||||||
"Queue"
|
"Queue",
|
||||||
);
|
);
|
||||||
const cancel = m(
|
const cancel = m(
|
||||||
"button",
|
"button",
|
||||||
{ title: "Cancel edit", onclick: cancelFunc },
|
{ title: "Cancel edit", onclick: cancelFunc },
|
||||||
"Cancel"
|
"Cancel",
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.push(m(".staging", elms, m("div.actions", queue, cancel)));
|
elements.push(m(".staging", elms, m("div.actions", queue, cancel)));
|
||||||
@@ -247,8 +247,8 @@ function renderQueue(queue: Set<QueueTask>): Child[] {
|
|||||||
queueTask(t);
|
queueTask(t);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("retry")
|
getIcon("retry"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,8 +261,8 @@ function renderQueue(queue: Set<QueueTask>): Child[] {
|
|||||||
deleteTask(t);
|
deleteTask(t);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getIcon("remove")
|
getIcon("remove"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (t.name === "setParameterValues") {
|
if (t.name === "setParameterValues") {
|
||||||
@@ -275,56 +275,56 @@ function renderQueue(queue: Set<QueueTask>): Child[] {
|
|||||||
mparam(t.parameterValues[0][0]),
|
mparam(t.parameterValues[0][0]),
|
||||||
" to '",
|
" to '",
|
||||||
mval(t.parameterValues[0][1]),
|
mval(t.parameterValues[0][1]),
|
||||||
"'"
|
"'",
|
||||||
),
|
),
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (t.name === "refreshObject") {
|
} else if (t.name === "refreshObject") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
m("span", "Refresh ", mparam(t.parameterName)),
|
m("span", "Refresh ", mparam(t.parameterName)),
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (t.name === "reboot") {
|
} else if (t.name === "reboot") {
|
||||||
details.push(m(`div.${t.status}`, "Reboot", m(".actions", actions)));
|
details.push(m(`div.${t.status}`, "Reboot", m(".actions", actions)));
|
||||||
} else if (t.name === "factoryReset") {
|
} else if (t.name === "factoryReset") {
|
||||||
details.push(
|
details.push(
|
||||||
m(`div.${t.status}`, "Factory reset", m(".actions", actions))
|
m(`div.${t.status}`, "Factory reset", m(".actions", actions)),
|
||||||
);
|
);
|
||||||
} else if (t.name === "addObject") {
|
} else if (t.name === "addObject") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
m("span", "Add ", mparam(t.objectName)),
|
m("span", "Add ", mparam(t.objectName)),
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (t.name === "deleteObject") {
|
} else if (t.name === "deleteObject") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
m("span", "Delete ", mparam(t.objectName)),
|
m("span", "Delete ", mparam(t.objectName)),
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (t.name === "getParameterValues") {
|
} else if (t.name === "getParameterValues") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
`Refresh ${t.parameterNames.length} parameters`,
|
`Refresh ${t.parameterNames.length} parameters`,
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else if (t.name === "download") {
|
} else if (t.name === "download") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
`Push file: ${t.fileName} (${t.fileType})`,
|
`Push file: ${t.fileName} (${t.fileType})`,
|
||||||
m(".actions", actions)
|
m(".actions", actions),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
|
details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
|
||||||
@@ -342,7 +342,7 @@ function renderNotifications(notifs): Child[] {
|
|||||||
let buttons;
|
let buttons;
|
||||||
if (n.actions) {
|
if (n.actions) {
|
||||||
const btns = Object.entries(n.actions).map(([label, onclick]) =>
|
const btns = Object.entries(n.actions).map(([label, onclick]) =>
|
||||||
m("button.primary", { onclick: onclick }, label)
|
m("button.primary", { onclick: onclick }, label),
|
||||||
);
|
);
|
||||||
if (btns.length) buttons = m("div", { style: "float: right" }, btns);
|
if (btns.length) buttons = m("div", { style: "float: right" }, btns);
|
||||||
}
|
}
|
||||||
@@ -366,8 +366,8 @@ function renderNotifications(notifs): Child[] {
|
|||||||
},
|
},
|
||||||
key: n.timestamp,
|
key: n.timestamp,
|
||||||
},
|
},
|
||||||
m("div", buttons, n.message)
|
m("div", buttons, n.message),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return notificationElements;
|
return notificationElements;
|
||||||
@@ -401,7 +401,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
height = Math.max(
|
height = Math.max(
|
||||||
height,
|
height,
|
||||||
(s.dom as HTMLDivElement).offsetTop +
|
(s.dom as HTMLDivElement).offsetTop +
|
||||||
(s.dom as HTMLDivElement).offsetHeight
|
(s.dom as HTMLDivElement).offsetHeight,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (vnode.state["mouseIn"]) {
|
} else if (vnode.state["mouseIn"]) {
|
||||||
@@ -424,7 +424,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
disabled: !statusCount.queued,
|
disabled: !statusCount.queued,
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
const tasks = Array.from(getQueue()).filter(
|
const tasks = Array.from(getQueue()).filter(
|
||||||
(t) => t["status"] === "queued"
|
(t) => t["status"] === "queued",
|
||||||
);
|
);
|
||||||
commit(
|
commit(
|
||||||
tasks,
|
tasks,
|
||||||
@@ -432,7 +432,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (err) {
|
if (err) {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: ${err.message}`
|
`${deviceId}: ${err.message}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -440,7 +440,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (connectionRequestStatus !== "OK") {
|
if (connectionRequestStatus !== "OK") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: ${connectionRequestStatus}`
|
`${deviceId}: ${connectionRequestStatus}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -449,13 +449,13 @@ const component: ClosureComponent = (): Component => {
|
|||||||
if (t.status === "stale") {
|
if (t.status === "stale") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: No contact from device`
|
`${deviceId}: No contact from device`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (t.status === "fault") {
|
} else if (t.status === "fault") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: Task(s) faulted`
|
`${deviceId}: Task(s) faulted`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -463,9 +463,9 @@ const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`${deviceId}: Task(s) committed`
|
`${deviceId}: Task(s) committed`,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
@@ -475,7 +475,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Commit"
|
"Commit",
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"button",
|
"button",
|
||||||
@@ -484,8 +484,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onclick: clear,
|
onclick: clear,
|
||||||
disabled: !queueElements.length,
|
disabled: !queueElements.length,
|
||||||
},
|
},
|
||||||
"Clear"
|
"Clear",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
statusElement = m(
|
statusElement = m(
|
||||||
@@ -493,24 +493,24 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"span.queued",
|
"span.queued",
|
||||||
{ class: statusCount.queued ? "active" : "" },
|
{ class: statusCount.queued ? "active" : "" },
|
||||||
`Queued: ${statusCount.queued}`
|
`Queued: ${statusCount.queued}`,
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"span.pending",
|
"span.pending",
|
||||||
{ class: statusCount.pending ? "active" : "" },
|
{ class: statusCount.pending ? "active" : "" },
|
||||||
`Pending: ${statusCount.pending}`
|
`Pending: ${statusCount.pending}`,
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"span.fault",
|
"span.fault",
|
||||||
{ class: statusCount.fault ? "active" : "" },
|
{ class: statusCount.fault ? "active" : "" },
|
||||||
`Fault: ${statusCount.fault}`
|
`Fault: ${statusCount.fault}`,
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"span.stale",
|
"span.stale",
|
||||||
{ class: statusCount.stale ? "active" : "" },
|
{ class: statusCount.stale ? "active" : "" },
|
||||||
`Stale: ${statusCount.stale}`
|
`Stale: ${statusCount.stale}`,
|
||||||
),
|
),
|
||||||
actions
|
actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
drawerElement = m(
|
drawerElement = m(
|
||||||
@@ -545,7 +545,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusElement,
|
statusElement,
|
||||||
stagingElements.length ? stagingElements : m(".queue", queueElements)
|
stagingElements.length ? stagingElements : m(".queue", queueElements),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,8 +560,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onupdate: repositionNotifications,
|
onupdate: repositionNotifications,
|
||||||
oncreate: repositionNotifications,
|
oncreate: repositionNotifications,
|
||||||
},
|
},
|
||||||
notificationElements
|
notificationElements,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function onError(): void {
|
|||||||
Reload: () => {
|
Reload: () => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ export function loadYaml(): Promise<void> {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
import("yaml")
|
import("yaml")
|
||||||
.then((module) => {
|
.then((module) => {
|
||||||
yaml = module;
|
yaml = module.default;
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
+8
-8
@@ -66,11 +66,11 @@ const unpackSmartQuery = memoize((query) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("faults", 2)) {
|
if (!window.authorizer.hasAccess("faults", 2)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
const valueCallback = (attr, fault): Children => {
|
const valueCallback = (attr, fault): Children => {
|
||||||
if (attr.id === "device") {
|
if (attr.id === "device") {
|
||||||
const deviceHref = `#!/devices/${encodeURIComponent(
|
const deviceHref = `#!/devices/${encodeURIComponent(
|
||||||
fault["device"]
|
fault["device"],
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
return m("a", { href: deviceHref }, fault["device"]);
|
return m("a", { href: deviceHref }, fault["device"]);
|
||||||
@@ -175,13 +175,13 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
|
|
||||||
Promise.all(
|
Promise.all(
|
||||||
Array.from(selected).map((id) =>
|
Array.from(selected).map((id) =>
|
||||||
store.deleteResource("faults", id)
|
store.deleteResource("faults", id),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`${res.length} faults deleted`
|
`${res.length} faults deleted`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
})
|
})
|
||||||
@@ -191,7 +191,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Delete"
|
"Delete",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [faults, count] },
|
{ queries: [faults, count] },
|
||||||
m(indexTableComponent, attrs)
|
m(indexTableComponent, attrs),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
+14
-14
@@ -77,13 +77,13 @@ function upload(
|
|||||||
file: File,
|
file: File,
|
||||||
headers: Record<string, string>,
|
headers: Record<string, string>,
|
||||||
abortSignal?: AbortSignal,
|
abortSignal?: AbortSignal,
|
||||||
progressListener?: (e: ProgressEvent) => void
|
progressListener?: (e: ProgressEvent) => void,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
headers = Object.assign(
|
headers = Object.assign(
|
||||||
{
|
{
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
},
|
},
|
||||||
headers
|
headers,
|
||||||
);
|
);
|
||||||
return store.xhrRequest({
|
return store.xhrRequest({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
@@ -112,11 +112,11 @@ const getDownloadUrl = memoize((filter) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("files", 2)) {
|
if (!window.authorizer.hasAccess("files", 2)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
file,
|
file,
|
||||||
headers,
|
headers,
|
||||||
abortController.signal,
|
abortController.signal,
|
||||||
progressListener
|
progressListener,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
notifications.push("success", "File created");
|
notifications.push("success", "File created");
|
||||||
@@ -247,8 +247,8 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
progress = -1;
|
progress = -1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formData
|
formData,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => {
|
cb = () => {
|
||||||
if (progress < 0) return [null, comp];
|
if (progress < 0) return [null, comp];
|
||||||
@@ -257,7 +257,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
"div.progress",
|
"div.progress",
|
||||||
m("div.progress-bar", {
|
m("div.progress-bar", {
|
||||||
style: `width: ${Math.trunc(progress * 100)}%`,
|
style: `width: ${Math.trunc(progress * 100)}%`,
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
comp,
|
comp,
|
||||||
];
|
];
|
||||||
@@ -273,7 +273,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"New"
|
"New",
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
@@ -290,13 +290,13 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
Promise.all(
|
Promise.all(
|
||||||
Array.from(selected).map((id) =>
|
Array.from(selected).map((id) =>
|
||||||
store.deleteResource("files", id)
|
store.deleteResource("files", id),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`${res.length} files deleted`
|
`${res.length} files deleted`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
})
|
})
|
||||||
@@ -306,7 +306,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Delete"
|
"Delete",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -324,7 +324,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [files, count] },
|
{ queries: [files, count] },
|
||||||
m(indexTableComponent, attrs)
|
m(indexTableComponent, attrs),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const getAutocomplete = memoize((resource) => {
|
|||||||
.map((s) => ({
|
.map((s) => ({
|
||||||
value: `${s}: `,
|
value: `${s}: `,
|
||||||
tip: smartQuery.getTip(resource, s),
|
tip: smartQuery.getTip(resource, s),
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return autocomplete;
|
return autocomplete;
|
||||||
|
|||||||
+1
-1
@@ -25,6 +25,6 @@ export function getIcon(name: string): Children {
|
|||||||
return m(
|
return m(
|
||||||
`svg.icon.icon-${name}`,
|
`svg.icon.icon-${name}`,
|
||||||
{ key: `icon-${name}` },
|
{ key: `icon-${name}` },
|
||||||
m("use", { href: `${ICONS_SVG}#icon-${name}` })
|
m("use", { href: `${ICONS_SVG}#icon-${name}` }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+13
-11
@@ -41,7 +41,9 @@ function renderTable(
|
|||||||
downloadUrl?: string,
|
downloadUrl?: string,
|
||||||
valueCallback?: (attr: Attribute, record: Record<string, any>) => Children,
|
valueCallback?: (attr: Attribute, record: Record<string, any>) => Children,
|
||||||
actionsCallback?: Children | ((sel: Set<string>) => Children),
|
actionsCallback?: Children | ((sel: Set<string>) => Children),
|
||||||
recordActionsCallback?: Children | ((record: Record<string, any>) => Children)
|
recordActionsCallback?:
|
||||||
|
| Children
|
||||||
|
| ((record: Record<string, any>) => Children),
|
||||||
): Children {
|
): Children {
|
||||||
const records = data || [];
|
const records = data || [];
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ function renderTable(
|
|||||||
onSort(i);
|
onSort(i);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
symbol
|
symbol,
|
||||||
);
|
);
|
||||||
|
|
||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
@@ -162,8 +164,8 @@ function renderTable(
|
|||||||
if (!selected.delete(id)) selected.add(id);
|
if (!selected.delete(id)) selected.add(id);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tds
|
tds,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,19 +186,19 @@ function renderTable(
|
|||||||
disabled:
|
disabled:
|
||||||
!data.length || records.length >= Math.min(MAX_PAGE_SIZE, total),
|
!data.length || records.length >= Math.min(MAX_PAGE_SIZE, total),
|
||||||
},
|
},
|
||||||
"More"
|
"More",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl) {
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const children = [
|
const children = [
|
||||||
@@ -204,7 +206,7 @@ function renderTable(
|
|||||||
"table.table.highlight",
|
"table.table.highlight",
|
||||||
m("thead", m("tr", labels)),
|
m("thead", m("tr", labels)),
|
||||||
m("tbody", rows),
|
m("tbody", rows),
|
||||||
tfoot
|
tfoot,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -241,7 +243,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
const sortArray = new Set(
|
const sortArray = new Set(
|
||||||
Object.keys(sortAttributes)
|
Object.keys(sortAttributes)
|
||||||
.map((x) => (parseInt(x) + 1) * sortAttributes[x])
|
.map((x) => (parseInt(x) + 1) * sortAttributes[x])
|
||||||
.filter((x) => x)
|
.filter((x) => x),
|
||||||
);
|
);
|
||||||
for (const num of events) {
|
for (const num of events) {
|
||||||
if (sortArray.delete(num + 1)) sortArray.add(-(num + 1));
|
if (sortArray.delete(num + 1)) sortArray.add(-(num + 1));
|
||||||
@@ -263,7 +265,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
downloadUrl,
|
downloadUrl,
|
||||||
valueCallback,
|
valueCallback,
|
||||||
actionsCallback,
|
actionsCallback,
|
||||||
recordActionsCallback
|
recordActionsCallback,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -57,7 +57,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"div.logo",
|
"div.logo",
|
||||||
m("img", { src: LOGO_SVG }),
|
m("img", { src: LOGO_SVG }),
|
||||||
m("span.version", "v" + VERSION)
|
m("span.version", "v" + VERSION),
|
||||||
),
|
),
|
||||||
m(userMenu),
|
m(userMenu),
|
||||||
m(menu, attrs),
|
m(menu, attrs),
|
||||||
@@ -68,7 +68,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
sideMenu,
|
sideMenu,
|
||||||
m("#content", { class: `page-${vnode.attrs["page"]}` }, [
|
m("#content", { class: `page-${vnode.attrs["page"]}` }, [
|
||||||
vnode.children,
|
vnode.children,
|
||||||
])
|
]),
|
||||||
),
|
),
|
||||||
overlay.render(),
|
overlay.render(),
|
||||||
m(datalist),
|
m(datalist),
|
||||||
|
|||||||
+7
-7
@@ -25,7 +25,7 @@ import * as overlay from "./overlay";
|
|||||||
import changePasswordComponent from "./change-password-component";
|
import changePasswordComponent from "./change-password-component";
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
return Promise.resolve(args);
|
return Promise.resolve(args);
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
oninput: (e) => {
|
oninput: (e) => {
|
||||||
vnode.state["username"] = e.target.value;
|
vnode.state["username"] = e.target.value;
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"p",
|
"p",
|
||||||
@@ -65,7 +65,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
oninput: (e) => {
|
oninput: (e) => {
|
||||||
vnode.state["password"] = e.target.value;
|
vnode.state["password"] = e.target.value;
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"p",
|
"p",
|
||||||
@@ -87,9 +87,9 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Login"
|
"Login",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"a",
|
"a",
|
||||||
@@ -107,7 +107,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
overlay.open(cb);
|
overlay.open(cb);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Change password"
|
"Change password",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
onupdate: (vnode2) => {
|
onupdate: (vnode2) => {
|
||||||
if (vnode2.dom.clientWidth === vnode2.dom.scrollWidth) {
|
if (vnode2.dom.clientWidth === vnode2.dom.scrollWidth) {
|
||||||
(vnode2.dom as HTMLElement).classList.remove(
|
(vnode2.dom as HTMLElement).classList.remove(
|
||||||
"long-text-overflowed"
|
"long-text-overflowed",
|
||||||
);
|
);
|
||||||
(vnode2.dom as HTMLElement).onclick = null;
|
(vnode2.dom as HTMLElement).onclick = null;
|
||||||
} else {
|
} else {
|
||||||
@@ -67,7 +67,7 @@ const component: ClosureComponent = (): Component => {
|
|||||||
class: "long-text",
|
class: "long-text",
|
||||||
title: text,
|
title: text,
|
||||||
},
|
},
|
||||||
text
|
text,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-8
@@ -30,8 +30,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["overview"] },
|
{ class: active["overview"] },
|
||||||
m("a", { href: "#!/overview" }, "Overview")
|
m("a", { href: "#!/overview" }, "Overview"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["devices"] },
|
{ class: active["devices"] },
|
||||||
m("a", { href: "#!/devices" }, "Devices")
|
m("a", { href: "#!/devices" }, "Devices"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["faults"] },
|
{ class: active["faults"] },
|
||||||
m("a", { href: "#!/faults" }, "Faults")
|
m("a", { href: "#!/faults" }, "Faults"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +67,8 @@ const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
{ class: active["admin"] },
|
{ class: active["admin"] },
|
||||||
m("a", { href: "#!/admin" }, "Admin")
|
m("a", { href: "#!/admin" }, "Admin"),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ const notifications = new Set<Notification>();
|
|||||||
export function push(
|
export function push(
|
||||||
type: string,
|
type: string,
|
||||||
message: string,
|
message: string,
|
||||||
actions?: { [label: string]: () => void }
|
actions?: { [label: string]: () => void },
|
||||||
): Notification {
|
): Notification {
|
||||||
const n: Notification = {
|
const n: Notification = {
|
||||||
type: type,
|
type: type,
|
||||||
|
|||||||
+3
-3
@@ -27,7 +27,7 @@ let closeCallback: CloseCallback = null;
|
|||||||
|
|
||||||
export function open(
|
export function open(
|
||||||
callback: OverlayCallback,
|
callback: OverlayCallback,
|
||||||
closeCb: CloseCallback = null
|
closeCb: CloseCallback = null,
|
||||||
): void {
|
): void {
|
||||||
overlayCallback = callback;
|
overlayCallback = callback;
|
||||||
closeCallback = closeCb;
|
closeCallback = closeCb;
|
||||||
@@ -74,8 +74,8 @@ export function render(): Children {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
overlayCallback()
|
overlayCallback(),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ function queryCharts(charts: Record<string, unknown>): Record<string, unknown> {
|
|||||||
export function init(): Promise<Record<string, unknown>> {
|
export function init(): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("devices", 1)) {
|
if (!window.authorizer.hasAccess("devices", 1)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-20
@@ -129,7 +129,7 @@ function putActionHandler(action, _object, isNew): Promise<ValidationErrors> {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`Permission ${exists ? "updated" : "created"}`
|
`Permission ${exists ? "updated" : "created"}`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -170,11 +170,11 @@ const getDownloadUrl = memoize((filter) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function init(
|
export function init(
|
||||||
args: Record<string, unknown>
|
args: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
if (!window.authorizer.hasAccess("permissions", 2)) {
|
if (!window.authorizer.hasAccess("permissions", 2)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error("You are not authorized to view this page")
|
new Error("You are not authorized to view this page"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const sort = args.hasOwnProperty("sort") ? "" + args["sort"] : "";
|
const sort = args.hasOwnProperty("sort") ? "" + args["sort"] : "";
|
||||||
@@ -274,13 +274,13 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
base: permission,
|
base: permission,
|
||||||
oncreate: (_vnode) => {
|
oncreate: (_vnode) => {
|
||||||
_vnode.dom.querySelector(
|
_vnode.dom.querySelector(
|
||||||
"input[name='role']"
|
"input[name='role']",
|
||||||
).disabled = true;
|
).disabled = true;
|
||||||
_vnode.dom.querySelector(
|
_vnode.dom.querySelector(
|
||||||
"select[name='access']"
|
"select[name='access']",
|
||||||
).disabled = true;
|
).disabled = true;
|
||||||
_vnode.dom.querySelector(
|
_vnode.dom.querySelector(
|
||||||
"select[name='resource']"
|
"select[name='resource']",
|
||||||
).disabled = true;
|
).disabled = true;
|
||||||
},
|
},
|
||||||
actionHandler: (action, object) => {
|
actionHandler: (action, object) => {
|
||||||
@@ -305,19 +305,19 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formData
|
formData,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => comp;
|
cb = () => comp;
|
||||||
overlay.open(
|
overlay.open(
|
||||||
cb,
|
cb,
|
||||||
() =>
|
() =>
|
||||||
!comp.state["current"]["modified"] ||
|
!comp.state["current"]["modified"] ||
|
||||||
confirm("You have unsaved changes. Close anyway?")
|
confirm("You have unsaved changes. Close anyway?"),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Show"
|
"Show",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -356,19 +356,19 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formData
|
formData,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
cb = () => comp;
|
cb = () => comp;
|
||||||
overlay.open(
|
overlay.open(
|
||||||
cb,
|
cb,
|
||||||
() =>
|
() =>
|
||||||
!comp.state["current"]["modified"] ||
|
!comp.state["current"]["modified"] ||
|
||||||
confirm("You have unsaved changes. Close anyway?")
|
confirm("You have unsaved changes. Close anyway?"),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"New"
|
"New",
|
||||||
),
|
),
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
@@ -378,7 +378,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
`Deleting ${selected.size} permissions. Are you sure?`
|
`Deleting ${selected.size} permissions. Are you sure?`,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
@@ -387,13 +387,13 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
Promise.all(
|
Promise.all(
|
||||||
Array.from(selected).map((id) =>
|
Array.from(selected).map((id) =>
|
||||||
store.deleteResource("permissions", id)
|
store.deleteResource("permissions", id),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
`${res.length} permissions deleted`
|
`${res.length} permissions deleted`,
|
||||||
);
|
);
|
||||||
store.setTimestamp(Date.now());
|
store.setTimestamp(Date.now());
|
||||||
})
|
})
|
||||||
@@ -403,7 +403,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Delete"
|
"Delete",
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -421,7 +421,7 @@ export const component: ClosureComponent = (): Component => {
|
|||||||
m(
|
m(
|
||||||
"loading",
|
"loading",
|
||||||
{ queries: [permissions, count] },
|
{ queries: [permissions, count] },
|
||||||
m(indexTableComponent, attrs)
|
m(indexTableComponent, attrs),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user