Update dependencies

This commit is contained in:
Zaid Abdulla committed 2022-08-22 09:44:25 -07:00
1 parent 2789961596
commit 25ea0c8b64
14 files changed
+3446 -3944

No files matched your search

+3 -2
View File
@@ -28,6 +28,7 @@ import commonjs from "@rollup/plugin-commonjs";
import postcss from "postcss"; import postcss from "postcss";
import postcssImport from "postcss-import"; import postcssImport from "postcss-import";
import postcssPresetEnv from "postcss-preset-env"; import postcssPresetEnv from "postcss-preset-env";
import postcssColorMod from "postcss-color-mod-function";
import cssnano from "cssnano"; import cssnano from "cssnano";
import { optimize } from "svgo"; import { optimize } from "svgo";
import * as xmlParser from "../lib/xml-parser"; import * as xmlParser from "../lib/xml-parser";
@@ -185,10 +186,10 @@ async function generateCss(): Promise<void> {
stage: 3, stage: 3,
features: { features: {
"nesting-rules": true, "nesting-rules": true,
"color-mod-function": true,
}, },
}), }),
cssnano, postcssColorMod,
...(MODE === "production" ? [cssnano] : []),
]).process(cssIn, { from: cssInPath, to: cssOutPath }); ]).process(cssIn, { from: cssInPath, to: cssOutPath });
fs.writeFileSync(cssOutPath, cssOut.css); fs.writeFileSync(cssOutPath, cssOut.css);
} }
+9 -12
View File
@@ -20,11 +20,12 @@
import { Collection } from "mongodb"; import { Collection } from "mongodb";
import { onConnect } from "./db"; import { onConnect } from "./db";
import * as config from "./config"; import * as config from "./config";
import { Cache } from "./mongodb-types";
const CLOCK_SKEW_TOLERANCE = 30000; const CLOCK_SKEW_TOLERANCE = 30000;
const MAX_CACHE_TTL = +config.get("MAX_CACHE_TTL"); const MAX_CACHE_TTL = +config.get("MAX_CACHE_TTL");
let cacheCollection: Collection; let cacheCollection: Collection<Cache>;
onConnect(async (db) => { onConnect(async (db) => {
cacheCollection = db.collection("cache"); cacheCollection = db.collection("cache");
@@ -33,8 +34,7 @@ onConnect(async (db) => {
export async function get(key: string): Promise<string> { export async function get(key: string): Promise<string> {
const res = await cacheCollection.findOne({ _id: key }); const res = await cacheCollection.findOne({ _id: key });
if (res) return res["value"]; return res?.value;
return null;
} }
export async function del(key: string): Promise<void> { export async function del(key: string): Promise<void> {
@@ -52,15 +52,14 @@ export async function set(
); );
await cacheCollection.replaceOne( await cacheCollection.replaceOne(
{ _id: key }, { _id: key },
{ _id: key, value, expire, timestamp }, { value, expire, timestamp },
{ upsert: true } { upsert: true }
); );
} }
export async function pop(key: string): Promise<any> { export async function pop(key: string): Promise<string> {
const res = await cacheCollection.findOneAndDelete({ _id: key }); const res = await cacheCollection.findOneAndDelete({ _id: key });
if (res?.["value"]) return res["value"]["value"]; return res.value?.value;
return null;
} }
export async function acquireLock( export async function acquireLock(
@@ -72,18 +71,16 @@ export async function acquireLock(
try { try {
const now = Date.now(); const now = Date.now();
const r = await cacheCollection.findOneAndUpdate( const r = await cacheCollection.findOneAndUpdate(
{ _id: lockName, token }, { _id: lockName, value: token },
{ {
$set: { $set: {
value: token,
expire: new Date(now + ttl + CLOCK_SKEW_TOLERANCE), expire: new Date(now + ttl + CLOCK_SKEW_TOLERANCE),
}, },
$currentDate: { timestamp: true as unknown as Date }, $currentDate: { timestamp: true },
}, },
{ upsert: true, returnDocument: "after" } { upsert: true, returnDocument: "after" }
); );
const v = r.value; if (Math.abs(r.value.timestamp.getTime() - now) > CLOCK_SKEW_TOLERANCE)
if (Math.abs(v["timestamp"].getTime() - now) > CLOCK_SKEW_TOLERANCE)
throw new Error("Database clock skew too great"); throw new Error("Database clock skew too great");
} catch (err) { } catch (err) {
if (err.code !== 11000) throw err; if (err.code !== 11000) throw err;
+1 -1
View File
@@ -536,7 +536,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
session.configContextCallback(sessionContext, e) session.configContextCallback(sessionContext, e)
) )
), ),
]); ]) as [string, ...Expression[]][];
if (blackList[p.channel] === 2) { if (blackList[p.channel] === 2) {
appendProvisionsToFaults[p.channel] = ( appendProvisionsToFaults[p.channel] = (
appendProvisionsToFaults[p.channel] || [] appendProvisionsToFaults[p.channel] || []
+85 -39
View File
@@ -30,19 +30,20 @@ import {
Expression, Expression,
} from "./types"; } from "./types";
import Path from "./common/path"; import Path from "./common/path";
import * as MongoTypes from "./mongodb-types";
export let tasksCollection: Collection, export let tasksCollection: Collection<MongoTypes.Task>,
devicesCollection: Collection, devicesCollection: Collection,
presetsCollection: Collection, presetsCollection: Collection,
objectsCollection: Collection, objectsCollection: Collection,
provisionsCollection: Collection, provisionsCollection: Collection,
virtualParametersCollection: Collection, virtualParametersCollection: Collection,
faultsCollection: Collection, faultsCollection: Collection<MongoTypes.Fault>,
filesCollection: Collection, filesCollection: Collection,
operationsCollection: Collection, operationsCollection: Collection<MongoTypes.Operation>,
permissionsCollection: Collection, permissionsCollection: Collection,
usersCollection: Collection, usersCollection: Collection,
configCollection: Collection; configCollection: Collection<MongoTypes.Config>;
let clientPromise: Promise<MongoClient>; let clientPromise: Promise<MongoClient>;
@@ -643,12 +644,16 @@ export async function getFaults(
const faults: { [channel: string]: SessionFault } = {}; const faults: { [channel: string]: SessionFault } = {};
for (const r of res) { for (const r of res) {
const channel = r._id.slice(deviceId.length + 1); const channel = r._id.slice(deviceId.length + 1);
delete r._id; const fault: SessionFault = {
delete r.channel; code: r.code,
delete r.device; message: r.message,
r.timestamp = +r.timestamp; ...(r.detail && { detail: r.detail }),
r.provisions = JSON.parse(r.provisions); timestamp: +r.timestamp,
faults[channel] = r as SessionFault; provisions: JSON.parse(r.provisions),
retries: r.retries,
...(r.expiry && { expiry: +r.expiry }),
};
faults[channel] = fault;
} }
return faults; return faults;
@@ -660,12 +665,18 @@ export async function saveFault(
fault: SessionFault fault: SessionFault
): Promise<void> { ): Promise<void> {
const id = `${deviceId}:${channel}`; const id = `${deviceId}:${channel}`;
const f = Object.assign({}, fault) as Record<string, any>; const f: MongoTypes.Fault = {
f["_id"] = id; _id: id,
f["device"] = deviceId; device: deviceId,
f["channel"] = channel; channel: channel,
f["timestamp"] = new Date(fault.timestamp); timestamp: new Date(fault.timestamp),
f["provisions"] = JSON.stringify(fault.provisions); code: fault.code,
message: fault.message,
...(fault.detail && { detail: fault.detail }),
retries: fault.retries,
...(fault.expiry && { expiry: new Date(fault.expiry) }),
provisions: JSON.stringify(fault.provisions),
};
await faultsCollection.replaceOne({ _id: id }, f, { upsert: true }); await faultsCollection.replaceOne({ _id: id }, f, { upsert: true });
} }
@@ -683,20 +694,47 @@ export async function getDueTasks(
const cur = tasksCollection.find({ device: deviceId }).sort({ timestamp: 1 }); const cur = tasksCollection.find({ device: deviceId }).sort({ timestamp: 1 });
const tasks = [] as Task[]; const tasks = [] as Task[];
for await (const task of cur) { for await (const t of cur) {
if (task.timestamp) task.timestamp = +task.timestamp; if (+t.timestamp >= timestamp) return [tasks, +t.timestamp];
if (task.expiry) task.expiry = +task.expiry; const task: Task = {
if (task.timestamp >= timestamp) return [tasks, +task.timestamp]; _id: t._id.toString(),
task._id = String(task._id); name: t.name,
...(t.timestamp && { timestamp: +t.timestamp }),
...(t.expiry && { expiry: +t.expiry }),
...(t.name === "getParameterValues" && {
parameterNames: t.parameterNames,
}),
...(t.name === "setParameterValues" && {
parameterValues: t.parameterValues,
}),
...(t.name === "refreshObject" && {
objectName: t.objectName,
}),
...(t.name === "download" && {
fileType: t.fileType,
fileName: t.fileName,
targetFileName: t.targetFileName,
}),
...(t.name === "addObject" && {
objectName: t.objectName,
parameterValues: t.parameterValues,
}),
...(t.name === "deleteObject" && {
objectName: t.objectName,
}),
...(t.name === "provisions" && {
provisions: t.provisions,
}),
};
tasks.push(task as Task); tasks.push(task);
// For API compatibility // For API compatibility
if (task.name === "download" && task.file) { if (task.name === "download" && t["file"]) {
let q; let q;
if (ObjectId.isValid(task.file)) if (ObjectId.isValid(t["file"]))
q = { _id: { $in: [task.file, new ObjectId(task.file)] } }; q = { _id: { $in: [t["file"], new ObjectId(t["file"])] } };
else q = { _id: task.file }; else q = { _id: t["file"] };
const res = await filesCollection.find(q).toArray(); const res = await filesCollection.find(q).toArray();
@@ -730,17 +768,22 @@ export async function getOperations(
const operations: { [commandKey: string]: Operation } = {}; const operations: { [commandKey: string]: Operation } = {};
for (const r of res) { for (const r of res) {
const commandKey = r._id.slice(deviceId.length + 1); const commandKey = r._id.slice(deviceId.length + 1);
delete r._id;
// Workaround for a bug in v1.2.1 where operation object is saved without deserialization // Workaround for a bug in v1.2.1 where operation object is saved without deserialization
if (typeof r.provisions !== "string") { if (typeof r.provisions !== "string") {
operations[commandKey] = r as Operation; delete r._id;
operations[commandKey] = r as unknown as Operation;
continue; continue;
} }
r.timestamp = +r.timestamp; const operation: Operation = {
if (r.args) r.args = JSON.parse(r.args); name: r.name,
r.provisions = JSON.parse(r.provisions); timestamp: +r.timestamp,
r.retries = JSON.parse(r.retries); channels:
operations[commandKey] = r as Operation; typeof r.channels === "string" ? JSON.parse(r.channels) : r.channels,
retries: JSON.parse(r.retries),
provisions: JSON.parse(r.provisions),
...(r.args && { args: JSON.parse(r.args) }),
};
operations[commandKey] = operation;
} }
return operations; return operations;
} }
@@ -751,12 +794,15 @@ export async function saveOperation(
operation: Operation operation: Operation
): Promise<void> { ): Promise<void> {
const id = `${deviceId}:${commandKey}`; const id = `${deviceId}:${commandKey}`;
const o = Object.assign({}, operation) as Record<string, any>; const o: MongoTypes.Operation = {
o["_id"] = id; _id: id,
o["timestamp"] = new Date(operation.timestamp); name: operation.name,
o["provisions"] = JSON.stringify(operation.provisions); timestamp: new Date(operation.timestamp),
o["retries"] = JSON.stringify(operation.retries); channels: JSON.stringify(operation.channels),
o["args"] = JSON.stringify(operation.args); provisions: JSON.stringify(operation.provisions),
retries: JSON.stringify(operation.retries),
args: JSON.stringify(operation.args),
};
await operationsCollection.replaceOne({ _id: id }, o, { await operationsCollection.replaceOne({ _id: id }, o, {
upsert: true, upsert: true,
}); });
+107
View File
@@ -0,0 +1,107 @@
import { ObjectId } from "mongodb";
import { Expression, FaultStruct } from "./types";
export interface Fault {
_id: string;
device: string;
channel: string;
timestamp: Date;
provisions: string;
retries: number;
code: string;
message: string;
detail?:
| FaultStruct
| {
name: string;
message: string;
stack?: string;
};
expiry?: Date;
}
interface TaskBase {
_id: ObjectId;
timestamp?: Date;
expiry?: Date;
name: string;
}
interface TaskGetParameterValues extends TaskBase {
name: "getParameterValues";
parameterNames: string[];
}
interface TaskSetParameterValues extends TaskBase {
name: "setParameterValues";
parameterValues: [string, string | number | boolean, string?][];
}
interface TaskRefreshObject extends TaskBase {
name: "refreshObject";
objectName: string;
}
interface TaskReboot extends TaskBase {
name: "reboot";
}
interface TaskFactoryReset extends TaskBase {
name: "factoryReset";
}
interface TaskDownload extends TaskBase {
name: "download";
fileType: string;
fileName: string;
targetFileName?: string;
}
interface TaskAddObject extends TaskBase {
name: "addObject";
objectName: string;
parameterValues: [string, string | number | boolean, string?][];
}
interface TaskDeleteObject extends TaskBase {
name: "deleteObject";
objectName: string;
}
interface TaskProvisions extends TaskBase {
name: "provisions";
provisions?: [string, ...Expression[]][];
}
export type Task =
| TaskGetParameterValues
| TaskSetParameterValues
| TaskRefreshObject
| TaskReboot
| TaskFactoryReset
| TaskDownload
| TaskAddObject
| TaskDeleteObject
| TaskProvisions;
export interface Operation {
_id: string;
name: string;
timestamp: Date;
channels: string;
retries: string;
provisions: string;
args: string;
}
export interface Config {
_id: string;
value: string;
}
export interface Cache {
_id: string;
value: string;
timestamp: Date;
expire: Date;
}
+1 -1
View File
@@ -845,7 +845,7 @@ export function listener(
cur.sort(sort); cur.sort(sort);
} }
cur.count((err, total) => { collection.countDocuments(q, (err, total) => {
if (err) return void throwError(err); if (err) return void throwError(err);
response.writeHead(200, { response.writeHead(200, {
+1 -1
View File
@@ -504,7 +504,7 @@ export async function timeoutOperations(
export function addProvisions( export function addProvisions(
sessionContext: SessionContext, sessionContext: SessionContext,
channel: string, channel: string,
provisions: (string | number | boolean)[][] provisions: [string, ...Expression[]][]
): void { ): void {
// Multiply by two because every iteration is two // Multiply by two because every iteration is two
// phases: read and update // phases: read and update
+1 -1
View File
@@ -180,7 +180,7 @@ export interface Task {
fileName?: string; fileName?: string;
targetFileName?: string; targetFileName?: string;
expiry?: number; expiry?: number;
provisions?: (string | number | boolean)[][]; provisions?: [string, ...Expression[]][];
} }
export interface Operation { export interface Operation {
+15 -19
View File
@@ -115,30 +115,31 @@ export function query(
cursor.toArray((err, docs) => { cursor.toArray((err, docs) => {
if (err) return reject(err); if (err) return reject(err);
if (resource === "devices") if (resource === "devices")
docs = docs.map((d) => mongodbFunctions.flattenDevice(d)); return resolve(docs.map((d) => mongodbFunctions.flattenDevice(d)));
else if (resource === "faults") else if (resource === "faults")
docs = docs.map((d) => mongodbFunctions.flattenFault(d)); return resolve(docs.map((d) => mongodbFunctions.flattenFault(d)));
else if (resource === "tasks") else if (resource === "tasks")
docs = docs.map((d) => mongodbFunctions.flattenTask(d)); return resolve(docs.map((d) => mongodbFunctions.flattenTask(d)));
else if (resource === "presets") else if (resource === "presets")
docs = docs.map((d) => mongodbFunctions.flattenPreset(d)); return resolve(docs.map((d) => mongodbFunctions.flattenPreset(d)));
else if (resource === "files") else if (resource === "files")
docs = docs.map((d) => mongodbFunctions.flattenFile(d)); return resolve(docs.map((d) => mongodbFunctions.flattenFile(d)));
return resolve(docs); return resolve(docs);
}); });
} else { } else {
cursor.forEach( cursor.forEach(
(doc) => { (doc) => {
if (resource === "devices") doc = mongodbFunctions.flattenDevice(doc); if (resource === "devices")
callback(mongodbFunctions.flattenDevice(doc));
else if (resource === "faults") else if (resource === "faults")
doc = mongodbFunctions.flattenFault(doc); callback(mongodbFunctions.flattenFault(doc));
else if (resource === "tasks") else if (resource === "tasks")
doc = mongodbFunctions.flattenTask(doc); callback(mongodbFunctions.flattenTask(doc));
else if (resource === "presets") else if (resource === "presets")
doc = mongodbFunctions.flattenPreset(doc); callback(mongodbFunctions.flattenPreset(doc));
else if (resource === "files") else if (resource === "files")
doc = mongodbFunctions.flattenFile(doc); callback(mongodbFunctions.flattenFile(doc));
callback(doc); else callback(doc);
}, },
(err) => { (err) => {
if (err) reject(err); if (err) reject(err);
@@ -150,7 +151,8 @@ export function query(
} }
export function count(resource: string, filter: Expression): Promise<number> { export function count(resource: string, filter: Expression): Promise<number> {
let q; const collection = db.collection(RESOURCE_COLLECTION[resource] || resource);
let q: Parameters<typeof collection.countDocuments>[0];
filter = expression.evaluate(filter, null, Date.now()); filter = expression.evaluate(filter, null, Date.now());
filter = minimize(filter, true); filter = minimize(filter, true);
@@ -166,13 +168,7 @@ export function count(resource: string, filter: Expression): Promise<number> {
return Promise.resolve(0); return Promise.resolve(0);
} }
return new Promise((resolve, reject) => { return collection.countDocuments(q);
const collection = db.collection(RESOURCE_COLLECTION[resource] || resource);
collection.find(q).count((err, c) => {
if (err) reject(err);
else resolve(c);
});
});
} }
export async function updateDeviceTags( export async function updateDeviceTags(
+3191 -3837
View File
File diff suppressed because it is too large. Load diff
+28 -27
View File
@@ -28,46 +28,47 @@
"koa-bodyparser": "^4.3.0", "koa-bodyparser": "^4.3.0",
"koa-compress": "^5.1.0", "koa-compress": "^5.1.0",
"koa-jwt": "^4.0.3", "koa-jwt": "^4.0.3",
"koa-router": "^10.1.1", "koa-router": "^12.0.0",
"koa-static": "^5.0.0", "koa-static": "^5.0.0",
"mongodb": "^4.1.3", "mongodb": "^4.9.0",
"parsimmon": "^1.18.0", "parsimmon": "^1.18.1",
"seedrandom": "^3.0.5" "seedrandom": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-json": "^4.1.0", "@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.6", "@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.0", "@rollup/plugin-typescript": "^8.3.4",
"@types/codemirror": "^5.60.5", "@types/codemirror": "^5.60.5",
"@types/jsonwebtoken": "^8.5.5", "@types/jsonwebtoken": "^8.5.8",
"@types/koa": "^2.13.4", "@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.8", "@types/mithril": "^2.0.11",
"@types/node": "^16.11.6", "@types/node": "^18.7.9",
"@types/parsimmon": "^1.10.6", "@types/parsimmon": "^1.10.6",
"@types/seedrandom": "^3.0.1", "@types/seedrandom": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^5.2.0", "@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.2.0", "@typescript-eslint/parser": "^5.33.1",
"ava": "^3.15.0", "ava": "^3.15.0",
"codemirror": "^5.63.3", "codemirror": "^5.65.8",
"cssnano": "^5.0.8", "cssnano": "^5.1.13",
"eslint": "^8.1.0", "eslint": "^8.22.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"esm": "^3.2.25", "esm": "^3.2.25",
"mithril": "^2.0.4", "mithril": "^2.2.2",
"postcss": "^8.3.11", "postcss": "^8.4.16",
"postcss-import": "^14.0.2", "postcss-color-mod-function": "^3.0.3",
"postcss-preset-env": "^6.7.0", "postcss-import": "^14.1.0",
"prettier": "^2.4.1", "postcss-preset-env": "^7.8.0",
"rollup": "^2.58.3", "prettier": "^2.7.1",
"rollup": "^2.78.1",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"sql.js": "^1.6.2", "sql.js": "^1.7.0",
"svgo": "^2.7.0", "svgo": "^2.8.0",
"ts-node": "^10.4.0", "ts-node": "^10.9.1",
"typescript": "^4.4.4", "typescript": "^4.7.4",
"yaml": "^1.10.2" "yaml": "^1.10.2"
}, },
"engines": { "engines": {
+1 -1
View File
@@ -1,7 +1,7 @@
import ava, { TestInterface } from "ava"; import ava, { TestInterface } from "ava";
import { minimize, unionDiff } from "../lib/common/boolean-expression"; import { minimize, unionDiff } from "../lib/common/boolean-expression";
import { parse, stringify } from "../lib/common/expression-parser"; import { parse, stringify } from "../lib/common/expression-parser";
import initSqlJs from "sql.js"; import initSqlJs from "sql.js/dist/sql-asm.js";
const test = ava as TestInterface<{ const test = ava as TestInterface<{
query: (filter: string) => Promise<Set<number>>; query: (filter: string) => Promise<Set<number>>;
+2 -2
View File
@@ -99,9 +99,9 @@ export default class Autocomplete {
if (this.visible) this.hide(); if (this.visible) this.hide();
return; return;
} }
this.container.style.left = `${domRect.left + window.pageXOffset}px`; this.container.style.left = `${domRect.left + window.scrollX}px`;
this.container.style.width = `${domRect.width}px`; this.container.style.width = `${domRect.width}px`;
this.container.style.top = `${domRect.bottom + window.pageYOffset}px`; this.container.style.top = `${domRect.bottom + window.scrollY}px`;
} }
private hide(): void { private hide(): void {
+1 -1
View File
@@ -648,7 +648,7 @@ span.na {
span.tag { span.tag {
display: inline-block; display: inline-block;
background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="16"><path d="M6.59 15.42l-6-6a2 2 0 0 1 0-2.83l6-6A2 2 0 0 1 8 0h180a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-1.41-.58zM4.35 6.59A2 2 0 1 0 7.17 9.4 2 2 0 0 0 4.35 6.6z" fill="#e8d5b2"></path></svg>'); background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="16"><path d="M6.59 15.42l-6-6a2 2 0 0 1 0-2.83l6-6A2 2 0 0 1 8 0h180a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-1.41-.58zM4.35 6.59A2 2 0 1 0 7.17 9.4 2 2 0 0 0 4.35 6.6z" fill="%23e8d5b2"></path></svg>');
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
border-radius: 0.2em; border-radius: 0.2em;