Add linting rules
This commit is contained in:
1 parent
75a5a42296
commit
c702003c32
43 files changed
+695
-566
No files matched your search
+4
-1
@@ -10,6 +10,9 @@
|
|||||||
"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", "consistent"]
|
"curly": ["error", "multi-or-nest", "consistent"],
|
||||||
|
"consistent-return": ["error", {"treatUndefinedAsUnspecified": true}],
|
||||||
|
"no-buffer-constructor": "error",
|
||||||
|
"prefer-const": ["error", {"destructuring": "all"}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -51,14 +51,14 @@ function pagify(pageName, page) {
|
|||||||
if (!page.init) {
|
if (!page.init) {
|
||||||
state = null;
|
state = null;
|
||||||
fulfill();
|
fulfill();
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
page
|
page
|
||||||
.init(args)
|
.init(args)
|
||||||
.then(st => {
|
.then(st => {
|
||||||
if (!st) return m.route.set("/");
|
if (!st) return void m.route.set("/");
|
||||||
state = st;
|
state = st;
|
||||||
resolve();
|
resolve();
|
||||||
fulfill();
|
fulfill();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Autocomplete {
|
|||||||
|
|
||||||
el.addEventListener("focus", () => {
|
el.addEventListener("focus", () => {
|
||||||
this.element = el;
|
this.element = el;
|
||||||
let domRect = el.getBoundingClientRect();
|
const domRect = el.getBoundingClientRect();
|
||||||
this.container.style.left = `${domRect.left}px`;
|
this.container.style.left = `${domRect.left}px`;
|
||||||
this.container.style.width = `${domRect.width}px`;
|
this.container.style.width = `${domRect.width}px`;
|
||||||
this.container.style.top = `${domRect.bottom}px`;
|
this.container.style.top = `${domRect.bottom}px`;
|
||||||
@@ -36,13 +36,15 @@ class Autocomplete {
|
|||||||
|
|
||||||
el.addEventListener("keydown", e => {
|
el.addEventListener("keydown", e => {
|
||||||
if (this.element !== el) return;
|
if (this.element !== el) return;
|
||||||
if (e.key === "Escape") this.hide();
|
if (e.key === "Escape") {
|
||||||
else if (e.key === "Enter")
|
this.hide();
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
if (this.default != null) {
|
if (this.default != null) {
|
||||||
el.value = this.default;
|
el.value = this.default;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener("input", () => {
|
el.addEventListener("input", () => {
|
||||||
@@ -94,10 +96,10 @@ class Autocomplete {
|
|||||||
|
|
||||||
this.default = suggestions[0];
|
this.default = suggestions[0];
|
||||||
|
|
||||||
for (let suggestion of suggestions) {
|
for (const suggestion of suggestions) {
|
||||||
let e = document.createElement("div");
|
const e = document.createElement("div");
|
||||||
e.className = "suggestion";
|
e.className = "suggestion";
|
||||||
let t = document.createTextNode(suggestion);
|
const t = document.createTextNode(suggestion);
|
||||||
e.appendChild(t);
|
e.appendChild(t);
|
||||||
e.addEventListener("mousedown", ev => {
|
e.addEventListener("mousedown", ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const component = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
const device = vnode.attrs.device;
|
const device = vnode.attrs.device;
|
||||||
|
|
||||||
let search = m("input", {
|
const search = m("input", {
|
||||||
type: "text",
|
type: "text",
|
||||||
placeholder: "Search parameters",
|
placeholder: "Search parameters",
|
||||||
oninput: e => {
|
oninput: e => {
|
||||||
@@ -20,10 +20,10 @@ const component = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let instanceRegex = /\.[0-9]+$/;
|
const instanceRegex = /\.[0-9]+$/;
|
||||||
let re;
|
let re;
|
||||||
if (vnode.state.searchString) {
|
if (vnode.state.searchString) {
|
||||||
let keywords = vnode.state.searchString.split(" ").filter(s => s);
|
const keywords = vnode.state.searchString.split(" ").filter(s => s);
|
||||||
if (keywords.length)
|
if (keywords.length)
|
||||||
re = new RegExp(keywords.map(s => escapeRegExp(s)).join(".*"), "i");
|
re = new RegExp(keywords.map(s => escapeRegExp(s)).join(".*"), "i");
|
||||||
}
|
}
|
||||||
@@ -31,22 +31,22 @@ const component = {
|
|||||||
const rows = Object.keys(device)
|
const rows = Object.keys(device)
|
||||||
.sort()
|
.sort()
|
||||||
.map(k => {
|
.map(k => {
|
||||||
let p = device[k];
|
const p = device[k];
|
||||||
let val = [];
|
const val = [];
|
||||||
let attrs = {};
|
const attrs = {};
|
||||||
if (re) {
|
if (re) {
|
||||||
let str = p.value && p.value[0] ? `${k} ${p.value[0]}` : k;
|
const str = p.value && p.value[0] ? `${k} ${p.value[0]}` : k;
|
||||||
if (!re.test(str)) attrs.style = "display: none;";
|
if (!re.test(str)) attrs.style = "display: none;";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.object === false)
|
if (p.object === false) {
|
||||||
val.push(
|
val.push(
|
||||||
m(
|
m(
|
||||||
components.get("parameter"),
|
components.get("parameter"),
|
||||||
Object.assign({ device: device, parameter: k })
|
Object.assign({ device: device, parameter: k })
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
else if (p.object && p.writable)
|
} else if (p.object && p.writable) {
|
||||||
if (instanceRegex.test(k)) {
|
if (instanceRegex.test(k)) {
|
||||||
val.push(
|
val.push(
|
||||||
m(
|
m(
|
||||||
@@ -82,6 +82,7 @@ const component = {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val.push(
|
val.push(
|
||||||
m(
|
m(
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ const component = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
if (vnode.attrs.filter) {
|
if (vnode.attrs.filter) {
|
||||||
const filter = memoizedParse(vnode.attrs.filter);
|
const filter = memoizedParse(vnode.attrs.filter);
|
||||||
if (!store.evaluateExpression(filter, vnode.attrs.device)) return;
|
if (!store.evaluateExpression(filter, vnode.attrs.device)) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let children = Object.values(vnode.attrs.components).map(c => {
|
const children = Object.values(vnode.attrs.components).map(c => {
|
||||||
if (typeof c !== "object") return `${c}`;
|
if (typeof c !== "object") return `${c}`;
|
||||||
const attrs = Object.assign({}, vnode.attrs, c);
|
const attrs = Object.assign({}, vnode.attrs, c);
|
||||||
return m(components.get(attrs.type), attrs);
|
return m(components.get(attrs.type), attrs);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const component = {
|
|||||||
const thead = m("thead", m("tr", headers));
|
const thead = m("thead", m("tr", headers));
|
||||||
|
|
||||||
const rows = [];
|
const rows = [];
|
||||||
for (let f of faults.value)
|
for (const f of faults.value) {
|
||||||
rows.push([
|
rows.push([
|
||||||
m("td", f["channel"]),
|
m("td", f["channel"]),
|
||||||
m("td", f["code"]),
|
m("td", f["code"]),
|
||||||
@@ -51,14 +51,17 @@ const component = {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let tbody;
|
let tbody;
|
||||||
if (rows.length) tbody = m("tbody", rows.map(r => m("tr", r)));
|
if (rows.length) {
|
||||||
else
|
tbody = m("tbody", rows.map(r => m("tr", r)));
|
||||||
|
} 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("table.table", thead, tbody);
|
return m("table.table", thead, tbody);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const component = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
const device = vnode.attrs.device;
|
const device = vnode.attrs.device;
|
||||||
const chart = CHARTS[vnode.attrs.chart];
|
const chart = CHARTS[vnode.attrs.chart];
|
||||||
for (let slice of Object.values(chart.slices)) {
|
for (const slice of Object.values(chart.slices)) {
|
||||||
const filter = memoizedParse(slice.filter);
|
const filter = memoizedParse(slice.filter);
|
||||||
if (store.evaluateExpression(filter, device)) {
|
if (store.evaluateExpression(filter, device)) {
|
||||||
const dot = m(
|
const dot = m(
|
||||||
@@ -35,6 +35,7 @@ const component = {
|
|||||||
return m("span.overview-dot", dot, `${slice.label}`);
|
return m("span.overview-dot", dot, `${slice.label}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as expressionParser from "../../common/expression-parser";
|
|||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
oninit: vnode => {
|
oninit: vnode => {
|
||||||
let obj = expression.parse(vnode.attrs.parameter);
|
const obj = expression.parse(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];
|
||||||
@@ -30,19 +30,20 @@ const component = {
|
|||||||
|
|
||||||
const instances = new Set();
|
const instances = new Set();
|
||||||
const prefix = `${object}.`;
|
const prefix = `${object}.`;
|
||||||
for (let p in device)
|
for (const p in device) {
|
||||||
if (p.startsWith(prefix)) {
|
if (p.startsWith(prefix)) {
|
||||||
let i = p.indexOf(".", prefix.length);
|
const i = p.indexOf(".", prefix.length);
|
||||||
if (i === -1) instances.add(p);
|
if (i === -1) instances.add(p);
|
||||||
else instances.add(p.slice(0, i));
|
else instances.add(p.slice(0, i));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const headers = Object.values(parameters).map(p => m("th", p.label));
|
const headers = Object.values(parameters).map(p => m("th", p.label));
|
||||||
|
|
||||||
const thead = m("thead", m("tr", headers));
|
const thead = m("thead", m("tr", headers));
|
||||||
|
|
||||||
const rows = [];
|
const rows = [];
|
||||||
for (let i of instances) {
|
for (const i of instances) {
|
||||||
const row = parameters.map(p => {
|
const row = parameters.map(p => {
|
||||||
const param = expressionParser.map(p.parameter, e => {
|
const param = expressionParser.map(p.parameter, e => {
|
||||||
if (Array.isArray(e) && e[0] === "PARAM")
|
if (Array.isArray(e) && e[0] === "PARAM")
|
||||||
@@ -62,7 +63,7 @@ const component = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (device[i].writable === true)
|
if (device[i].writable === true) {
|
||||||
row.push(
|
row.push(
|
||||||
m(
|
m(
|
||||||
"td",
|
"td",
|
||||||
@@ -82,15 +83,17 @@ const component = {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
rows.push(m("tr", row));
|
rows.push(m("tr", row));
|
||||||
}
|
}
|
||||||
|
|
||||||
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"))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (device[object].writable === true)
|
if (device[object].writable === true) {
|
||||||
rows.push(
|
rows.push(
|
||||||
m(
|
m(
|
||||||
"tr",
|
"tr",
|
||||||
@@ -114,6 +117,7 @@ const component = {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let label;
|
let label;
|
||||||
if (vnode.attrs.label) label = m("h2", vnode.attrs.label);
|
if (vnode.attrs.label) label = m("h2", vnode.attrs.label);
|
||||||
|
|||||||
@@ -11,17 +11,13 @@ const memoizedParse = memoize(expression.parse);
|
|||||||
|
|
||||||
const evaluateParam = memoize((exp, obj, now) => {
|
const evaluateParam = memoize((exp, obj, now) => {
|
||||||
let timestamp = now;
|
let timestamp = now;
|
||||||
let params = new Set();
|
const params = new Set();
|
||||||
let value = expression.evaluate(
|
const value = expression.evaluate(memoizedParse(exp), obj, now, e => {
|
||||||
memoizedParse(exp),
|
|
||||||
obj,
|
|
||||||
now,
|
|
||||||
e => {
|
|
||||||
if (Array.isArray(e)) {
|
if (Array.isArray(e)) {
|
||||||
if (e[0] === "PARAM") {
|
if (e[0] === "PARAM") {
|
||||||
params.add(e[1]);
|
params.add(e[1]);
|
||||||
if (!Array.isArray(e[1])) {
|
if (!Array.isArray(e[1])) {
|
||||||
let p = obj[e[1]];
|
const p = obj[e[1]];
|
||||||
if (p && p.valueTimestamp)
|
if (p && p.valueTimestamp)
|
||||||
timestamp = Math.min(timestamp, p.valueTimestamp);
|
timestamp = Math.min(timestamp, p.valueTimestamp);
|
||||||
}
|
}
|
||||||
@@ -30,8 +26,7 @@ const evaluateParam = memoize((exp, obj, now) => {
|
|||||||
return new Date(e[2]).toLocaleString();
|
return new Date(e[2]).toLocaleString();
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
let parameter = params.size === 1 ? params.values().next().value : null;
|
let parameter = params.size === 1 ? params.values().next().value : null;
|
||||||
if (Array.isArray(parameter)) parameter = null;
|
if (Array.isArray(parameter)) parameter = null;
|
||||||
@@ -42,7 +37,7 @@ const component = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
const device = vnode.attrs.device;
|
const device = vnode.attrs.device;
|
||||||
|
|
||||||
let { value, timestamp, parameter } = evaluateParam(
|
const { value, timestamp, parameter } = evaluateParam(
|
||||||
vnode.attrs.parameter,
|
vnode.attrs.parameter,
|
||||||
vnode.attrs.device,
|
vnode.attrs.device,
|
||||||
store.getTimestamp()
|
store.getTimestamp()
|
||||||
@@ -51,7 +46,7 @@ const component = {
|
|||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
|
||||||
let edit;
|
let edit;
|
||||||
if (device[parameter] && device[parameter].writable)
|
if (device[parameter] && device[parameter].writable) {
|
||||||
edit = m(
|
edit = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
@@ -72,6 +67,7 @@ const component = {
|
|||||||
},
|
},
|
||||||
"✎"
|
"✎"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return m(
|
return m(
|
||||||
"span.parameter-value",
|
"span.parameter-value",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const component = {
|
|||||||
vnode.state.timeout = null;
|
vnode.state.timeout = null;
|
||||||
},
|
},
|
||||||
view: vnode => {
|
view: vnode => {
|
||||||
let refresh = () => {
|
const refresh = () => {
|
||||||
const device = vnode.attrs.device;
|
const device = vnode.attrs.device;
|
||||||
let param =
|
let param =
|
||||||
device["InternetGatewayDevice.ManagementServer.ConnectionRequestURL"];
|
device["InternetGatewayDevice.ManagementServer.ConnectionRequestURL"];
|
||||||
@@ -34,20 +34,22 @@ const component = {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let t = vnode.state.timestamp + REFRESH_INTERVAL - Date.now();
|
const t = vnode.state.timestamp + REFRESH_INTERVAL - Date.now();
|
||||||
clearTimeout(vnode.state.timeout);
|
clearTimeout(vnode.state.timeout);
|
||||||
if (t <= 0) refresh();
|
if (t <= 0) refresh();
|
||||||
else vnode.state.timeout = setTimeout(refresh, t);
|
else vnode.state.timeout = setTimeout(refresh, t);
|
||||||
|
|
||||||
if (vnode.state.host) {
|
if (vnode.state.host) {
|
||||||
let status = "";
|
let status = "";
|
||||||
if (vnode.state.ping)
|
if (vnode.state.ping) {
|
||||||
if (vnode.state.ping.avg != null)
|
if (vnode.state.ping.avg != null)
|
||||||
status = `${Math.trunc(vnode.state.ping.avg)} ms`;
|
status = `${Math.trunc(vnode.state.ping.avg)} ms`;
|
||||||
else status = "Unreachable";
|
else status = "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
return m("div", `Pinging ${vnode.state.host}: ${status}`);
|
return m("div", `Pinging ${vnode.state.host}: ${status}`);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ const singular = {
|
|||||||
|
|
||||||
function createField(current, attr, focus) {
|
function createField(current, attr, focus) {
|
||||||
if (attr.type === "combo") {
|
if (attr.type === "combo") {
|
||||||
let options = [m("option", { value: "" }, "--Please choose--")];
|
const options = [m("option", { value: "" }, "--Please choose--")];
|
||||||
for (let op of attr.options) options.push(m("option", { value: op }, op));
|
for (const op of attr.options) options.push(m("option", { value: op }, op));
|
||||||
|
|
||||||
let selected = "";
|
let selected = "";
|
||||||
if (attr.options.includes(current.object[attr.id]))
|
if (attr.options.includes(current.object[attr.id]))
|
||||||
@@ -40,7 +40,7 @@ function createField(current, attr, focus) {
|
|||||||
name: attr.id,
|
name: attr.id,
|
||||||
value: current.object[attr.id],
|
value: current.object[attr.id],
|
||||||
oncreate: _vnode => {
|
oncreate: _vnode => {
|
||||||
let editor = codeMirror.fromTextArea(_vnode.dom, {
|
const editor = codeMirror.fromTextArea(_vnode.dom, {
|
||||||
mode: "javascript",
|
mode: "javascript",
|
||||||
lineNumbers: true
|
lineNumbers: true
|
||||||
});
|
});
|
||||||
@@ -87,21 +87,22 @@ function createField(current, attr, focus) {
|
|||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
view: vnode => {
|
view: vnode => {
|
||||||
let actionHandler = vnode.attrs.actionHandler;
|
const actionHandler = vnode.attrs.actionHandler;
|
||||||
let attributes = vnode.attrs.attributes;
|
const attributes = vnode.attrs.attributes;
|
||||||
let resource = vnode.attrs.resource;
|
const resource = vnode.attrs.resource;
|
||||||
let base = vnode.attrs.base || {};
|
const base = vnode.attrs.base || {};
|
||||||
if (!vnode.state.current)
|
if (!vnode.state.current) {
|
||||||
vnode.state.current = {
|
vnode.state.current = {
|
||||||
isNew: !base["_id"],
|
isNew: !base["_id"],
|
||||||
object: Object.assign({}, base)
|
object: Object.assign({}, base)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let current = vnode.state.current;
|
const current = vnode.state.current;
|
||||||
|
|
||||||
let form = [];
|
const form = [];
|
||||||
let focused = false;
|
let focused = false;
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let focus = false;
|
let focus = false;
|
||||||
if (!focused && (current.isNew || attr.id !== "_id"))
|
if (!focused && (current.isNew || attr.id !== "_id"))
|
||||||
focus = focused = true;
|
focus = focused = true;
|
||||||
@@ -117,9 +118,9 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const submit = m("button.primary", { type: "submit" }, "Save");
|
const submit = m("button.primary", { type: "submit" }, "Save");
|
||||||
let buttons = [submit];
|
const buttons = [submit];
|
||||||
|
|
||||||
if (!current.isNew)
|
if (!current.isNew) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
@@ -135,10 +136,11 @@ const component = {
|
|||||||
"Delete"
|
"Delete"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
form.push(m(".actions-bar", buttons));
|
form.push(m(".actions-bar", buttons));
|
||||||
|
|
||||||
let children = [
|
const children = [
|
||||||
m("h1", `${current.isNew ? "New" : "Editing"} ${singular[resource]}`),
|
m("h1", `${current.isNew ? "New" : "Editing"} ${singular[resource]}`),
|
||||||
m(
|
m(
|
||||||
"form",
|
"form",
|
||||||
@@ -147,7 +149,7 @@ const component = {
|
|||||||
e.redraw = false;
|
e.redraw = false;
|
||||||
e.target.onsubmit = null;
|
e.target.onsubmit = null;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
for (let elem of e.target.elements) elem.disabled = true;
|
for (const elem of e.target.elements) elem.disabled = true;
|
||||||
submit.dom.textContent = "Loading ...";
|
submit.dom.textContent = "Loading ...";
|
||||||
actionHandler("save", current.object);
|
actionHandler("save", current.object);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,28 +37,29 @@ const component = {
|
|||||||
.commit(
|
.commit(
|
||||||
[task],
|
[task],
|
||||||
(deviceId, err, connectionRequestStatus, tasks2) => {
|
(deviceId, err, connectionRequestStatus, tasks2) => {
|
||||||
if (err)
|
if (err) {
|
||||||
return notifications.push(
|
notifications.push("error", `${deviceId}: ${err.message}`);
|
||||||
"error",
|
return;
|
||||||
`${deviceId}: ${err.message}`
|
}
|
||||||
);
|
|
||||||
|
|
||||||
for (let t of tasks2)
|
for (const t of tasks2)
|
||||||
if (t.status === "stale") taskQueue.deleteTask(t);
|
if (t.status === "stale") taskQueue.deleteTask(t);
|
||||||
|
|
||||||
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 notifications.push("success", `${deviceId}: Summoned`);
|
} else {
|
||||||
|
notifications.push("success", `${deviceId}: Summoned`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const component = {
|
|||||||
if (vnode.attrs.writable != null) writable = vnode.attrs.writable;
|
if (vnode.attrs.writable != null) writable = vnode.attrs.writable;
|
||||||
|
|
||||||
const tags = [];
|
const tags = [];
|
||||||
for (let p of Object.keys(device))
|
for (const p of Object.keys(device))
|
||||||
if (p.startsWith("Tags.")) tags.push(p.slice(5));
|
if (p.startsWith("Tags.")) tags.push(p.slice(5));
|
||||||
|
|
||||||
tags.sort();
|
tags.sort();
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import * as store from "./store";
|
|||||||
import * as components from "./components";
|
import * as components from "./components";
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
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")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
deviceId: args.id,
|
deviceId: args.id,
|
||||||
@@ -22,15 +23,16 @@ const component = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
document.title = `${vnode.attrs.deviceId} - Devices - GenieACS`;
|
document.title = `${vnode.attrs.deviceId} - Devices - GenieACS`;
|
||||||
|
|
||||||
let dev = store.fetch("devices", vnode.attrs.deviceFilter).value;
|
const dev = store.fetch("devices", vnode.attrs.deviceFilter).value;
|
||||||
if (!dev.length) return "Loading";
|
if (!dev.length) return "Loading";
|
||||||
const conf = config.ui.device;
|
const conf = config.ui.device;
|
||||||
const cmps = [];
|
const cmps = [];
|
||||||
|
|
||||||
for (let c of Object.values(conf))
|
for (const c of Object.values(conf)) {
|
||||||
cmps.push(
|
cmps.push(
|
||||||
m(components.get(c["type"]), Object.assign({ device: dev[0] }, c))
|
m(components.get(c["type"]), Object.assign({ device: dev[0] }, c))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
m("h1", vnode.attrs.deviceId),
|
m("h1", vnode.attrs.deviceId),
|
||||||
|
|||||||
+34
-29
@@ -18,11 +18,9 @@ const PAGE_SIZE = config.ui.pageSize || 10;
|
|||||||
const memoizedParse = memoize(expression.parse);
|
const memoizedParse = memoize(expression.parse);
|
||||||
const memoizedJsonParse = memoize(JSON.parse);
|
const memoizedJsonParse = memoize(JSON.parse);
|
||||||
const memoizedGetSortable = memoize(str => {
|
const memoizedGetSortable = memoize(str => {
|
||||||
let expressionParams = expression.extractParams(
|
const expressionParams = expression.extractParams(expression.parse(str));
|
||||||
expression.parse(str)
|
|
||||||
);
|
|
||||||
if (expressionParams.length === 1) {
|
if (expressionParams.length === 1) {
|
||||||
let param = expression.evaluate(expressionParams[0]);
|
const param = expression.evaluate(expressionParams[0]);
|
||||||
if (typeof param === "string") return param;
|
if (typeof param === "string") return param;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -52,7 +50,7 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
function init(args) {
|
function init(args) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!window.authorizer.hasAccess("devices", 2))
|
if (!window.authorizer.hasAccess("devices", 2))
|
||||||
return reject(new Error("You are not authorized to view this page"));
|
return void reject(new Error("You are not authorized to view this page"));
|
||||||
|
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
const sort = args.sort;
|
const sort = args.sort;
|
||||||
@@ -76,16 +74,17 @@ function renderTable(
|
|||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: devices.length && selected.size === devices.length,
|
checked: devices.length && selected.size === devices.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let d of devices)
|
for (const d of devices) {
|
||||||
if (e.target.checked) selected.add(d["DeviceID.ID"].value[0]);
|
if (e.target.checked) selected.add(d["DeviceID.ID"].value[0]);
|
||||||
else selected.delete(d["DeviceID.ID"].value[0]);
|
else selected.delete(d["DeviceID.ID"].value[0]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
let labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
for (let param of parameters) {
|
for (const param of parameters) {
|
||||||
let label = param.label;
|
const label = param.label;
|
||||||
let _param;
|
let _param;
|
||||||
if (!param.unsortable && (_param = memoizedGetSortable(param.parameter))) {
|
if (!param.unsortable && (_param = memoizedGetSortable(param.parameter))) {
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
@@ -94,7 +93,7 @@ function renderTable(
|
|||||||
if (sort[_param] > 0) symbol = "\u2bc6";
|
if (sort[_param] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[_param] < 0) symbol = "\u2bc5";
|
else if (sort[_param] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -111,9 +110,9 @@ function renderTable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let device of devices) {
|
for (const device of devices) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(device["DeviceID.ID"].value[0]),
|
checked: selected.has(device["DeviceID.ID"].value[0]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -158,12 +157,13 @@ function renderTable(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rows.length)
|
if (!rows.length) {
|
||||||
rows.push(
|
rows.push(
|
||||||
m("tr.empty", m("td", { colspan: parameters.length + 1 }, "No devices"))
|
m("tr.empty", m("td", { colspan: parameters.length + 1 }, "No devices"))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null) footerElements.push(`${devices.length}/${total}`);
|
if (total != null) footerElements.push(`${devices.length}/${total}`);
|
||||||
else footerElements.push(`${devices.length}`);
|
else footerElements.push(`${devices.length}`);
|
||||||
|
|
||||||
@@ -179,12 +179,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -206,12 +207,13 @@ function renderActions(selected) {
|
|||||||
title: "Reboot selected devices",
|
title: "Reboot selected devices",
|
||||||
disabled: !selected.size,
|
disabled: !selected.size,
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
for (let d of selected)
|
for (const d of selected) {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "reboot",
|
name: "reboot",
|
||||||
device: d
|
device: d
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Reboot"
|
"Reboot"
|
||||||
)
|
)
|
||||||
@@ -224,12 +226,13 @@ function renderActions(selected) {
|
|||||||
title: "Factory reset selected devices",
|
title: "Factory reset selected devices",
|
||||||
disabled: !selected.size,
|
disabled: !selected.size,
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
for (let d of selected)
|
for (const d of selected) {
|
||||||
taskQueue.queueTask({
|
taskQueue.queueTask({
|
||||||
name: "factoryReset",
|
name: "factoryReset",
|
||||||
device: d
|
device: d
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Reset"
|
"Reset"
|
||||||
)
|
)
|
||||||
@@ -246,7 +249,7 @@ function renderActions(selected) {
|
|||||||
if (!confirm(`Deleting ${ids.length} devices. Are you sure?`)) return;
|
if (!confirm(`Deleting ${ids.length} devices. Are you sure?`)) return;
|
||||||
|
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (let id of ids) {
|
for (const id of ids) {
|
||||||
++counter;
|
++counter;
|
||||||
store
|
store
|
||||||
.deleteResource("devices", id)
|
.deleteResource("devices", id)
|
||||||
@@ -278,7 +281,7 @@ function renderActions(selected) {
|
|||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
|
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (let id of ids) {
|
for (const id of ids) {
|
||||||
++counter;
|
++counter;
|
||||||
store
|
store
|
||||||
.updateTags(id, { [tag]: true })
|
.updateTags(id, { [tag]: true })
|
||||||
@@ -312,7 +315,7 @@ function renderActions(selected) {
|
|||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
|
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (let id of ids) {
|
for (const id of ids) {
|
||||||
++counter;
|
++counter;
|
||||||
store
|
store
|
||||||
.updateTags(id, { [tag]: false })
|
.updateTags(id, { [tag]: false })
|
||||||
@@ -345,13 +348,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/devices", ops);
|
m.route.set("/devices", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/devices", ops);
|
m.route.set("/devices", ops);
|
||||||
}
|
}
|
||||||
@@ -360,17 +363,19 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let devs = store.fetch("devices", filter, {
|
const devs = store.fetch("devices", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
let count = store.count("devices", filter);
|
const count = store.count("devices", filter);
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let d of devs.value)
|
for (const d of devs.value) {
|
||||||
if (vnode.state.selected.has(d["DeviceID.ID"].value[0]))
|
if (vnode.state.selected.has(d["DeviceID.ID"].value[0]))
|
||||||
selected.add(d["DeviceID.ID"].value[0]);
|
selected.add(d["DeviceID.ID"].value[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(
|
const downloadUrl = getDownloadUrl(
|
||||||
|
|||||||
+42
-34
@@ -89,7 +89,7 @@ function renderStagingDownload(task) {
|
|||||||
onclick: () => {
|
onclick: () => {
|
||||||
task.fileName = f;
|
task.fileName = f;
|
||||||
task.fileType = "";
|
task.fileType = "";
|
||||||
for (let file of files.value)
|
for (const file of files.value)
|
||||||
if (file._id === f) task.fileType = file["metadata.fileType"];
|
if (file._id === f) task.fileType = file["metadata.fileType"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -110,8 +110,8 @@ function renderStagingDownload(task) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderStaging(staging) {
|
function renderStaging(staging) {
|
||||||
let elements = [];
|
const elements = [];
|
||||||
for (let s of staging) {
|
for (const s of staging) {
|
||||||
const queueFunc = () => {
|
const queueFunc = () => {
|
||||||
staging.delete(s);
|
staging.delete(s);
|
||||||
taskQueue.queueTask(s);
|
taskQueue.queueTask(s);
|
||||||
@@ -143,19 +143,19 @@ function renderStaging(staging) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderQueue(queue) {
|
function renderQueue(queue) {
|
||||||
let details = [];
|
const details = [];
|
||||||
let devices = {};
|
const devices = {};
|
||||||
for (let t of queue) {
|
for (const t of queue) {
|
||||||
devices[t.device] = devices[t.device] || [];
|
devices[t.device] = devices[t.device] || [];
|
||||||
devices[t.device].push(t);
|
devices[t.device].push(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(devices)) {
|
for (const [k, v] of Object.entries(devices)) {
|
||||||
details.push(m("strong", k));
|
details.push(m("strong", k));
|
||||||
for (let t of v) {
|
for (const t of v) {
|
||||||
let actions = [];
|
const actions = [];
|
||||||
|
|
||||||
if (t.status === "fault" || t.status === "stale")
|
if (t.status === "fault" || t.status === "stale") {
|
||||||
actions.push(
|
actions.push(
|
||||||
m(
|
m(
|
||||||
"button",
|
"button",
|
||||||
@@ -168,6 +168,7 @@ function renderQueue(queue) {
|
|||||||
"↺"
|
"↺"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
actions.push(
|
actions.push(
|
||||||
m(
|
m(
|
||||||
@@ -182,7 +183,7 @@ function renderQueue(queue) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (t.name === "setParameterValues")
|
if (t.name === "setParameterValues") {
|
||||||
details.push(
|
details.push(
|
||||||
m(
|
m(
|
||||||
`div.${t.status}`,
|
`div.${t.status}`,
|
||||||
@@ -197,7 +198,7 @@ function renderQueue(queue) {
|
|||||||
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}`,
|
||||||
@@ -205,13 +206,13 @@ function renderQueue(queue) {
|
|||||||
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}`,
|
||||||
@@ -219,7 +220,7 @@ function renderQueue(queue) {
|
|||||||
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}`,
|
||||||
@@ -227,7 +228,7 @@ function renderQueue(queue) {
|
|||||||
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}`,
|
||||||
@@ -235,7 +236,7 @@ function renderQueue(queue) {
|
|||||||
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}`,
|
||||||
@@ -243,7 +244,9 @@ function renderQueue(queue) {
|
|||||||
m(".actions", actions)
|
m(".actions", actions)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
else details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
|
} else {
|
||||||
|
details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +256,7 @@ function renderQueue(queue) {
|
|||||||
function renderNotifications(notifs) {
|
function renderNotifications(notifs) {
|
||||||
const notificationElements = [];
|
const notificationElements = [];
|
||||||
|
|
||||||
for (let n of notifs)
|
for (const n of notifs) {
|
||||||
notificationElements.push(
|
notificationElements.push(
|
||||||
m(
|
m(
|
||||||
"div.notification",
|
"div.notification",
|
||||||
@@ -276,6 +279,7 @@ function renderNotifications(notifs) {
|
|||||||
n.message
|
n.message
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return notificationElements;
|
return notificationElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,13 +290,13 @@ const component = {
|
|||||||
const notifs = notifications.getNotifications();
|
const notifs = notifications.getNotifications();
|
||||||
|
|
||||||
let drawerElement, statusElement;
|
let drawerElement, statusElement;
|
||||||
let notificationElements = renderNotifications(notifs);
|
const notificationElements = renderNotifications(notifs);
|
||||||
let stagingElements = renderStaging(staging);
|
const stagingElements = renderStaging(staging);
|
||||||
let queueElements = renderQueue(queue);
|
const queueElements = renderQueue(queue);
|
||||||
|
|
||||||
function repositionNotifications() {
|
function repositionNotifications() {
|
||||||
let top = 10;
|
let top = 10;
|
||||||
for (let c of notificationElements) {
|
for (const c of notificationElements) {
|
||||||
c.dom.style.top = top;
|
c.dom.style.top = top;
|
||||||
top += c.dom.offsetHeight + 10;
|
top += c.dom.offsetHeight + 10;
|
||||||
}
|
}
|
||||||
@@ -300,20 +304,21 @@ const component = {
|
|||||||
|
|
||||||
function resizeDrawer() {
|
function resizeDrawer() {
|
||||||
let height = statusElement.dom.offsetTop + statusElement.dom.offsetHeight;
|
let height = statusElement.dom.offsetTop + statusElement.dom.offsetHeight;
|
||||||
if (stagingElements.length)
|
if (stagingElements.length) {
|
||||||
for (let s of stagingElements)
|
for (const s of stagingElements)
|
||||||
height = Math.max(height, s.dom.offsetTop + s.dom.offsetHeight);
|
height = Math.max(height, s.dom.offsetTop + s.dom.offsetHeight);
|
||||||
else if (vnode.state.mouseIn)
|
} else if (vnode.state.mouseIn) {
|
||||||
for (let c of drawerElement.children)
|
for (const c of drawerElement.children)
|
||||||
height = Math.max(height, c.dom.offsetTop + c.dom.offsetHeight);
|
height = Math.max(height, c.dom.offsetTop + c.dom.offsetHeight);
|
||||||
|
}
|
||||||
drawerElement.dom.style.height = height;
|
drawerElement.dom.style.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stagingElements.length + queueElements.length) {
|
if (stagingElements.length + queueElements.length) {
|
||||||
const statusCount = { queued: 0, pending: 0, fault: 0, stale: 0 };
|
const statusCount = { queued: 0, pending: 0, fault: 0, stale: 0 };
|
||||||
for (let t of queue) statusCount[t.status] += 1;
|
for (const t of queue) statusCount[t.status] += 1;
|
||||||
|
|
||||||
let actions = m(
|
const actions = m(
|
||||||
".actions",
|
".actions",
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
@@ -321,18 +326,20 @@ const component = {
|
|||||||
title: "Commit queued tasks",
|
title: "Commit queued tasks",
|
||||||
disabled: !statusCount.queued,
|
disabled: !statusCount.queued,
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let tasks = Array.from(taskQueue.getQueue()).filter(
|
const tasks = Array.from(taskQueue.getQueue()).filter(
|
||||||
t => t.status === "queued"
|
t => t.status === "queued"
|
||||||
);
|
);
|
||||||
taskQueue
|
taskQueue
|
||||||
.commit(
|
.commit(
|
||||||
tasks,
|
tasks,
|
||||||
(deviceId, err, connectionRequestStatus, tasks2) => {
|
(deviceId, err, connectionRequestStatus, tasks2) => {
|
||||||
if (err)
|
if (err) {
|
||||||
return notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
`${deviceId}: ${err.message}`
|
`${deviceId}: ${err.message}`
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (connectionRequestStatus !== "OK") {
|
if (connectionRequestStatus !== "OK") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
@@ -342,7 +349,7 @@ const component = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let t of tasks2)
|
for (const t of tasks2) {
|
||||||
if (t.status === "stale") {
|
if (t.status === "stale") {
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"error",
|
"error",
|
||||||
@@ -356,6 +363,7 @@ const component = {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
notifications.push(
|
notifications.push(
|
||||||
"success",
|
"success",
|
||||||
|
|||||||
+28
-22
@@ -24,10 +24,11 @@ const attributes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const getDownloadUrl = memoize(filter => {
|
const getDownloadUrl = memoize(filter => {
|
||||||
let cols = {};
|
const cols = {};
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
cols[attr.label] =
|
cols[attr.label] =
|
||||||
attr.id === "timestamp" ? `DATE_STRING(${attr.id})` : attr.id;
|
attr.id === "timestamp" ? `DATE_STRING(${attr.id})` : attr.id;
|
||||||
|
}
|
||||||
|
|
||||||
return `/api/faults.csv?${m.buildQueryString({
|
return `/api/faults.csv?${m.buildQueryString({
|
||||||
filter: filter,
|
filter: filter,
|
||||||
@@ -44,10 +45,11 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
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")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sort = args.sort;
|
const sort = args.sort;
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
@@ -68,16 +70,17 @@ function renderTable(
|
|||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: faults.length && selected.size === faults.length,
|
checked: faults.length && selected.size === faults.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let f of faults)
|
for (const f of faults) {
|
||||||
if (e.target.checked) selected.add(f["_id"]);
|
if (e.target.checked) selected.add(f["_id"]);
|
||||||
else selected.delete(f["_id"]);
|
else selected.delete(f["_id"]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let label = attr.label;
|
const label = attr.label;
|
||||||
|
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
|
|
||||||
@@ -85,7 +88,7 @@ function renderTable(
|
|||||||
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -99,9 +102,9 @@ function renderTable(
|
|||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let f of faults) {
|
for (const f of faults) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(f["_id"]),
|
checked: selected.has(f["_id"]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -116,13 +119,14 @@ function renderTable(
|
|||||||
|
|
||||||
const deviceHref = `#!/devices/${encodeURIComponent(f["device"])}`;
|
const deviceHref = `#!/devices/${encodeURIComponent(f["device"])}`;
|
||||||
|
|
||||||
let tds = [m("td", checkbox)];
|
const tds = [m("td", checkbox)];
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
if (attr.id === "device")
|
if (attr.id === "device")
|
||||||
tds.push(m("a", { href: deviceHref }, f[attr.id]));
|
tds.push(m("a", { href: deviceHref }, f[attr.id]));
|
||||||
else if (attr.id === "timestamp")
|
else if (attr.id === "timestamp")
|
||||||
tds.push(m("td", new Date(f[attr.id]).toLocaleString()));
|
tds.push(m("td", new Date(f[attr.id]).toLocaleString()));
|
||||||
else tds.push(m("td", f[attr.id]));
|
else tds.push(m("td", f[attr.id]));
|
||||||
|
}
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
m(
|
m(
|
||||||
@@ -145,7 +149,7 @@ function renderTable(
|
|||||||
if (!rows.length)
|
if (!rows.length)
|
||||||
rows.push(m("tr.empty", m("td", { colspan: 7 }, "No faults")));
|
rows.push(m("tr.empty", m("td", { colspan: 7 }, "No faults")));
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null) footerElements.push(`${faults.length}/${total}`);
|
if (total != null) footerElements.push(`${faults.length}/${total}`);
|
||||||
else footerElements.push(`${faults.length}`);
|
else footerElements.push(`${faults.length}`);
|
||||||
|
|
||||||
@@ -161,12 +165,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -217,13 +222,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/faults", ops);
|
m.route.set("/faults", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/faults", ops);
|
m.route.set("/faults", ops);
|
||||||
}
|
}
|
||||||
@@ -232,16 +237,17 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let faults = store.fetch("faults", filter, {
|
const faults = store.fetch("faults", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
let count = store.count("faults", filter);
|
const count = store.count("faults", filter);
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let f of faults.value)
|
for (const f of faults.value)
|
||||||
if (vnode.state.selected.has(f["_id"])) selected.add(f["_id"]);
|
if (vnode.state.selected.has(f["_id"])) selected.add(f["_id"]);
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
||||||
|
|||||||
+37
-33
@@ -37,7 +37,7 @@ const attributes = [
|
|||||||
{ id: "metadata.version", label: "Version" }
|
{ id: "metadata.version", label: "Version" }
|
||||||
];
|
];
|
||||||
|
|
||||||
let formData = {
|
const formData = {
|
||||||
resource: "files",
|
resource: "files",
|
||||||
attributes: attributes
|
attributes: attributes
|
||||||
.slice(1) // remove _id from new object form
|
.slice(1) // remove _id from new object form
|
||||||
@@ -55,21 +55,21 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
function putActionHandler(action, object) {
|
function putActionHandler(action, object) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
let file = object["file"] ? object["file"][0] : null;
|
const file = object["file"] ? object["file"][0] : null;
|
||||||
delete object["file"];
|
delete object["file"];
|
||||||
|
|
||||||
if (!file) return reject(new Error("File not selected"));
|
if (!file) return void reject(new Error("File not selected"));
|
||||||
|
|
||||||
let id = file.name;
|
const id = file.name;
|
||||||
|
|
||||||
store
|
store
|
||||||
.resourceExists("files", id)
|
.resourceExists("files", id)
|
||||||
.then(exists => {
|
.then(exists => {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
store.fulfill(0, Date.now());
|
store.fulfill(0, Date.now());
|
||||||
return reject(new Error("File already exists"));
|
return void reject(new Error("File already exists"));
|
||||||
}
|
}
|
||||||
let headers = Object.assign(
|
const headers = Object.assign(
|
||||||
{
|
{
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
Accept: "application/octet-stream"
|
Accept: "application/octet-stream"
|
||||||
@@ -81,9 +81,7 @@ function putActionHandler(action, object) {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
url: `/api/files/${encodeURIComponent(id)}`,
|
url: `/api/files/${encodeURIComponent(id)}`,
|
||||||
serialize: body => {
|
serialize: body => body, // Identity function to prevent JSON.parse on blob data
|
||||||
return body; // Identity function to prevent JSON.parse on blob data
|
|
||||||
},
|
|
||||||
data: file
|
data: file
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -104,8 +102,8 @@ function putActionHandler(action, object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getDownloadUrl = memoize(filter => {
|
const getDownloadUrl = memoize(filter => {
|
||||||
let cols = {};
|
const cols = {};
|
||||||
for (let attr of attributes) cols[attr.label] = attr.id;
|
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||||
return `/api/files.csv?${m.buildQueryString({
|
return `/api/files.csv?${m.buildQueryString({
|
||||||
filter: filter,
|
filter: filter,
|
||||||
columns: JSON.stringify(cols)
|
columns: JSON.stringify(cols)
|
||||||
@@ -113,10 +111,11 @@ const getDownloadUrl = memoize(filter => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
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")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sort = args.sort;
|
const sort = args.sort;
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
@@ -137,16 +136,17 @@ function renderTable(
|
|||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: files.length && selected.size === files.length,
|
checked: files.length && selected.size === files.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let file of files)
|
for (const file of files) {
|
||||||
if (e.target.checked) selected.add(file["_id"]);
|
if (e.target.checked) selected.add(file["_id"]);
|
||||||
else selected.delete(file["_id"]);
|
else selected.delete(file["_id"]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let label = attr.label;
|
const label = attr.label;
|
||||||
|
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ function renderTable(
|
|||||||
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -168,9 +168,9 @@ function renderTable(
|
|||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let file of files) {
|
for (const file of files) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(file["_id"]),
|
checked: selected.has(file["_id"]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -183,11 +183,12 @@ function renderTable(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tds = [m("td", checkbox)];
|
const tds = [m("td", checkbox)];
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
if (attr.id == "script")
|
if (attr.id == "script")
|
||||||
tds.push(m("td", { title: file[attr.id] }, file[attr.id]));
|
tds.push(m("td", { title: file[attr.id] }, file[attr.id]));
|
||||||
else tds.push(m("td", file[attr.id]));
|
else tds.push(m("td", file[attr.id]));
|
||||||
|
}
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
m(
|
m(
|
||||||
@@ -210,7 +211,7 @@ function renderTable(
|
|||||||
if (!rows.length)
|
if (!rows.length)
|
||||||
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No files")));
|
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No files")));
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null) footerElements.push(`${files.length}/${total}`);
|
if (total != null) footerElements.push(`${files.length}/${total}`);
|
||||||
else footerElements.push(`${files.length}`);
|
else footerElements.push(`${files.length}`);
|
||||||
|
|
||||||
@@ -226,12 +227,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -261,14 +263,14 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("files", 3))
|
if (window.authorizer.hasAccess("files", 3)) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
{
|
{
|
||||||
title: "Create new file",
|
title: "Create new file",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -295,6 +297,7 @@ function renderTable(
|
|||||||
"New"
|
"New"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
m(
|
m(
|
||||||
@@ -317,13 +320,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/files", ops);
|
m.route.set("/files", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/files", ops);
|
m.route.set("/files", ops);
|
||||||
}
|
}
|
||||||
@@ -332,17 +335,18 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let files = store.fetch("files", filter, {
|
const files = store.fetch("files", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
let count = store.count("files", filter);
|
const count = store.count("files", filter);
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let file of files.value)
|
for (const file of files.value)
|
||||||
if (vnode.state.selected.has(file["_id"])) selected.add(file["_id"]);
|
if (vnode.state.selected.has(file["_id"])) selected.add(file["_id"]);
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const getAutocomplete = memoize(resource => {
|
|||||||
|
|
||||||
function parseFilter(f) {
|
function parseFilter(f) {
|
||||||
if (/^[\s0-9a-zA-Z]+:/.test(f)) {
|
if (/^[\s0-9a-zA-Z]+:/.test(f)) {
|
||||||
let k = f.split(":", 1)[0];
|
const k = f.split(":", 1)[0];
|
||||||
let v = f.slice(k.length + 1).trim();
|
const v = f.slice(k.length + 1).trim();
|
||||||
return ["FUNC", "Q", k.trim(), v];
|
return ["FUNC", "Q", k.trim(), v];
|
||||||
}
|
}
|
||||||
return expression.parse(f);
|
return expression.parse(f);
|
||||||
@@ -35,7 +35,7 @@ const splitFilter = memoize(filter => {
|
|||||||
const list = [];
|
const list = [];
|
||||||
const f = expression.parse(filter);
|
const f = expression.parse(filter);
|
||||||
if (Array.isArray(f) && f[0] === "AND")
|
if (Array.isArray(f) && f[0] === "AND")
|
||||||
for (let ff of f.slice(1)) list.push(stringifyFilter(ff));
|
for (const ff of f.slice(1)) list.push(stringifyFilter(ff));
|
||||||
else list.push(stringifyFilter(f));
|
else list.push(stringifyFilter(f));
|
||||||
|
|
||||||
list.push("");
|
list.push("");
|
||||||
@@ -59,6 +59,7 @@ const component = {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
vnode.state.filterInvalid |= 1 << idx;
|
vnode.state.filterInvalid |= 1 << idx;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
vnode.state.filterList.push("");
|
vnode.state.filterList.push("");
|
||||||
|
|
||||||
|
|||||||
+15
-8
@@ -6,8 +6,8 @@ const menu = {
|
|||||||
view: vnode => {
|
view: vnode => {
|
||||||
const active = { [vnode.attrs.page]: "active" };
|
const active = { [vnode.attrs.page]: "active" };
|
||||||
|
|
||||||
let tabs = [];
|
const tabs = [];
|
||||||
if (window.authorizer.hasAccess("devices", 1))
|
if (window.authorizer.hasAccess("devices", 1)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -15,8 +15,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/overview" }, "Overview")
|
m("a", { href: "#!/overview" }, "Overview")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("devices", 2))
|
if (window.authorizer.hasAccess("devices", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -24,8 +25,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/devices" }, "Devices")
|
m("a", { href: "#!/devices" }, "Devices")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("faults", 2))
|
if (window.authorizer.hasAccess("faults", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -33,8 +35,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/faults" }, "Faults")
|
m("a", { href: "#!/faults" }, "Faults")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("presets", 2))
|
if (window.authorizer.hasAccess("presets", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -42,8 +45,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/presets" }, "Presets")
|
m("a", { href: "#!/presets" }, "Presets")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("provisions", 2))
|
if (window.authorizer.hasAccess("provisions", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -51,8 +55,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/provisions" }, "Provisions")
|
m("a", { href: "#!/provisions" }, "Provisions")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("virtualParameters", 2))
|
if (window.authorizer.hasAccess("virtualParameters", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -60,8 +65,9 @@ const menu = {
|
|||||||
m("a", { href: "#!/virtualParameters" }, "Virtual Parameters")
|
m("a", { href: "#!/virtualParameters" }, "Virtual Parameters")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("files", 2))
|
if (window.authorizer.hasAccess("files", 2)) {
|
||||||
tabs.push(
|
tabs.push(
|
||||||
m(
|
m(
|
||||||
"li",
|
"li",
|
||||||
@@ -69,6 +75,7 @@ const menu = {
|
|||||||
m("a", { href: "#!/files" }, "Files")
|
m("a", { href: "#!/files" }, "Files")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return m("nav", m("ul", tabs));
|
return m("nav", m("ul", tabs));
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -18,7 +18,7 @@ function close(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
if (overlayCallback)
|
if (overlayCallback) {
|
||||||
return m(
|
return m(
|
||||||
".overlay-wrapper",
|
".overlay-wrapper",
|
||||||
{
|
{
|
||||||
@@ -50,6 +50,7 @@ function render() {
|
|||||||
overlayCallback()
|
overlayCallback()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -11,9 +11,10 @@ const memoizedParse = memoize(expression.parse);
|
|||||||
|
|
||||||
const GROUPS = config.ui.overview.groups;
|
const GROUPS = config.ui.overview.groups;
|
||||||
const CHARTS = {};
|
const CHARTS = {};
|
||||||
for (let group of Object.values(GROUPS))
|
for (const group of Object.values(GROUPS)) {
|
||||||
for (let chartName of Object.values(group["charts"]))
|
for (const chartName of Object.values(group["charts"]))
|
||||||
CHARTS[chartName] = config.ui.overview.charts[chartName];
|
CHARTS[chartName] = config.ui.overview.charts[chartName];
|
||||||
|
}
|
||||||
|
|
||||||
function queryCharts(charts) {
|
function queryCharts(charts) {
|
||||||
charts = Object.assign({}, charts);
|
charts = Object.assign({}, charts);
|
||||||
@@ -30,10 +31,11 @@ function queryCharts(charts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const init = function() {
|
const init = function() {
|
||||||
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")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve({ charts: queryCharts(CHARTS) });
|
return Promise.resolve({ charts: queryCharts(CHARTS) });
|
||||||
};
|
};
|
||||||
@@ -41,14 +43,14 @@ const init = function() {
|
|||||||
const component = {
|
const component = {
|
||||||
view: vnode => {
|
view: vnode => {
|
||||||
document.title = "Overview - GenieACS";
|
document.title = "Overview - GenieACS";
|
||||||
let children = [];
|
const children = [];
|
||||||
for (let group of Object.values(GROUPS)) {
|
for (const group of Object.values(GROUPS)) {
|
||||||
if (group.label) children.push(m("h1", group.label));
|
if (group.label) children.push(m("h1", group.label));
|
||||||
|
|
||||||
let groupChildren = [];
|
const groupChildren = [];
|
||||||
for (let chartName of Object.values(group.charts)) {
|
for (const chartName of Object.values(group.charts)) {
|
||||||
const chart = vnode.attrs.charts[chartName];
|
const chart = vnode.attrs.charts[chartName];
|
||||||
let chartChildren = [];
|
const chartChildren = [];
|
||||||
if (chart.label) chartChildren.push(m("h2", chart.label));
|
if (chart.label) chartChildren.push(m("h2", chart.label));
|
||||||
|
|
||||||
chartChildren.push(m(pieChartComponent, { chart: chart }));
|
chartChildren.push(m(pieChartComponent, { chart: chart }));
|
||||||
|
|||||||
@@ -3,21 +3,21 @@
|
|||||||
import m from "mithril";
|
import m from "mithril";
|
||||||
|
|
||||||
const drawChart = function(chartData) {
|
const drawChart = function(chartData) {
|
||||||
let slices = chartData.slices;
|
const slices = chartData.slices;
|
||||||
let total = Array.from(Object.values(chartData.slices)).reduce(
|
const total = Array.from(Object.values(chartData.slices)).reduce(
|
||||||
(a, s) => a + (s.count.value || 0),
|
(a, s) => a + (s.count.value || 0),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
let legend = [];
|
const legend = [];
|
||||||
let paths = [];
|
const paths = [];
|
||||||
let links = [];
|
const links = [];
|
||||||
let currentProgressPercentage = 0;
|
let currentProgressPercentage = 0;
|
||||||
let startX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
|
let startX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
|
||||||
let startY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
|
let startY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
|
||||||
let endX, endY;
|
let endX, endY;
|
||||||
|
|
||||||
for (let slice of Object.values(slices)) {
|
for (const slice of Object.values(slices)) {
|
||||||
let percent = total > 0 ? (slice.count.value || 0) / total : 0;
|
const percent = total > 0 ? (slice.count.value || 0) / total : 0;
|
||||||
legend.push(
|
legend.push(
|
||||||
m(".legend-line", [
|
m(".legend-line", [
|
||||||
m("span.color", {
|
m("span.color", {
|
||||||
@@ -41,9 +41,9 @@ const drawChart = function(chartData) {
|
|||||||
currentProgressPercentage += percent;
|
currentProgressPercentage += percent;
|
||||||
endX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
|
endX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
|
||||||
endY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
|
endY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
|
||||||
let isBigArc = percent > 0.5 ? 1 : 0;
|
const isBigArc = percent > 0.5 ? 1 : 0;
|
||||||
|
|
||||||
let sketch =
|
const sketch =
|
||||||
`M ${startX} ${startY} ` + // Move to the starting point
|
`M ${startX} ${startY} ` + // Move to the starting point
|
||||||
`A 100 100 0 ${isBigArc} 1 ${endX} ${endY} ` + // Draw an Arc from starting point to ending point
|
`A 100 100 0 ${isBigArc} 1 ${endX} ${endY} ` + // Draw an Arc from starting point to ending point
|
||||||
`L 0 0 z`; // complete the shape by drawing a line to the center of circle
|
`L 0 0 z`; // complete the shape by drawing a line to the center of circle
|
||||||
@@ -58,9 +58,9 @@ const drawChart = function(chartData) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let percentageX =
|
const percentageX =
|
||||||
Math.cos(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
|
Math.cos(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
|
||||||
let percentageY =
|
const percentageY =
|
||||||
Math.sin(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
|
Math.sin(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
|
||||||
|
|
||||||
links.push(
|
links.push(
|
||||||
|
|||||||
+42
-32
@@ -39,10 +39,10 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
|
|
||||||
function putActionHandler(action, object, isNew) {
|
function putActionHandler(action, object, isNew) {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
let id = object["_id"];
|
const id = object["_id"];
|
||||||
delete object["_id"];
|
delete object["_id"];
|
||||||
|
|
||||||
if (!id) return notifications.push("error", "ID can not be empty");
|
if (!id) return void notifications.push("error", "ID can not be empty");
|
||||||
|
|
||||||
store
|
store
|
||||||
.resourceExists("presets", id)
|
.resourceExists("presets", id)
|
||||||
@@ -90,14 +90,14 @@ function putActionHandler(action, object, isNew) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let formData = {
|
const formData = {
|
||||||
resource: "presets",
|
resource: "presets",
|
||||||
attributes: attributes
|
attributes: attributes
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDownloadUrl = memoize(filter => {
|
const getDownloadUrl = memoize(filter => {
|
||||||
let cols = {};
|
const cols = {};
|
||||||
for (let attr of attributes) cols[attr.label] = attr.id;
|
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||||
return `/api/presets.csv?${m.buildQueryString({
|
return `/api/presets.csv?${m.buildQueryString({
|
||||||
filter: filter,
|
filter: filter,
|
||||||
columns: JSON.stringify(cols)
|
columns: JSON.stringify(cols)
|
||||||
@@ -105,10 +105,11 @@ const getDownloadUrl = memoize(filter => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
if (!window.authorizer.hasAccess("presets", 2))
|
if (!window.authorizer.hasAccess("presets", 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.sort;
|
const sort = args.sort;
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
@@ -129,17 +130,18 @@ function renderTable(
|
|||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: presets.length && selected.size === presets.length,
|
checked: presets.length && selected.size === presets.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let preset of presets)
|
for (const preset of presets) {
|
||||||
if (e.target.checked) selected.add(preset["_id"]);
|
if (e.target.checked) selected.add(preset["_id"]);
|
||||||
else selected.delete(preset["_id"]);
|
else selected.delete(preset["_id"]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
|
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let label = attr.label;
|
const label = attr.label;
|
||||||
|
|
||||||
if (attr.unsortable) {
|
if (attr.unsortable) {
|
||||||
labels.push(m("th", label));
|
labels.push(m("th", label));
|
||||||
@@ -152,7 +154,7 @@ function renderTable(
|
|||||||
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -166,9 +168,9 @@ function renderTable(
|
|||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let preset of presets) {
|
for (const preset of presets) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(preset["_id"]),
|
checked: selected.has(preset["_id"]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -182,14 +184,15 @@ function renderTable(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let devicesUrl = "/#!/devices";
|
let devicesUrl = "/#!/devices";
|
||||||
if (preset["precondition"].length)
|
if (preset["precondition"].length) {
|
||||||
devicesUrl += `?${m.buildQueryString({
|
devicesUrl += `?${m.buildQueryString({
|
||||||
filter: preset["precondition"]
|
filter: preset["precondition"]
|
||||||
})}`;
|
})}`;
|
||||||
|
}
|
||||||
|
|
||||||
let tds = [m("td", checkbox)];
|
const tds = [m("td", checkbox)];
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
if (attr.id === "precondition")
|
if (attr.id === "precondition") {
|
||||||
tds.push(
|
tds.push(
|
||||||
m(
|
m(
|
||||||
"td",
|
"td",
|
||||||
@@ -197,7 +200,10 @@ function renderTable(
|
|||||||
m("a", { href: devicesUrl }, preset[attr.id])
|
m("a", { href: devicesUrl }, preset[attr.id])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
else tds.push(m("td", preset[attr.id]));
|
} else {
|
||||||
|
tds.push(m("td", preset[attr.id]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tds.push(
|
tds.push(
|
||||||
m(
|
m(
|
||||||
@@ -206,7 +212,7 @@ function renderTable(
|
|||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -250,7 +256,7 @@ function renderTable(
|
|||||||
if (!rows.length)
|
if (!rows.length)
|
||||||
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No presets")));
|
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No presets")));
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null) footerElements.push(`${presets.length}/${total}`);
|
if (total != null) footerElements.push(`${presets.length}/${total}`);
|
||||||
else footerElements.push(`${presets.length}`);
|
else footerElements.push(`${presets.length}`);
|
||||||
|
|
||||||
@@ -266,12 +272,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -301,14 +308,14 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("presets", 3))
|
if (window.authorizer.hasAccess("presets", 3)) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
{
|
{
|
||||||
title: "Create new preset",
|
title: "Create new preset",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -328,6 +335,7 @@ function renderTable(
|
|||||||
"New"
|
"New"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
m(
|
m(
|
||||||
@@ -350,13 +358,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/presets", ops);
|
m.route.set("/presets", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/presets", ops);
|
m.route.set("/presets", ops);
|
||||||
}
|
}
|
||||||
@@ -365,15 +373,15 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let presets = store.fetch("presets", filter, {
|
const presets = store.fetch("presets", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
let count = store.count("presets", filter);
|
const count = store.count("presets", filter);
|
||||||
|
|
||||||
const provisions = store.fetch("provisions", true);
|
const provisions = store.fetch("provisions", true);
|
||||||
if (provisions.fulfilled) {
|
if (provisions.fulfilled) {
|
||||||
let provisionAttr = attributes.find(attr => {
|
const provisionAttr = attributes.find(attr => {
|
||||||
return attr.id === "provision";
|
return attr.id === "provision";
|
||||||
});
|
});
|
||||||
provisionAttr.options = provisions.value.map(v => {
|
provisionAttr.options = provisions.value.map(v => {
|
||||||
@@ -381,11 +389,13 @@ const component = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let preset of presets.value)
|
for (const preset of presets.value) {
|
||||||
if (vnode.state.selected.has(preset["_id"]))
|
if (vnode.state.selected.has(preset["_id"]))
|
||||||
selected.add(preset["_id"]);
|
selected.add(preset["_id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
||||||
|
|||||||
+37
-29
@@ -34,10 +34,10 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
|
|
||||||
function putActionHandler(action, object, isNew) {
|
function putActionHandler(action, object, isNew) {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
let id = object["_id"];
|
const id = object["_id"];
|
||||||
delete object["_id"];
|
delete object["_id"];
|
||||||
|
|
||||||
if (!id) return notifications.push("error", "ID can not be empty");
|
if (!id) return void notifications.push("error", "ID can not be empty");
|
||||||
|
|
||||||
store
|
store
|
||||||
.resourceExists("provisions", id)
|
.resourceExists("provisions", id)
|
||||||
@@ -85,14 +85,14 @@ function putActionHandler(action, object, isNew) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let formData = {
|
const formData = {
|
||||||
resource: "provisions",
|
resource: "provisions",
|
||||||
attributes: attributes
|
attributes: attributes
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDownloadUrl = memoize(filter => {
|
const getDownloadUrl = memoize(filter => {
|
||||||
let cols = {};
|
const cols = {};
|
||||||
for (let attr of attributes) cols[attr.label] = attr.id;
|
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||||
return `/api/provisions.csv?${m.buildQueryString({
|
return `/api/provisions.csv?${m.buildQueryString({
|
||||||
filter: filter,
|
filter: filter,
|
||||||
columns: JSON.stringify(cols)
|
columns: JSON.stringify(cols)
|
||||||
@@ -100,10 +100,11 @@ const getDownloadUrl = memoize(filter => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
if (!window.authorizer.hasAccess("provisions", 2))
|
if (!window.authorizer.hasAccess("provisions", 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.sort;
|
const sort = args.sort;
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
@@ -131,16 +132,17 @@ function renderTable(
|
|||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: provisions.length && selected.size === provisions.length,
|
checked: provisions.length && selected.size === provisions.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let provision of provisions)
|
for (const provision of provisions) {
|
||||||
if (e.target.checked) selected.add(provision["_id"]);
|
if (e.target.checked) selected.add(provision["_id"]);
|
||||||
else selected.delete(provision["_id"]);
|
else selected.delete(provision["_id"]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let label = attr.label;
|
const label = attr.label;
|
||||||
|
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
|
|
||||||
@@ -148,7 +150,7 @@ function renderTable(
|
|||||||
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -162,9 +164,9 @@ function renderTable(
|
|||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let provision of provisions) {
|
for (const provision of provisions) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(provision["_id"]),
|
checked: selected.has(provision["_id"]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -177,11 +179,12 @@ function renderTable(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tds = [m("td", checkbox)];
|
const tds = [m("td", checkbox)];
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
if (attr.id == "script")
|
if (attr.id == "script")
|
||||||
tds.push(m("td", { title: provision[attr.id] }, provision[attr.id]));
|
tds.push(m("td", { title: provision[attr.id] }, provision[attr.id]));
|
||||||
else tds.push(m("td", provision[attr.id]));
|
else tds.push(m("td", provision[attr.id]));
|
||||||
|
}
|
||||||
|
|
||||||
tds.push(
|
tds.push(
|
||||||
m(
|
m(
|
||||||
@@ -190,7 +193,7 @@ function renderTable(
|
|||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -232,12 +235,13 @@ function renderTable(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rows.length)
|
if (!rows.length) {
|
||||||
rows.push(
|
rows.push(
|
||||||
m("tr.empty", m("td", { colspan: labels.length }, "No provisions"))
|
m("tr.empty", m("td", { colspan: labels.length }, "No provisions"))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null) footerElements.push(`${provisions.length}/${total}`);
|
if (total != null) footerElements.push(`${provisions.length}/${total}`);
|
||||||
else footerElements.push(`${provisions.length}`);
|
else footerElements.push(`${provisions.length}`);
|
||||||
|
|
||||||
@@ -253,12 +257,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -290,14 +295,14 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("provisions", 3))
|
if (window.authorizer.hasAccess("provisions", 3)) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
{
|
{
|
||||||
title: "Create new provision",
|
title: "Create new provision",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -317,6 +322,7 @@ function renderTable(
|
|||||||
"New"
|
"New"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
m(
|
m(
|
||||||
@@ -339,13 +345,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/provisions", ops);
|
m.route.set("/provisions", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/provisions", ops);
|
m.route.set("/provisions", ops);
|
||||||
}
|
}
|
||||||
@@ -354,18 +360,20 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let provisions = store.fetch("provisions", filter, {
|
const provisions = store.fetch("provisions", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
let count = store.count("provisions", filter);
|
const count = store.count("provisions", filter);
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let provision of provisions.value)
|
for (const provision of provisions.value) {
|
||||||
if (vnode.state.selected.has(provision["_id"]))
|
if (vnode.state.selected.has(provision["_id"]))
|
||||||
selected.add(provision["_id"]);
|
selected.add(provision["_id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
||||||
|
|||||||
+12
-9
@@ -3,7 +3,7 @@
|
|||||||
import config from "./config";
|
import config from "./config";
|
||||||
import * as expression from "../common/expression";
|
import * as expression from "../common/expression";
|
||||||
|
|
||||||
let resources = {
|
const resources = {
|
||||||
devices: {},
|
devices: {},
|
||||||
faults: {
|
faults: {
|
||||||
Device: { parameter: ["PARAM", "device"], type: "string" },
|
Device: { parameter: ["PARAM", "device"], type: "string" },
|
||||||
@@ -35,11 +35,12 @@ let resources = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let v of Object.values(config.ui.filters))
|
for (const v of Object.values(config.ui.filters)) {
|
||||||
resources.devices[v.label] = {
|
resources.devices[v.label] = {
|
||||||
parameter: expression.parse(v.parameter),
|
parameter: expression.parse(v.parameter),
|
||||||
type: (v.type || "").split(",").map(s => s.trim())
|
type: (v.type || "").split(",").map(s => s.trim())
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getLabels(resource) {
|
function getLabels(resource) {
|
||||||
if (!resources[resource]) return [];
|
if (!resources[resource]) return [];
|
||||||
@@ -48,14 +49,15 @@ function getLabels(resource) {
|
|||||||
|
|
||||||
function queryNumber(param, value) {
|
function queryNumber(param, value) {
|
||||||
let op = "=";
|
let op = "=";
|
||||||
for (let o of ["<>", "=", "<=", "<", ">=", ">"])
|
for (const o of ["<>", "=", "<=", "<", ">=", ">"]) {
|
||||||
if (value.startsWith(o)) {
|
if (value.startsWith(o)) {
|
||||||
op = o;
|
op = o;
|
||||||
value = value.slice(o.length).trim();
|
value = value.slice(o.length).trim();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let v = parseInt(value);
|
const v = parseInt(value);
|
||||||
if (v !== +value) return null;
|
if (v !== +value) return null;
|
||||||
|
|
||||||
return [op, param, v];
|
return [op, param, v];
|
||||||
@@ -68,12 +70,13 @@ function queryString(param, value) {
|
|||||||
function queryMac(param, value) {
|
function queryMac(param, value) {
|
||||||
value = value.replace(/[^a-f0-9]/gi, "").toLowerCase();
|
value = value.replace(/[^a-f0-9]/gi, "").toLowerCase();
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
if (value.length === 12)
|
if (value.length === 12) {
|
||||||
return [
|
return [
|
||||||
"LIKE",
|
"LIKE",
|
||||||
["FUNC", "LOWER", param],
|
["FUNC", "LOWER", param],
|
||||||
value.replace(/(..)(?!$)/g, "$1:")
|
value.replace(/(..)(?!$)/g, "$1:")
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"OR",
|
"OR",
|
||||||
@@ -90,20 +93,20 @@ function unpack(resource, label, value) {
|
|||||||
if (!resources[resource]) return null;
|
if (!resources[resource]) return null;
|
||||||
const type = resources[resource][label].type;
|
const type = resources[resource][label].type;
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
let res = ["OR"];
|
const res = ["OR"];
|
||||||
|
|
||||||
if (type.length === 0 || type.includes("number")) {
|
if (type.length === 0 || type.includes("number")) {
|
||||||
let q = queryNumber(resources[resource][label].parameter, value);
|
const q = queryNumber(resources[resource][label].parameter, value);
|
||||||
if (q) res.push(q);
|
if (q) res.push(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.length === 0 || type.includes("string")) {
|
if (type.length === 0 || type.includes("string")) {
|
||||||
let q = queryString(resources[resource][label].parameter, value);
|
const q = queryString(resources[resource][label].parameter, value);
|
||||||
if (q) res.push(q);
|
if (q) res.push(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.includes("mac")) {
|
if (type.includes("mac")) {
|
||||||
let q = queryMac(resources[resource][label].parameter, value);
|
const q = queryMac(resources[resource][label].parameter, value);
|
||||||
if (q) res.push(q);
|
if (q) res.push(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-32
@@ -1,7 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import m from "mithril";
|
import m from "mithril";
|
||||||
import config from "./config";
|
|
||||||
import * as expressionParser from "../common/expression-parser";
|
import * as expressionParser from "../common/expression-parser";
|
||||||
import * as expression from "../common/expression";
|
import * as expression from "../common/expression";
|
||||||
import memoize from "../common/memoize";
|
import memoize from "../common/memoize";
|
||||||
@@ -23,7 +22,7 @@ const queries = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const resources = {};
|
const resources = {};
|
||||||
for (let r of [
|
for (const r of [
|
||||||
"devices",
|
"devices",
|
||||||
"faults",
|
"faults",
|
||||||
"files",
|
"files",
|
||||||
@@ -31,13 +30,14 @@ for (let r of [
|
|||||||
"provisions",
|
"provisions",
|
||||||
"virtualParameters",
|
"virtualParameters",
|
||||||
"files"
|
"files"
|
||||||
])
|
]) {
|
||||||
resources[r] = {
|
resources[r] = {
|
||||||
objects: new Map(),
|
objects: new Map(),
|
||||||
count: new Map(),
|
count: new Map(),
|
||||||
fetch: new Map(),
|
fetch: new Map(),
|
||||||
combinedFilter: null
|
combinedFilter: null
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class QueryResponse {
|
class QueryResponse {
|
||||||
get fulfilled() {
|
get fulfilled() {
|
||||||
@@ -79,11 +79,12 @@ function limitFilter(filter, sort, bookmark) {
|
|||||||
arr.reduce((cur, kv) => {
|
arr.reduce((cur, kv) => {
|
||||||
const [param, asc] = kv;
|
const [param, asc] = kv;
|
||||||
if (asc <= 0) {
|
if (asc <= 0) {
|
||||||
if (bookmark[param] == null)
|
if (bookmark[param] == null) {
|
||||||
return expression.or(
|
return expression.or(
|
||||||
["IS NOT NULL", ["PARAM", param]],
|
["IS NOT NULL", ["PARAM", param]],
|
||||||
expression.and(["IS NULL", ["PARAM", param]], cur)
|
expression.and(["IS NULL", ["PARAM", param]], cur)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let f = null;
|
let f = null;
|
||||||
|
|
||||||
@@ -124,13 +125,15 @@ function compareFunction(sort) {
|
|||||||
for (const [param, asc] of sortEntries) {
|
for (const [param, asc] of sortEntries) {
|
||||||
let v1 = a[param];
|
let v1 = a[param];
|
||||||
let v2 = b[param];
|
let v2 = b[param];
|
||||||
if (v1 != null && typeof v1 === "object")
|
if (v1 != null && typeof v1 === "object") {
|
||||||
if (v1.value) v1 = v1.value[0];
|
if (v1.value) v1 = v1.value[0];
|
||||||
else v1 = null;
|
else v1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (v2 != null && typeof v2 === "object")
|
if (v2 != null && typeof v2 === "object") {
|
||||||
if (v2.value) v2 = v2.value[0];
|
if (v2.value) v2 = v2.value[0];
|
||||||
else v2 = null;
|
else v2 = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (v1 > v2) {
|
if (v1 > v2) {
|
||||||
return asc;
|
return asc;
|
||||||
@@ -153,23 +156,24 @@ function compareFunction(sort) {
|
|||||||
|
|
||||||
function findMatches(resourceType, filter, sort, limit) {
|
function findMatches(resourceType, filter, sort, limit) {
|
||||||
// Handle "tag =" and "tag <>" special cases
|
// Handle "tag =" and "tag <>" special cases
|
||||||
if (resourceType === "devices")
|
if (resourceType === "devices") {
|
||||||
filter = expressionParser.map(filter, e => {
|
filter = expressionParser.map(filter, e => {
|
||||||
if (
|
if (
|
||||||
Array.isArray(e) &&
|
Array.isArray(e) &&
|
||||||
Array.isArray(e[1]) &&
|
Array.isArray(e[1]) &&
|
||||||
e[1][0] === "PARAM" &&
|
e[1][0] === "PARAM" &&
|
||||||
e[1][1] === "tag"
|
e[1][1] === "tag"
|
||||||
)
|
) {
|
||||||
if (e[0] === "=") return ["IS NOT NULL", ["PARAM", `Tags.${e[2]}`]];
|
if (e[0] === "=") return ["IS NOT NULL", ["PARAM", `Tags.${e[2]}`]];
|
||||||
else if (e[0] === "<>") return ["IS NULL", ["PARAM", `Tags.${e[2]}`]];
|
else if (e[0] === "<>") return ["IS NULL", ["PARAM", `Tags.${e[2]}`]];
|
||||||
|
}
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let value = [];
|
let value = [];
|
||||||
for (let obj of resources[resourceType].objects.values())
|
for (const obj of resources[resourceType].objects.values())
|
||||||
if (expression.evaluate(filter, obj, fulfillTimestamp))
|
if (expression.evaluate(filter, obj, fulfillTimestamp)) value.push(obj);
|
||||||
value.push(obj);
|
|
||||||
|
|
||||||
value = value.sort(compareFunction(sort));
|
value = value.sort(compareFunction(sort));
|
||||||
if (limit) value = value.slice(0, limit);
|
if (limit) value = value.slice(0, limit);
|
||||||
@@ -181,7 +185,7 @@ function inferQuery(resourceType, queryResponse) {
|
|||||||
const limit = queries.limit.get(queryResponse);
|
const limit = queries.limit.get(queryResponse);
|
||||||
let filter = queries.filter.get(queryResponse);
|
let filter = queries.filter.get(queryResponse);
|
||||||
filter = unpackExpression(filter);
|
filter = unpackExpression(filter);
|
||||||
let bookmark = queries.bookmark.get(queryResponse);
|
const bookmark = queries.bookmark.get(queryResponse);
|
||||||
const sort = queries.sort.get(queryResponse);
|
const sort = queries.sort.get(queryResponse);
|
||||||
if (bookmark || !limit) {
|
if (bookmark || !limit) {
|
||||||
if (bookmark) filter = limitFilter(filter, sort, bookmark);
|
if (bookmark) filter = limitFilter(filter, sort, bookmark);
|
||||||
@@ -208,7 +212,7 @@ function fetch(resourceType, filter, options = {}) {
|
|||||||
sort["DeviceID.ID"] = sort["DeviceID.ID"] || 1;
|
sort["DeviceID.ID"] = sort["DeviceID.ID"] || 1;
|
||||||
else sort["_id"] = sort["_id"] || 1;
|
else sort["_id"] = sort["_id"] || 1;
|
||||||
|
|
||||||
let key = `${filterStr}:${limit}:${JSON.stringify(sort)}`;
|
const key = `${filterStr}:${limit}:${JSON.stringify(sort)}`;
|
||||||
let queryResponse = resources[resourceType].fetch.get(key);
|
let queryResponse = resources[resourceType].fetch.get(key);
|
||||||
if (queryResponse) return queryResponse;
|
if (queryResponse) return queryResponse;
|
||||||
|
|
||||||
@@ -225,15 +229,15 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
let updated = false;
|
let updated = false;
|
||||||
|
|
||||||
if (_fulfillTimestamp > fulfillTimestamp) {
|
if (_fulfillTimestamp > fulfillTimestamp) {
|
||||||
for (let resource of Object.values(resources))
|
for (const resource of Object.values(resources))
|
||||||
resource.combinedFilter = null;
|
resource.combinedFilter = null;
|
||||||
fulfillTimestamp = _fulfillTimestamp;
|
fulfillTimestamp = _fulfillTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPromises = [];
|
const allPromises = [];
|
||||||
|
|
||||||
for (let [resourceType, resource] of Object.entries(resources))
|
for (const [resourceType, resource] of Object.entries(resources)) {
|
||||||
for (let [queryResponseKey, queryResponse] of resource.count) {
|
for (const [queryResponseKey, queryResponse] of resource.count) {
|
||||||
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
|
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
|
||||||
resource.count.delete(queryResponseKey);
|
resource.count.delete(queryResponseKey);
|
||||||
continue;
|
continue;
|
||||||
@@ -269,11 +273,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let toFetchAll = {};
|
const toFetchAll = {};
|
||||||
|
|
||||||
for (let [resourceType, resource] of Object.entries(resources))
|
for (const [resourceType, resource] of Object.entries(resources)) {
|
||||||
for (let [queryResponseKey, queryResponse] of resource.fetch) {
|
for (const [queryResponseKey, queryResponse] of resource.fetch) {
|
||||||
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
|
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
|
||||||
resource.fetch.delete(queryResponseKey);
|
resource.fetch.delete(queryResponseKey);
|
||||||
continue;
|
continue;
|
||||||
@@ -287,7 +292,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
toFetchAll[resourceType].push(queryResponse);
|
toFetchAll[resourceType].push(queryResponse);
|
||||||
const limit = queries.limit.get(queryResponse);
|
const limit = queries.limit.get(queryResponse);
|
||||||
const sort = queries.sort.get(queryResponse);
|
const sort = queries.sort.get(queryResponse);
|
||||||
if (limit)
|
if (limit) {
|
||||||
allPromises.push(
|
allPromises.push(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
updated = true;
|
updated = true;
|
||||||
@@ -308,12 +313,15 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.length) {
|
if (res.length) {
|
||||||
// Generate bookmark object
|
// Generate bookmark object
|
||||||
let bm = Object.keys(sort).reduce((b, k) => {
|
const bm = Object.keys(sort).reduce((b, k) => {
|
||||||
if (res[0][k] != null)
|
if (res[0][k] != null) {
|
||||||
if (typeof res[0][k] === "object") {
|
if (typeof res[0][k] === "object") {
|
||||||
if (res[0][k].value != null)
|
if (res[0][k].value != null)
|
||||||
b[k] = res[0][k].value[0];
|
b[k] = res[0][k].value[0];
|
||||||
} else b[k] = res[0][k];
|
} else {
|
||||||
|
b[k] = res[0][k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}, {});
|
}, {});
|
||||||
@@ -328,6 +336,8 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
Promise.all(allPromises)
|
Promise.all(allPromises)
|
||||||
@@ -368,11 +378,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
if (!resources[resourceType].combinedFilter)
|
if (!resources[resourceType].combinedFilter)
|
||||||
deleted = new Set(resources[resourceType].objects.keys());
|
deleted = new Set(resources[resourceType].objects.keys());
|
||||||
let combinedFilterDiff = combinedFilter;
|
let combinedFilterDiff = combinedFilter;
|
||||||
if (resources[resourceType].combinedFilter)
|
if (resources[resourceType].combinedFilter) {
|
||||||
combinedFilterDiff = expression.and(
|
combinedFilterDiff = expression.and(
|
||||||
combinedFilterDiff,
|
combinedFilterDiff,
|
||||||
expression.not(resources[resourceType].combinedFilter)
|
expression.not(resources[resourceType].combinedFilter)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
resources[resourceType].combinedFilter = expression.or(
|
resources[resourceType].combinedFilter = expression.or(
|
||||||
combinedFilter,
|
combinedFilter,
|
||||||
resources[resourceType].combinedFilter
|
resources[resourceType].combinedFilter
|
||||||
@@ -389,7 +400,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
for (let r of res) {
|
for (const r of res) {
|
||||||
const id =
|
const id =
|
||||||
resourceType === "devices"
|
resourceType === "devices"
|
||||||
? r["DeviceID.ID"].value[0]
|
? r["DeviceID.ID"].value[0]
|
||||||
@@ -398,7 +409,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
deleted.delete(id);
|
deleted.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let d of deleted) {
|
for (const d of deleted) {
|
||||||
const obj = resources[resourceType].objects.get(d);
|
const obj = resources[resourceType].objects.get(d);
|
||||||
if (
|
if (
|
||||||
expression.evaluate(
|
expression.evaluate(
|
||||||
@@ -410,7 +421,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
|
|||||||
resources[resourceType].objects.delete(d);
|
resources[resourceType].objects.delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let queryResponse of toFetch) {
|
for (const queryResponse of toFetch) {
|
||||||
let filter = queries.filter.get(queryResponse);
|
let filter = queries.filter.get(queryResponse);
|
||||||
filter = unpackExpression(filter);
|
filter = unpackExpression(filter);
|
||||||
const limit = queries.limit.get(queryResponse);
|
const limit = queries.limit.get(queryResponse);
|
||||||
@@ -444,7 +455,7 @@ function getTimestamp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function postTasks(deviceId, tasks) {
|
function postTasks(deviceId, tasks) {
|
||||||
for (let t of tasks) {
|
for (const t of tasks) {
|
||||||
t.status = "pending";
|
t.status = "pending";
|
||||||
t.device = deviceId;
|
t.device = deviceId;
|
||||||
}
|
}
|
||||||
@@ -459,8 +470,8 @@ function postTasks(deviceId, tasks) {
|
|||||||
const connectionRequestStatus = xhr.getResponseHeader(
|
const connectionRequestStatus = xhr.getResponseHeader(
|
||||||
"Connection-Request"
|
"Connection-Request"
|
||||||
);
|
);
|
||||||
let st = JSON.parse(xhr.response);
|
const st = JSON.parse(xhr.response);
|
||||||
for (let [i, t] of st.entries()) {
|
for (const [i, t] of st.entries()) {
|
||||||
tasks[i]._id = t._id;
|
tasks[i]._id = t._id;
|
||||||
tasks[i].status = t.status;
|
tasks[i].status = t.status;
|
||||||
tasks[i].fault = t.fault;
|
tasks[i].fault = t.fault;
|
||||||
@@ -487,7 +498,7 @@ function deleteResource(resourceType, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function putResource(resourceType, id, object) {
|
function putResource(resourceType, id, object) {
|
||||||
for (let k in object) if (object[k] === undefined) object[k] = null;
|
for (const k in object) if (object[k] === undefined) object[k] = null;
|
||||||
|
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
@@ -498,7 +509,7 @@ function putResource(resourceType, id, object) {
|
|||||||
|
|
||||||
function resourceExists(resource, id) {
|
function resourceExists(resource, id) {
|
||||||
const param = resource === "devices" ? "DeviceID.ID" : "_id";
|
const param = resource === "devices" ? "DeviceID.ID" : "_id";
|
||||||
let filter = ["=", ["PARAM", param], id];
|
const filter = ["=", ["PARAM", param], id];
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "HEAD",
|
method: "HEAD",
|
||||||
url:
|
url:
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ function stageDownload(task) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function commit(tasks, callback) {
|
function commit(tasks, callback) {
|
||||||
let devices = {};
|
const devices = {};
|
||||||
for (let t of tasks) {
|
for (const t of tasks) {
|
||||||
devices[t.device] = devices[t.device] || [];
|
devices[t.device] = devices[t.device] || [];
|
||||||
devices[t.device].push(t);
|
devices[t.device].push(t);
|
||||||
queueTask(t);
|
queueTask(t);
|
||||||
@@ -50,19 +50,20 @@ function commit(tasks, callback) {
|
|||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
for (let [deviceId, tasks2] of Object.entries(devices)) {
|
for (const [deviceId, tasks2] of Object.entries(devices)) {
|
||||||
++counter;
|
++counter;
|
||||||
store
|
store
|
||||||
.postTasks(deviceId, tasks)
|
.postTasks(deviceId, tasks)
|
||||||
.then(connectionRequestStatus => {
|
.then(connectionRequestStatus => {
|
||||||
for (let t of tasks2)
|
for (const t of tasks2) {
|
||||||
if (t.status === "pending") t.status = "stale";
|
if (t.status === "pending") t.status = "stale";
|
||||||
else if (t.status === "done") queue.delete(t);
|
else if (t.status === "done") queue.delete(t);
|
||||||
|
}
|
||||||
callback(deviceId, null, connectionRequestStatus, tasks2);
|
callback(deviceId, null, connectionRequestStatus, tasks2);
|
||||||
if (--counter === 0) resolve();
|
if (--counter === 0) resolve();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
for (let t of tasks2) t.status = "stale";
|
for (const t of tasks2) t.status = "stale";
|
||||||
callback(deviceId, err, null, tasks2);
|
callback(deviceId, err, null, tasks2);
|
||||||
if (--counter === 0) resolve();
|
if (--counter === 0) resolve();
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-1
@@ -13,7 +13,7 @@ function timeAgo(dtime) {
|
|||||||
let res = "";
|
let res = "";
|
||||||
let level = 2;
|
let level = 2;
|
||||||
|
|
||||||
for (const [u, t] of Object.entries(UNITS))
|
for (const [u, t] of Object.entries(UNITS)) {
|
||||||
if (dtime >= t) {
|
if (dtime >= t) {
|
||||||
let n;
|
let n;
|
||||||
if (level > 1) {
|
if (level > 1) {
|
||||||
@@ -26,6 +26,7 @@ function timeAgo(dtime) {
|
|||||||
else res += `${n} ${u} `;
|
else res += `${n} ${u} `;
|
||||||
if (!--level) break;
|
if (!--level) break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res + "ago";
|
return res + "ago";
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -7,7 +7,7 @@ import * as notifications from "./notifications";
|
|||||||
|
|
||||||
const userMenu = {
|
const userMenu = {
|
||||||
view: () => {
|
view: () => {
|
||||||
if (window.username)
|
if (window.username) {
|
||||||
return m(
|
return m(
|
||||||
"div.user-menu",
|
"div.user-menu",
|
||||||
window.username,
|
window.username,
|
||||||
@@ -32,7 +32,7 @@ const userMenu = {
|
|||||||
"Log out"
|
"Log out"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
return m(
|
return m(
|
||||||
"div.user-menu",
|
"div.user-menu",
|
||||||
m(
|
m(
|
||||||
@@ -44,6 +44,7 @@ const userMenu = {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default userMenu;
|
export default userMenu;
|
||||||
@@ -34,10 +34,10 @@ const unpackSmartQuery = memoize(query => {
|
|||||||
|
|
||||||
function putActionHandler(action, object, isNew) {
|
function putActionHandler(action, object, isNew) {
|
||||||
if (action === "save") {
|
if (action === "save") {
|
||||||
let id = object["_id"];
|
const id = object["_id"];
|
||||||
delete object["_id"];
|
delete object["_id"];
|
||||||
|
|
||||||
if (!id) return notifications.push("error", "ID can not be empty");
|
if (!id) return void notifications.push("error", "ID can not be empty");
|
||||||
|
|
||||||
store
|
store
|
||||||
.resourceExists("virtualParameters", id)
|
.resourceExists("virtualParameters", id)
|
||||||
@@ -85,14 +85,14 @@ function putActionHandler(action, object, isNew) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let formData = {
|
const formData = {
|
||||||
resource: "virtualParameters",
|
resource: "virtualParameters",
|
||||||
attributes: attributes
|
attributes: attributes
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDownloadUrl = memoize(filter => {
|
const getDownloadUrl = memoize(filter => {
|
||||||
let cols = {};
|
const cols = {};
|
||||||
for (let attr of attributes) cols[attr.label] = attr.id;
|
for (const attr of attributes) cols[attr.label] = attr.id;
|
||||||
return `/api/virtualParameters.csv?${m.buildQueryString({
|
return `/api/virtualParameters.csv?${m.buildQueryString({
|
||||||
filter: filter,
|
filter: filter,
|
||||||
columns: JSON.stringify(cols)
|
columns: JSON.stringify(cols)
|
||||||
@@ -100,10 +100,11 @@ const getDownloadUrl = memoize(filter => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function init(args) {
|
function init(args) {
|
||||||
if (!window.authorizer.hasAccess("virtualParameters", 2))
|
if (!window.authorizer.hasAccess("virtualParameters", 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.sort;
|
const sort = args.sort;
|
||||||
const filter = args.filter;
|
const filter = args.filter;
|
||||||
@@ -132,16 +133,17 @@ function renderTable(
|
|||||||
checked:
|
checked:
|
||||||
virtualParameters.length && selected.size === virtualParameters.length,
|
virtualParameters.length && selected.size === virtualParameters.length,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
for (let virtualParameter of virtualParameters)
|
for (const virtualParameter of virtualParameters) {
|
||||||
if (e.target.checked) selected.add(virtualParameter["_id"]);
|
if (e.target.checked) selected.add(virtualParameter["_id"]);
|
||||||
else selected.delete(virtualParameter["_id"]);
|
else selected.delete(virtualParameter["_id"]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disabled: !total
|
disabled: !total
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = [m("th", selectAll)];
|
const labels = [m("th", selectAll)];
|
||||||
for (let attr of attributes) {
|
for (const attr of attributes) {
|
||||||
let label = attr.label;
|
const label = attr.label;
|
||||||
|
|
||||||
let direction = 1;
|
let direction = 1;
|
||||||
|
|
||||||
@@ -149,7 +151,7 @@ function renderTable(
|
|||||||
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
if (sort[attr.id] > 0) symbol = "\u2bc6";
|
||||||
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
else if (sort[attr.id] < 0) symbol = "\u2bc5";
|
||||||
|
|
||||||
let sortable = m(
|
const sortable = m(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@@ -163,9 +165,9 @@ function renderTable(
|
|||||||
labels.push(m("th", [label, sortable]));
|
labels.push(m("th", [label, sortable]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = [];
|
const rows = [];
|
||||||
for (let virtualParameter of virtualParameters) {
|
for (const virtualParameter of virtualParameters) {
|
||||||
let checkbox = m("input", {
|
const checkbox = m("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: selected.has(virtualParameter["_id"]),
|
checked: selected.has(virtualParameter["_id"]),
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
@@ -178,9 +180,9 @@ function renderTable(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tds = [m("td", checkbox)];
|
const tds = [m("td", checkbox)];
|
||||||
for (let attr of attributes)
|
for (const attr of attributes) {
|
||||||
if (attr.id == "script")
|
if (attr.id == "script") {
|
||||||
tds.push(
|
tds.push(
|
||||||
m(
|
m(
|
||||||
"td",
|
"td",
|
||||||
@@ -188,7 +190,10 @@ function renderTable(
|
|||||||
virtualParameter[attr.id]
|
virtualParameter[attr.id]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
else tds.push(m("td", virtualParameter[attr.id]));
|
} else {
|
||||||
|
tds.push(m("td", virtualParameter[attr.id]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tds.push(
|
tds.push(
|
||||||
m(
|
m(
|
||||||
@@ -197,7 +202,7 @@ function renderTable(
|
|||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -239,15 +244,16 @@ function renderTable(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rows.length)
|
if (!rows.length) {
|
||||||
rows.push(
|
rows.push(
|
||||||
m(
|
m(
|
||||||
"tr.empty",
|
"tr.empty",
|
||||||
m("td", { colspan: labels.length }, "No virtual parameters")
|
m("td", { colspan: labels.length }, "No virtual parameters")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let footerElements = [];
|
const footerElements = [];
|
||||||
if (total != null)
|
if (total != null)
|
||||||
footerElements.push(`${virtualParameters.length}/${total}`);
|
footerElements.push(`${virtualParameters.length}/${total}`);
|
||||||
else footerElements.push(`${virtualParameters.length}`);
|
else footerElements.push(`${virtualParameters.length}`);
|
||||||
@@ -266,12 +272,13 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadUrl)
|
if (downloadUrl) {
|
||||||
footerElements.push(
|
footerElements.push(
|
||||||
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let tfoot = m(
|
const tfoot = m(
|
||||||
"tfoot",
|
"tfoot",
|
||||||
m("tr", m("td", { colspan: labels.length }, footerElements))
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
);
|
);
|
||||||
@@ -306,14 +313,14 @@ function renderTable(
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if (window.authorizer.hasAccess("virtualParameters", 3))
|
if (window.authorizer.hasAccess("virtualParameters", 3)) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
m(
|
m(
|
||||||
"button.primary",
|
"button.primary",
|
||||||
{
|
{
|
||||||
title: "Create new virtual parameter",
|
title: "Create new virtual parameter",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let cb = () => {
|
const cb = () => {
|
||||||
return m(
|
return m(
|
||||||
putForm,
|
putForm,
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -333,6 +340,7 @@ function renderTable(
|
|||||||
"New"
|
"New"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
m(
|
m(
|
||||||
@@ -355,13 +363,13 @@ const component = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFilterChanged(filter) {
|
function onFilterChanged(filter) {
|
||||||
let ops = { filter };
|
const ops = { filter };
|
||||||
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
|
||||||
m.route.set("/virtualParameters", ops);
|
m.route.set("/virtualParameters", ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSortChange(sort) {
|
function onSortChange(sort) {
|
||||||
let ops = { sort };
|
const ops = { sort };
|
||||||
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
|
||||||
m.route.set("/virtualParameters", ops);
|
m.route.set("/virtualParameters", ops);
|
||||||
}
|
}
|
||||||
@@ -370,18 +378,20 @@ const component = {
|
|||||||
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
|
||||||
filter = unpackSmartQuery(filter);
|
filter = unpackSmartQuery(filter);
|
||||||
|
|
||||||
let virtualParameters = store.fetch("virtualParameters", filter, {
|
const virtualParameters = store.fetch("virtualParameters", filter, {
|
||||||
limit: vnode.state.showCount || PAGE_SIZE,
|
limit: vnode.state.showCount || PAGE_SIZE,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
let count = store.count("virtualParameters", filter);
|
const count = store.count("virtualParameters", filter);
|
||||||
|
|
||||||
let selected = new Set();
|
const selected = new Set();
|
||||||
if (vnode.state.selected)
|
if (vnode.state.selected) {
|
||||||
for (let virtualParameter of virtualParameters.value)
|
for (const virtualParameter of virtualParameters.value) {
|
||||||
if (vnode.state.selected.has(virtualParameter["_id"]))
|
if (vnode.state.selected.has(virtualParameter["_id"]))
|
||||||
selected.add(virtualParameter["_id"]);
|
selected.add(virtualParameter["_id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
vnode.state.selected = selected;
|
vnode.state.selected = selected;
|
||||||
|
|
||||||
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
|
||||||
|
|||||||
+16
-13
@@ -1,7 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const expression = require("./expression");
|
|
||||||
|
|
||||||
const validators = {
|
const validators = {
|
||||||
pass: require("./validators/pass"),
|
pass: require("./validators/pass"),
|
||||||
test: require("./validators/test")
|
test: require("./validators/test")
|
||||||
@@ -16,18 +14,19 @@ class Authorizer {
|
|||||||
|
|
||||||
hasAccess(resourceType, access) {
|
hasAccess(resourceType, access) {
|
||||||
const cacheKey = `${resourceType}-${access}`;
|
const cacheKey = `${resourceType}-${access}`;
|
||||||
if (cacheKey in this.hasAccessCache)
|
if (cacheKey in this.hasAccessCache) return this.hasAccessCache[cacheKey];
|
||||||
return this.hasAccessCache[cacheKey];
|
|
||||||
|
|
||||||
let has = false;
|
let has = false;
|
||||||
for (let permissionSet of this.permissionSets) {
|
for (const permissionSet of this.permissionSets) {
|
||||||
for (let perm of permissionSet)
|
for (const perm of permissionSet) {
|
||||||
if (perm[resourceType])
|
if (perm[resourceType]) {
|
||||||
if (perm[resourceType].access >= access) {
|
if (perm[resourceType].access >= access) {
|
||||||
has = true;
|
has = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.hasAccessCache[cacheKey] = has;
|
this.hasAccessCache[cacheKey] = has;
|
||||||
return has;
|
return has;
|
||||||
@@ -39,23 +38,26 @@ class Authorizer {
|
|||||||
|
|
||||||
let funcs = {};
|
let funcs = {};
|
||||||
|
|
||||||
for (let permissionSet of this.permissionSets)
|
for (const permissionSet of this.permissionSets) {
|
||||||
for (let perm of permissionSet)
|
for (const perm of permissionSet) {
|
||||||
if (perm[resourceType]) {
|
if (perm[resourceType]) {
|
||||||
if (perm[resourceType].access >= 3) {
|
if (perm[resourceType].access >= 3) {
|
||||||
if (perm[resourceType].validate)
|
if (perm[resourceType].validate) {
|
||||||
for (let [k, v] of Object.entries(perm[resourceType].validate))
|
for (const [k, v] of Object.entries(perm[resourceType].validate))
|
||||||
funcs[k] = v;
|
funcs[k] = v;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
funcs = {};
|
funcs = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const validator = (mutationType, mutation, any) => {
|
const validator = (mutationType, mutation, any) => {
|
||||||
let valid = false;
|
let valid = false;
|
||||||
for (let [k, v] of Object.entries(funcs))
|
for (const [k, v] of Object.entries(funcs)) {
|
||||||
if (v) {
|
if (v) {
|
||||||
let res = validators[k](
|
const res = validators[k](
|
||||||
resourceType,
|
resourceType,
|
||||||
resource,
|
resource,
|
||||||
mutationType,
|
mutationType,
|
||||||
@@ -66,6 +68,7 @@ class Authorizer {
|
|||||||
if (res > 0) valid = true;
|
if (res > 0) valid = true;
|
||||||
else if (res < 0) return false;
|
else if (res < 0) return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function or(cnf1, cnf2) {
|
|||||||
else if (cnf2.length === 0) return cnf1;
|
else if (cnf2.length === 0) return cnf1;
|
||||||
|
|
||||||
if (cnf1.length > cnf2.length) {
|
if (cnf1.length > cnf2.length) {
|
||||||
let c = cnf1;
|
const c = cnf1;
|
||||||
cnf1 = cnf2;
|
cnf1 = cnf2;
|
||||||
cnf2 = c;
|
cnf2 = c;
|
||||||
}
|
}
|
||||||
@@ -53,10 +53,11 @@ function booleanCnf(exp) {
|
|||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let [k, v] of ranges)
|
for (const [k, v] of ranges) {
|
||||||
if (k.endsWith(":number"))
|
if (k.endsWith(":number"))
|
||||||
ranges.set(k, Array.from(v).sort((a, b) => a - b));
|
ranges.set(k, Array.from(v).sort((a, b) => a - b));
|
||||||
else ranges.set(k, Array.from(v).sort());
|
else ranges.set(k, Array.from(v).sort());
|
||||||
|
}
|
||||||
|
|
||||||
const expressions = new Map();
|
const expressions = new Map();
|
||||||
const mutuallyExclusive = new Map();
|
const mutuallyExclusive = new Map();
|
||||||
@@ -113,11 +114,12 @@ function booleanCnf(exp) {
|
|||||||
|
|
||||||
function getLikeVariables(lhs, pat, esc) {
|
function getLikeVariables(lhs, pat, esc) {
|
||||||
const vars = [];
|
const vars = [];
|
||||||
let pats = likePatterns.get(lhs);
|
const pats = likePatterns.get(lhs);
|
||||||
pat = pats.get(`${esc || ""}:${pat}`);
|
pat = pats.get(`${esc || ""}:${pat}`);
|
||||||
for (let p of pats.values())
|
for (const p of pats.values()) {
|
||||||
if (pat === p || likePatternIncludes(pat, p))
|
if (pat === p || likePatternIncludes(pat, p))
|
||||||
vars.push(`${lhs}:like:${JSON.stringify(p)}`);
|
vars.push(`${lhs}:like:${JSON.stringify(p)}`);
|
||||||
|
}
|
||||||
|
|
||||||
return vars.map(key => {
|
return vars.map(key => {
|
||||||
let f = expressions.get(key);
|
let f = expressions.get(key);
|
||||||
@@ -185,7 +187,7 @@ function booleanCnf(exp) {
|
|||||||
let rhs = JSON.stringify(clause[2]);
|
let rhs = JSON.stringify(clause[2]);
|
||||||
// For consistency
|
// For consistency
|
||||||
if (rhs > lhs) {
|
if (rhs > lhs) {
|
||||||
let t = rhs;
|
const t = rhs;
|
||||||
rhs = lhs;
|
rhs = lhs;
|
||||||
lhs = t;
|
lhs = t;
|
||||||
op = { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[op] || op;
|
op = { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[op] || op;
|
||||||
@@ -218,8 +220,8 @@ function booleanCnf(exp) {
|
|||||||
|
|
||||||
const cnf = recursive(exp, false);
|
const cnf = recursive(exp, false);
|
||||||
|
|
||||||
for (let m of mutuallyExclusive.values()) {
|
for (const m of mutuallyExclusive.values()) {
|
||||||
let ar = Array.from(m);
|
const ar = Array.from(m);
|
||||||
for (let i = 0; i < ar.length; ++i)
|
for (let i = 0; i < ar.length; ++i)
|
||||||
for (let j = i + 1; j < ar.length; ++j) cnf.push([0 - ar[i], 0 - ar[j]]);
|
for (let j = i + 1; j < ar.length; ++j) cnf.push([0 - ar[i], 0 - ar[j]]);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-12
@@ -4,7 +4,7 @@ const parsimmon = require("parsimmon");
|
|||||||
|
|
||||||
// Turn escaped characters into real ones (e.g. "\\n" becomes "\n").
|
// Turn escaped characters into real ones (e.g. "\\n" becomes "\n").
|
||||||
function interpretEscapes(str) {
|
function interpretEscapes(str) {
|
||||||
let escapes = {
|
const escapes = {
|
||||||
b: "\b",
|
b: "\b",
|
||||||
f: "\f",
|
f: "\f",
|
||||||
n: "\n",
|
n: "\n",
|
||||||
@@ -12,8 +12,8 @@ function interpretEscapes(str) {
|
|||||||
t: "\t"
|
t: "\t"
|
||||||
};
|
};
|
||||||
return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => {
|
return str.replace(/\\(u[0-9a-fA-F]{4}|[^u])/, (_, escape) => {
|
||||||
let type = escape.charAt(0);
|
const type = escape.charAt(0);
|
||||||
let hex = escape.slice(1);
|
const hex = escape.slice(1);
|
||||||
if (type === "u") return String.fromCharCode(parseInt(hex, 16));
|
if (type === "u") return String.fromCharCode(parseInt(hex, 16));
|
||||||
|
|
||||||
if (escapes.hasOwnProperty(type)) return escapes[type];
|
if (escapes.hasOwnProperty(type)) return escapes[type];
|
||||||
@@ -27,7 +27,7 @@ function map(exp, callback) {
|
|||||||
|
|
||||||
let clone;
|
let clone;
|
||||||
for (let i = 1; i < exp.length; ++i) {
|
for (let i = 1; i < exp.length; ++i) {
|
||||||
let sub = map(exp[i], callback);
|
const sub = map(exp[i], callback);
|
||||||
if (sub !== exp[i]) {
|
if (sub !== exp[i]) {
|
||||||
clone = clone || exp.slice();
|
clone = clone || exp.slice();
|
||||||
clone[i] = sub;
|
clone[i] = sub;
|
||||||
@@ -43,7 +43,7 @@ function binaryLeft(operatorsParser, nextParser) {
|
|||||||
parsimmon.seq(operatorsParser, nextParser).many(),
|
parsimmon.seq(operatorsParser, nextParser).many(),
|
||||||
(first, rest) =>
|
(first, rest) =>
|
||||||
rest.reduce((acc, ch) => {
|
rest.reduce((acc, ch) => {
|
||||||
let [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]);
|
||||||
if (Array.isArray(another) && op === another[0])
|
if (Array.isArray(another) && op === another[0])
|
||||||
return [op, acc].concat(another.slice(1));
|
return [op, acc].concat(another.slice(1));
|
||||||
@@ -315,8 +315,9 @@ function stringify(exp, level = 0) {
|
|||||||
.join(", ")})`
|
.join(", ")})`
|
||||||
);
|
);
|
||||||
} else if (op === "PARAM") {
|
} else if (op === "PARAM") {
|
||||||
if (typeof exp[1] === "string") return wrap(exp[1]);
|
if (typeof exp[1] === "string") {
|
||||||
else if (Array.isArray(exp[1]) && exp[1][0] === "||")
|
return wrap(exp[1]);
|
||||||
|
} else if (Array.isArray(exp[1]) && exp[1][0] === "||") {
|
||||||
return wrap(
|
return wrap(
|
||||||
exp[1]
|
exp[1]
|
||||||
.slice(1)
|
.slice(1)
|
||||||
@@ -326,24 +327,27 @@ function stringify(exp, level = 0) {
|
|||||||
})
|
})
|
||||||
.join("")
|
.join("")
|
||||||
);
|
);
|
||||||
else return wrap(`{${stringify(exp[1])}}`);
|
} else {
|
||||||
|
return wrap(`{${stringify(exp[1])}}`);
|
||||||
|
}
|
||||||
} else if (op === "IS NULL" || op === "IS NOT NULL") {
|
} else if (op === "IS NULL" || op === "IS NOT NULL") {
|
||||||
return wrap(`${stringify(exp[1], opLevels[op])} ${op}`);
|
return wrap(`${stringify(exp[1], opLevels[op])} ${op}`);
|
||||||
} else if (op === "LIKE" || op === "NOT LIKE") {
|
} else if (op === "LIKE" || op === "NOT LIKE") {
|
||||||
if (exp[3])
|
if (exp[3]) {
|
||||||
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 in opLevels) {
|
} else if (op in opLevels) {
|
||||||
const parts = exp.slice(1).map((e, i) => {
|
const parts = exp.slice(1).map((e, i) => {
|
||||||
return stringify(e, opLevels[exp[0]] + Math.min(i - 1, 0));
|
return stringify(e, opLevels[exp[0]] + Math.min(i - 1, 0));
|
||||||
@@ -357,10 +361,10 @@ function stringify(exp, level = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseLikePattern(pat, esc) {
|
function parseLikePattern(pat, esc) {
|
||||||
let chars = pat.split("");
|
const chars = pat.split("");
|
||||||
|
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
let c = chars[i];
|
const c = chars[i];
|
||||||
if (c === esc) {
|
if (c === esc) {
|
||||||
chars[i] = chars[i + 1] || "";
|
chars[i] = chars[i + 1] || "";
|
||||||
chars[i + 1] = "";
|
chars[i + 1] = "";
|
||||||
|
|||||||
+11
-14
@@ -8,24 +8,14 @@ const isArray = Array.isArray;
|
|||||||
|
|
||||||
const regExpCache = new WeakMap();
|
const regExpCache = new WeakMap();
|
||||||
|
|
||||||
function* permute(arr) {
|
const REDUCE_SKIP = {};
|
||||||
if (arr.length <= 1) {
|
|
||||||
for (let i = 0; i < arr[0]; ++i) yield [i];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let slc = arr.slice(1);
|
|
||||||
for (let i = 0; i < arr[0]; ++i)
|
|
||||||
for (let innerArr of permute(slc)) yield [i].concat(innerArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reduce(exp, callback) {
|
function reduce(exp, callback) {
|
||||||
let loop = true;
|
let loop = true;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
loop = false;
|
loop = false;
|
||||||
for (let i = 2; i < exp.length; ++i) {
|
for (let i = 2; i < exp.length; ++i) {
|
||||||
let res = callback(exp[i - 1], exp[i]);
|
const res = callback(exp[i - 1], exp[i]);
|
||||||
if (res !== undefined) {
|
if (res !== REDUCE_SKIP) {
|
||||||
loop = true;
|
loop = true;
|
||||||
exp = exp.slice();
|
exp = exp.slice();
|
||||||
exp.splice(i - 1, 2, res);
|
exp.splice(i - 1, 2, res);
|
||||||
@@ -71,11 +61,13 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
if (!isArray(a)) return a ? b : a;
|
if (!isArray(a)) return a ? b : a;
|
||||||
if (!isArray(b)) return b ? a : b;
|
if (!isArray(b)) return b ? a : b;
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "OR") {
|
} else if (e[0] === "OR") {
|
||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
if (!isArray(a)) return a ? a : b;
|
if (!isArray(a)) return a ? a : b;
|
||||||
if (!isArray(b)) return b ? b : a;
|
if (!isArray(b)) return b ? b : a;
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "NOT") {
|
} else if (e[0] === "NOT") {
|
||||||
if (!isArray(e[1])) return !e[1];
|
if (!isArray(e[1])) return !e[1];
|
||||||
@@ -131,6 +123,7 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
if (a == null || b == null) return null;
|
if (a == null || b == null) return null;
|
||||||
return a * b;
|
return a * b;
|
||||||
}
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "/") {
|
} else if (e[0] === "/") {
|
||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
@@ -138,6 +131,7 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
if (a == null || b == null) return null;
|
if (a == null || b == null) return null;
|
||||||
return a / b;
|
return a / b;
|
||||||
}
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "+") {
|
} else if (e[0] === "+") {
|
||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
@@ -145,6 +139,7 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
if (a == null || b == null) return null;
|
if (a == null || b == null) return null;
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "-") {
|
} else if (e[0] === "-") {
|
||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
@@ -152,6 +147,7 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
if (a == null || b == null) return null;
|
if (a == null || b == null) return null;
|
||||||
return a - b;
|
return a - b;
|
||||||
}
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
} else if (e[0] === "||") {
|
} else if (e[0] === "||") {
|
||||||
return reduce(e, (a, b) => {
|
return reduce(e, (a, b) => {
|
||||||
@@ -159,6 +155,7 @@ function evaluate(exp, obj, now, cb) {
|
|||||||
if (a == null || b == null) return null;
|
if (a == null || b == null) return null;
|
||||||
return `${a}${b}`;
|
return `${a}${b}`;
|
||||||
}
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
@@ -238,7 +235,7 @@ function likePatternToRegExp(pat, esc = "", flags = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extractParams(exp) {
|
function extractParams(exp) {
|
||||||
let params = [];
|
const params = [];
|
||||||
expressionParser.map(exp, e => {
|
expressionParser.map(exp, e => {
|
||||||
if (isArray(e) && e[0] === "PARAM") params.push(e[1]);
|
if (isArray(e) && e[0] === "PARAM") params.push(e[1]);
|
||||||
return e;
|
return e;
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ function satisfiableAt(clauses, varVals, varnr, val) {
|
|||||||
|
|
||||||
// find next unassigned var to split
|
// find next unassigned var to split
|
||||||
let nextVar = 0;
|
let nextVar = 0;
|
||||||
for (let i = 1; i < varVals.length; i++)
|
for (let i = 1; i < varVals.length; i++) {
|
||||||
if (varVals[i] === 0) {
|
if (varVals[i] === 0) {
|
||||||
nextVar = i;
|
nextVar = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nextVar === 0) throw new Error("Error in satisfiableAt");
|
if (nextVar === 0) throw new Error("Error in satisfiableAt");
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ function unitPropagate(clauses, varVals) {
|
|||||||
let unassignedLit = 0; // 0 means none found for this clause
|
let unassignedLit = 0; // 0 means none found for this clause
|
||||||
|
|
||||||
for (let j = 0; j < clause.length; j++) {
|
for (let j = 0; j < clause.length; j++) {
|
||||||
let lit = clause[j];
|
const lit = clause[j];
|
||||||
let nr, polarity;
|
let nr, polarity;
|
||||||
if (lit < 0) {
|
if (lit < 0) {
|
||||||
nr = 0 - lit;
|
nr = 0 - lit;
|
||||||
@@ -149,7 +150,7 @@ function unitPropagate(clauses, varVals) {
|
|||||||
|
|
||||||
// some units were derived: iterate the main loop
|
// some units were derived: iterate the main loop
|
||||||
for (let j = 0; j < derivedQueue.length; j++) {
|
for (let j = 0; j < derivedQueue.length; j++) {
|
||||||
let lit = derivedQueue[j];
|
const lit = derivedQueue[j];
|
||||||
let nr, polarity;
|
let nr, polarity;
|
||||||
if (lit < 0) {
|
if (lit < 0) {
|
||||||
nr = 0 - lit;
|
nr = 0 - lit;
|
||||||
|
|||||||
+51
-41
@@ -6,7 +6,6 @@ const url = require("url");
|
|||||||
|
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
const db = require("./db");
|
const db = require("./db");
|
||||||
const expression = require("../common/expression");
|
|
||||||
const mongodbFunctions = require("./mongodb-functions");
|
const mongodbFunctions = require("./mongodb-functions");
|
||||||
|
|
||||||
function filterToMongoQuery(filter, negate = false, res = {}) {
|
function filterToMongoQuery(filter, negate = false, res = {}) {
|
||||||
@@ -25,7 +24,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
|
|||||||
filterToMongoQuery(filter[1], !negate, res);
|
filterToMongoQuery(filter[1], !negate, res);
|
||||||
} else if (op === "=") {
|
} else if (op === "=") {
|
||||||
const param = filter[1][1];
|
const param = filter[1][1];
|
||||||
let p = (res[param] = res[param] || {});
|
const p = (res[param] = res[param] || {});
|
||||||
if (negate) p["$ne"] = filter[2];
|
if (negate) p["$ne"] = filter[2];
|
||||||
else p["$eq"] = filter[2];
|
else p["$eq"] = filter[2];
|
||||||
} else if (op === "<>") {
|
} else if (op === "<>") {
|
||||||
@@ -69,15 +68,15 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
|
|||||||
|
|
||||||
function deleteResource(resource, id) {
|
function deleteResource(resource, id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let resourceUrl =
|
const resourceUrl =
|
||||||
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
||||||
let options = url.parse(
|
const options = url.parse(
|
||||||
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
options.method = "DELETE";
|
options.method = "DELETE";
|
||||||
|
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
_http
|
_http
|
||||||
.request(options, res => {
|
.request(options, res => {
|
||||||
@@ -91,13 +90,13 @@ function deleteResource(resource, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function postTask(task, connectionRequest, callback) {
|
function postTask(task, connectionRequest, callback) {
|
||||||
let options = url.parse(
|
const options = url.parse(
|
||||||
`${config.server.nbi}devices/${encodeURIComponent(task.device)}/tasks${
|
`${config.server.nbi}devices/${encodeURIComponent(task.device)}/tasks${
|
||||||
connectionRequest ? "?connection_request" : ""
|
connectionRequest ? "?connection_request" : ""
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
options.method = "POST";
|
options.method = "POST";
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
_http
|
_http
|
||||||
.request(options, res => {
|
.request(options, res => {
|
||||||
@@ -114,14 +113,14 @@ function postTask(task, connectionRequest, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.on("end", () => {
|
res.on("end", () => {
|
||||||
let buf = new Buffer(bytes);
|
const buf = Buffer.allocUnsafe(bytes);
|
||||||
let o = 0;
|
let o = 0;
|
||||||
for (let c of chunks) {
|
for (const c of chunks) {
|
||||||
c.copy(buf, o, 0, c.length);
|
c.copy(buf, o, 0, c.length);
|
||||||
o += c.length;
|
o += c.length;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let t = JSON.parse(buf);
|
const t = JSON.parse(buf);
|
||||||
let connectionRequestStatus;
|
let connectionRequestStatus;
|
||||||
if (connectionRequest) {
|
if (connectionRequest) {
|
||||||
connectionRequestStatus = "OK";
|
connectionRequestStatus = "OK";
|
||||||
@@ -144,16 +143,16 @@ function postTask(task, connectionRequest, callback) {
|
|||||||
function postTasks(deviceId, tasks) {
|
function postTasks(deviceId, tasks) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let connectionRequestStatus;
|
let connectionRequestStatus;
|
||||||
let promises = [];
|
const promises = [];
|
||||||
for (let [idx, task] of tasks.entries()) {
|
for (const [idx, task] of tasks.entries()) {
|
||||||
task.device = deviceId;
|
task.device = deviceId;
|
||||||
promises.push(
|
promises.push(
|
||||||
new Promise((res, rej) => {
|
new Promise((res, rej) => {
|
||||||
let conReq = idx === tasks.length - 1;
|
const conReq = idx === tasks.length - 1;
|
||||||
delete task._id;
|
delete task._id;
|
||||||
task.expiry = 5;
|
task.expiry = 5;
|
||||||
postTask(task, conReq, (err, t, crs) => {
|
postTask(task, conReq, (err, t, crs) => {
|
||||||
if (err) return rej(err);
|
if (err) return void rej(err);
|
||||||
if (conReq) connectionRequestStatus = crs;
|
if (conReq) connectionRequestStatus = crs;
|
||||||
res({ _id: t._id, status: "pending" });
|
res({ _id: t._id, status: "pending" });
|
||||||
});
|
});
|
||||||
@@ -163,14 +162,15 @@ function postTasks(deviceId, tasks) {
|
|||||||
|
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(statuses => {
|
.then(statuses => {
|
||||||
if (connectionRequestStatus !== "OK")
|
if (connectionRequestStatus !== "OK") {
|
||||||
return resolve({
|
return void resolve({
|
||||||
connectionRequest: connectionRequestStatus,
|
connectionRequest: connectionRequestStatus,
|
||||||
tasks: statuses
|
tasks: statuses
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let promises2 = [];
|
const promises2 = [];
|
||||||
for (let s of statuses) {
|
for (const s of statuses) {
|
||||||
promises2.push(db.query("tasks", ["=", ["PARAM", "_id"], s._id]));
|
promises2.push(db.query("tasks", ["=", ["PARAM", "_id"], s._id]));
|
||||||
promises2.push(
|
promises2.push(
|
||||||
db.query("faults", [
|
db.query("faults", [
|
||||||
@@ -183,7 +183,7 @@ function postTasks(deviceId, tasks) {
|
|||||||
|
|
||||||
Promise.all(promises2)
|
Promise.all(promises2)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
for (let [i, r] of statuses.entries()) {
|
for (const [i, r] of statuses.entries()) {
|
||||||
if (res[i * 2].length === 0) {
|
if (res[i * 2].length === 0) {
|
||||||
r.status = "done";
|
r.status = "done";
|
||||||
} else if (res[i * 2 + 1].length === 1) {
|
} else if (res[i * 2 + 1].length === 1) {
|
||||||
@@ -216,7 +216,7 @@ function updateTags(deviceId, tags) {
|
|||||||
);
|
);
|
||||||
if (onOff) options.method = "POST";
|
if (onOff) options.method = "POST";
|
||||||
else options.method = "DELETE";
|
else options.method = "DELETE";
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
_http
|
_http
|
||||||
.request(options, res => {
|
.request(options, res => {
|
||||||
@@ -237,17 +237,19 @@ function ping(host) {
|
|||||||
`${config.server.nbi}ping/${encodeURIComponent(host)}`
|
`${config.server.nbi}ping/${encodeURIComponent(host)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
_http
|
_http
|
||||||
.request(options, res => {
|
.request(options, res => {
|
||||||
if (res.statusCode === 404) {
|
if (res.statusCode === 404) {
|
||||||
res.resume();
|
res.resume();
|
||||||
return resolve({});
|
return void resolve({});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.statusCode !== 200) {
|
if (res.statusCode !== 200) {
|
||||||
res.resume();
|
res.resume();
|
||||||
return reject(new Error(`Unexpected status code ${res.statusCode}`));
|
return void reject(
|
||||||
|
new Error(`Unexpected status code ${res.statusCode}`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
@@ -258,18 +260,19 @@ function ping(host) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.on("end", () => {
|
res.on("end", () => {
|
||||||
let buf = new Buffer(bytes);
|
const buf = Buffer.allocUnsafe(bytes);
|
||||||
let o = 0;
|
let o = 0;
|
||||||
for (let c of chunks) {
|
for (const c of chunks) {
|
||||||
c.copy(buf, o, 0, c.length);
|
c.copy(buf, o, 0, c.length);
|
||||||
o += c.length;
|
o += c.length;
|
||||||
}
|
}
|
||||||
let m = buf
|
const m = buf
|
||||||
.toString()
|
.toString()
|
||||||
.match(
|
.match(
|
||||||
/(\d) packets transmitted, (\d) received, ([\d.%]+) packet loss[^]*([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+)/
|
/(\d) packets transmitted, (\d) received, ([\d.%]+) packet loss[^]*([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+)/
|
||||||
);
|
);
|
||||||
if (!m) return reject(new Error("Could not parse ping response"));
|
if (!m)
|
||||||
|
return void reject(new Error("Could not parse ping response"));
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
packetsTransmitted: +m[1],
|
packetsTransmitted: +m[1],
|
||||||
@@ -288,15 +291,15 @@ function ping(host) {
|
|||||||
|
|
||||||
function putResource(resource, id, data) {
|
function putResource(resource, id, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let resourceUrl =
|
const resourceUrl =
|
||||||
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
||||||
let options = url.parse(
|
const options = url.parse(
|
||||||
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
options.method = "PUT";
|
options.method = "PUT";
|
||||||
|
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
if (resource === "presets") data = mongodbFunctions.preProcessPreset(data);
|
if (resource === "presets") data = mongodbFunctions.preProcessPreset(data);
|
||||||
|
|
||||||
@@ -306,7 +309,7 @@ function putResource(resource, id, data) {
|
|||||||
if (resource === "virtualParameters")
|
if (resource === "virtualParameters")
|
||||||
data = mongodbFunctions.preProcessVirtualParameters(data);
|
data = mongodbFunctions.preProcessVirtualParameters(data);
|
||||||
|
|
||||||
let body = typeof data !== "string" ? JSON.stringify(data) : data;
|
const body = typeof data !== "string" ? JSON.stringify(data) : data;
|
||||||
_http
|
_http
|
||||||
.request(options, res => {
|
.request(options, res => {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
@@ -317,14 +320,16 @@ function putResource(resource, id, data) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.on("end", () => {
|
res.on("end", () => {
|
||||||
if (res.statusCode === 200) return resolve();
|
if (res.statusCode === 200) {
|
||||||
else if (res.statusCode !== 400)
|
return void resolve();
|
||||||
return reject(
|
} else if (res.statusCode !== 400) {
|
||||||
|
return void reject(
|
||||||
new Error(`Unexpected status code ${res.statusCode}`)
|
new Error(`Unexpected status code ${res.statusCode}`)
|
||||||
);
|
);
|
||||||
let buf = new Buffer(bytes);
|
}
|
||||||
|
const buf = Buffer.allocUnsafe(bytes);
|
||||||
let o = 0;
|
let o = 0;
|
||||||
for (let c of chunks) {
|
for (const c of chunks) {
|
||||||
c.copy(buf, o, 0, c.length);
|
c.copy(buf, o, 0, c.length);
|
||||||
o += c.length;
|
o += c.length;
|
||||||
}
|
}
|
||||||
@@ -337,17 +342,22 @@ function putResource(resource, id, data) {
|
|||||||
|
|
||||||
function putFile(id, metadata, fileStream) {
|
function putFile(id, metadata, fileStream) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let options = url.parse(
|
const options = url.parse(
|
||||||
`${config.server.nbi}files/${encodeURIComponent(id)}`
|
`${config.server.nbi}files/${encodeURIComponent(id)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
options.method = "PUT";
|
options.method = "PUT";
|
||||||
options.headers = Object.assign({}, metadata);
|
options.headers = Object.assign({}, metadata);
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
const _http = options.protocol === "https:" ? https : http;
|
||||||
let req = _http.request(options, res => {
|
const req = _http.request(options, res => {
|
||||||
res.resume();
|
res.resume();
|
||||||
if (res.statusCode === 201) return resolve();
|
if (res.statusCode === 201) {
|
||||||
else return reject(new Error(`Unexpected status code ${res.statusCode}`));
|
return void resolve();
|
||||||
|
} else {
|
||||||
|
return void reject(
|
||||||
|
new Error(`Unexpected status code ${res.statusCode}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
fileStream.pipe(req);
|
fileStream.pipe(req);
|
||||||
});
|
});
|
||||||
|
|||||||
+66
-62
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
const zlib = require("zlib");
|
const zlib = require("zlib");
|
||||||
const Router = require("koa-router");
|
const Router = require("koa-router");
|
||||||
const config = require("./config");
|
|
||||||
const db = require("./db");
|
const db = require("./db");
|
||||||
const apiFunctions = require("./api-functions");
|
const apiFunctions = require("./api-functions");
|
||||||
const expression = require("../common/expression");
|
const expression = require("../common/expression");
|
||||||
@@ -38,7 +37,7 @@ const resources = {
|
|||||||
tasks: 0
|
tasks: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let [resource, flags] of Object.entries(resources)) {
|
for (const [resource, flags] of Object.entries(resources)) {
|
||||||
router.head(`/${resource}`, async (ctx, next) => {
|
router.head(`/${resource}`, async (ctx, next) => {
|
||||||
let filter = true;
|
let filter = true;
|
||||||
if (ctx.request.query.filter)
|
if (ctx.request.query.filter)
|
||||||
@@ -52,17 +51,18 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
|
|
||||||
if (!ctx.state.authorizer.hasAccess(resource, 1)) {
|
if (!ctx.state.authorizer.hasAccess(resource, 1)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude temporary tasks and faults
|
// Exclude temporary tasks and faults
|
||||||
if (resource === "tasks" || resource === "faults")
|
if (resource === "tasks" || resource === "faults") {
|
||||||
filter = expression.and(filter, [
|
filter = expression.and(filter, [
|
||||||
"NOT",
|
"NOT",
|
||||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let count = await db.count(resource, filter);
|
const count = await db.count(resource, filter);
|
||||||
|
|
||||||
ctx.set("X-Total-Count", count);
|
ctx.set("X-Total-Count", count);
|
||||||
ctx.body = "";
|
ctx.body = "";
|
||||||
@@ -72,7 +72,7 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.get(`/${resource}`, async (ctx, next) => {
|
router.get(`/${resource}`, async (ctx, next) => {
|
||||||
let options = {};
|
const options = {};
|
||||||
let filter = true;
|
let filter = true;
|
||||||
if (ctx.request.query.filter)
|
if (ctx.request.query.filter)
|
||||||
filter = expression.parse(ctx.request.query.filter);
|
filter = expression.parse(ctx.request.query.filter);
|
||||||
@@ -80,10 +80,11 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
if (ctx.request.query.skip) options.skip = +ctx.request.query.skip;
|
if (ctx.request.query.skip) options.skip = +ctx.request.query.skip;
|
||||||
if (ctx.request.query.sort)
|
if (ctx.request.query.sort)
|
||||||
options.sort = JSON.parse(ctx.request.query.sort);
|
options.sort = JSON.parse(ctx.request.query.sort);
|
||||||
if (ctx.request.query.projection)
|
if (ctx.request.query.projection) {
|
||||||
options.projection = ctx.request.query.projection
|
options.projection = ctx.request.query.projection
|
||||||
.split(",")
|
.split(",")
|
||||||
.reduce((obj, k) => Object.assign(obj, { [k]: 1 }), {});
|
.reduce((obj, k) => Object.assign(obj, { [k]: 1 }), {});
|
||||||
|
}
|
||||||
|
|
||||||
const log = {
|
const log = {
|
||||||
message: `Query ${resource}`,
|
message: `Query ${resource}`,
|
||||||
@@ -97,15 +98,16 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
|
|
||||||
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude temporary tasks and faults
|
// Exclude temporary tasks and faults
|
||||||
if (resource === "tasks" || resource === "faults")
|
if (resource === "tasks" || resource === "faults") {
|
||||||
filter = expression.and(filter, [
|
filter = expression.and(filter, [
|
||||||
"NOT",
|
"NOT",
|
||||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let stream;
|
let stream;
|
||||||
switch (ctx.acceptsEncodings("gzip", "deflate", "identity")) {
|
switch (ctx.acceptsEncodings("gzip", "deflate", "identity")) {
|
||||||
@@ -139,7 +141,7 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
|
|
||||||
// CSV download
|
// CSV download
|
||||||
router.get(`/${resource}.csv`, async (ctx, next) => {
|
router.get(`/${resource}.csv`, async (ctx, next) => {
|
||||||
let options = { projection: {} };
|
const options = { projection: {} };
|
||||||
let filter = true;
|
let filter = true;
|
||||||
if (ctx.request.query.filter)
|
if (ctx.request.query.filter)
|
||||||
filter = expression.parse(ctx.request.query.filter);
|
filter = expression.parse(ctx.request.query.filter);
|
||||||
@@ -159,28 +161,25 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
|
|
||||||
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = JSON.parse(ctx.request.query.columns);
|
const columns = JSON.parse(ctx.request.query.columns);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(columns)) {
|
for (const [k, v] of Object.entries(columns)) {
|
||||||
const e = expression.evaluate(
|
const e = expression.evaluate(expression.parse(v), null, now);
|
||||||
expression.parse(v),
|
|
||||||
null,
|
|
||||||
now
|
|
||||||
);
|
|
||||||
columns[k] = e;
|
columns[k] = e;
|
||||||
for (let p of expression.extractParams(e)) options.projection[p] = 1;
|
for (const p of expression.extractParams(e)) options.projection[p] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude temporary tasks and faults
|
// Exclude temporary tasks and faults
|
||||||
if (resource === "tasks" || resource === "faults")
|
if (resource === "tasks" || resource === "faults") {
|
||||||
filter = expression.and(filter, [
|
filter = expression.and(filter, [
|
||||||
"NOT",
|
"NOT",
|
||||||
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
["<", ["PARAM", "expiry"], Date.now() + 60000]
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let stream;
|
let stream;
|
||||||
switch (ctx.acceptsEncodings("gzip", "deflate", "identity")) {
|
switch (ctx.acceptsEncodings("gzip", "deflate", "identity")) {
|
||||||
@@ -209,24 +208,26 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
Object.keys(columns).map(k => `"${k.replace(/"/, '""')}"`) + "\n"
|
Object.keys(columns).map(k => `"${k.replace(/"/, '""')}"`) + "\n"
|
||||||
);
|
);
|
||||||
await db.query(resource, filter, options, obj => {
|
await db.query(resource, filter, options, obj => {
|
||||||
let arr = Object.values(columns).map(exp => {
|
const arr = Object.values(columns).map(exp => {
|
||||||
let v = expression.evaluate(exp, obj, null, e => {
|
const v = expression.evaluate(exp, obj, null, e => {
|
||||||
if (Array.isArray(e))
|
if (Array.isArray(e)) {
|
||||||
if (e[0] === "PARAM") {
|
if (e[0] === "PARAM") {
|
||||||
if (resource === "devices") {
|
if (resource === "devices") {
|
||||||
if (e[1] === "Tags") {
|
if (e[1] === "Tags") {
|
||||||
let tags = [];
|
const tags = [];
|
||||||
for (let p in obj)
|
for (const p in obj)
|
||||||
if (p.startsWith("Tags.")) tags.push(p.slice(5));
|
if (p.startsWith("Tags.")) tags.push(p.slice(5));
|
||||||
|
|
||||||
return tags.join(", ");
|
return tags.join(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e[0] === "FUNC") {
|
} else if (e[0] === "FUNC") {
|
||||||
if (e[1] === "DATE_STRING")
|
if (e[1] === "DATE_STRING") {
|
||||||
if (e[2] && !Array.isArray(e[2]))
|
if (e[2] && !Array.isArray(e[2]))
|
||||||
return new Date(e[2]).toISOString();
|
return new Date(e[2]).toISOString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
@@ -249,15 +250,15 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
const filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
||||||
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await db.query(resource, filter);
|
const res = await db.query(resource, filter);
|
||||||
|
|
||||||
if (!res.length) return next();
|
if (!res.length) return void next();
|
||||||
|
|
||||||
logger.accessInfo(log);
|
logger.accessInfo(log);
|
||||||
ctx.body = "";
|
ctx.body = "";
|
||||||
@@ -270,22 +271,22 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
filter: `${RESOURCE_IDS[resource]} = "${ctx.params.id}"`
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
const filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
||||||
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
if (!ctx.state.authorizer.hasAccess(resource, 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await db.query(resource, filter);
|
const res = await db.query(resource, filter);
|
||||||
|
|
||||||
if (!res.length) return next();
|
if (!res.length) return void next();
|
||||||
|
|
||||||
logger.accessInfo(log);
|
logger.accessInfo(log);
|
||||||
ctx.body = res[0];
|
ctx.body = res[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO add PUT, PATCH routes
|
// TODO add PUT, PATCH routes
|
||||||
if (flags & RESOURCE_DELETE)
|
if (flags & RESOURCE_DELETE) {
|
||||||
router.delete(`/${resource}/:id`, async (ctx, next) => {
|
router.delete(`/${resource}/:id`, async (ctx, next) => {
|
||||||
const log = {
|
const log = {
|
||||||
message: `Delete ${resource}`,
|
message: `Delete ${resource}`,
|
||||||
@@ -294,18 +295,18 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const authorizer = ctx.state.authorizer;
|
const authorizer = ctx.state.authorizer;
|
||||||
let filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
const filter = ["=", ["PARAM", RESOURCE_IDS[resource]], ctx.params.id];
|
||||||
if (!authorizer.hasAccess(resource, 2)) {
|
if (!authorizer.hasAccess(resource, 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
let res = await db.query(resource, filter);
|
const res = await db.query(resource, filter);
|
||||||
if (!res.length) return next();
|
if (!res.length) return void next();
|
||||||
|
|
||||||
const validate = authorizer.getValidator(resource, res[0]);
|
const validate = authorizer.getValidator(resource, res[0]);
|
||||||
if (!validate("delete")) {
|
if (!validate("delete")) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return (ctx.status = 403);
|
return void (ctx.status = 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
await apiFunctions.deleteResource(resource, ctx.params.id);
|
await apiFunctions.deleteResource(resource, ctx.params.id);
|
||||||
@@ -314,8 +315,9 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
|
|
||||||
ctx.body = "";
|
ctx.body = "";
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & RESOURCE_PUT)
|
if (flags & RESOURCE_PUT) {
|
||||||
router.put(`/${resource}/:id`, async (ctx, next) => {
|
router.put(`/${resource}/:id`, async (ctx, next) => {
|
||||||
const id = ctx.params.id;
|
const id = ctx.params.id;
|
||||||
|
|
||||||
@@ -328,30 +330,31 @@ for (let [resource, flags] of Object.entries(resources)) {
|
|||||||
const authorizer = ctx.state.authorizer;
|
const authorizer = ctx.state.authorizer;
|
||||||
if (!authorizer.hasAccess(resource, 3)) {
|
if (!authorizer.hasAccess(resource, 3)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
let obj = ctx.request.body;
|
const obj = ctx.request.body;
|
||||||
|
|
||||||
const validate = authorizer.getValidator(resource, obj);
|
const validate = authorizer.getValidator(resource, obj);
|
||||||
if (!validate("put")) {
|
if (!validate("put")) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return (ctx.status = 403);
|
return void (ctx.status = 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = await apiFunctions.putResource(resource, id, obj);
|
const err = await apiFunctions.putResource(resource, id, obj);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
log.message += " failed";
|
log.message += " failed";
|
||||||
logger.accessWarn(log);
|
logger.accessWarn(log);
|
||||||
ctx.body = err;
|
ctx.body = err;
|
||||||
return (ctx.status = 400);
|
return void (ctx.status = 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.accessInfo(log);
|
logger.accessInfo(log);
|
||||||
|
|
||||||
ctx.body = "";
|
ctx.body = "";
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.put("/files/:id", async (ctx, next) => {
|
router.put("/files/:id", async (ctx, next) => {
|
||||||
@@ -367,10 +370,10 @@ router.put("/files/:id", async (ctx, next) => {
|
|||||||
const authorizer = ctx.state.authorizer;
|
const authorizer = ctx.state.authorizer;
|
||||||
if (!authorizer.hasAccess(resource, 3)) {
|
if (!authorizer.hasAccess(resource, 3)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = {
|
const metadata = {
|
||||||
fileType: ctx.request.headers["metadata.filetype"] || "",
|
fileType: ctx.request.headers["metadata.filetype"] || "",
|
||||||
oui: ctx.request.headers["metadata.oui"] || "",
|
oui: ctx.request.headers["metadata.oui"] || "",
|
||||||
productClass: ctx.request.headers["metadata.productclass"] || "",
|
productClass: ctx.request.headers["metadata.productclass"] || "",
|
||||||
@@ -380,16 +383,16 @@ router.put("/files/:id", async (ctx, next) => {
|
|||||||
const validate = authorizer.getValidator(resource, metadata);
|
const validate = authorizer.getValidator(resource, metadata);
|
||||||
if (!validate("put")) {
|
if (!validate("put")) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return (ctx.status = 403);
|
return void (ctx.status = 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = await apiFunctions.putFile(id, metadata, ctx.req);
|
const err = await apiFunctions.putFile(id, metadata, ctx.req);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
log.message += " failed";
|
log.message += " failed";
|
||||||
logger.accessWarn(log);
|
logger.accessWarn(log);
|
||||||
ctx.body = err;
|
ctx.body = err;
|
||||||
return (ctx.status = 400);
|
return void (ctx.status = 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.metadata = metadata;
|
log.metadata = metadata;
|
||||||
@@ -406,22 +409,23 @@ router.post("/devices/:id/tasks", async (ctx, next) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const authorizer = ctx.state.authorizer;
|
const authorizer = ctx.state.authorizer;
|
||||||
let filter = ["=", ["PARAM", "DeviceID.ID"], ctx.params.id];
|
const filter = ["=", ["PARAM", "DeviceID.ID"], ctx.params.id];
|
||||||
if (!authorizer.hasAccess("devices", 2)) {
|
if (!authorizer.hasAccess("devices", 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
let devices = await db.query("devices", filter);
|
const devices = await db.query("devices", filter);
|
||||||
if (!devices.length) return next();
|
if (!devices.length) return void next();
|
||||||
|
|
||||||
const validate = authorizer.getValidator("devices", devices[0]);
|
const validate = authorizer.getValidator("devices", devices[0]);
|
||||||
for (let t of ctx.request.body)
|
for (const t of ctx.request.body) {
|
||||||
if (!validate("task", t)) {
|
if (!validate("task", t)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return (ctx.status = 403);
|
return void (ctx.status = 403);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await apiFunctions.postTasks(ctx.params.id, ctx.request.body);
|
const res = await apiFunctions.postTasks(ctx.params.id, ctx.request.body);
|
||||||
|
|
||||||
log.tasks = res.tasks.map(t => t._id).join(",");
|
log.tasks = res.tasks.map(t => t._id).join(",");
|
||||||
|
|
||||||
@@ -440,18 +444,18 @@ router.post("/devices/:id/tags", async (ctx, next) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const authorizer = ctx.state.authorizer;
|
const authorizer = ctx.state.authorizer;
|
||||||
let filter = ["=", ["PARAM", "DeviceID.ID"], ctx.params.id];
|
const filter = ["=", ["PARAM", "DeviceID.ID"], ctx.params.id];
|
||||||
if (!authorizer.hasAccess("devices", 2)) {
|
if (!authorizer.hasAccess("devices", 2)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return next();
|
return void next();
|
||||||
}
|
}
|
||||||
let res = await db.query("devices", filter);
|
const res = await db.query("devices", filter);
|
||||||
if (!res.length) return next();
|
if (!res.length) return void next();
|
||||||
|
|
||||||
const validate = authorizer.getValidator("devices", res[0]);
|
const validate = authorizer.getValidator("devices", res[0]);
|
||||||
if (!validate("tags", ctx.request.body)) {
|
if (!validate("tags", ctx.request.body)) {
|
||||||
logUnauthorizedWarning(log);
|
logUnauthorizedWarning(log);
|
||||||
return (ctx.status = 403);
|
return void (ctx.status = 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
await apiFunctions.updateTags(ctx.params.id, ctx.request.body);
|
await apiFunctions.updateTags(ctx.params.id, ctx.request.body);
|
||||||
|
|||||||
+1
-3
@@ -3,7 +3,7 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const configFile = JSON.parse(fs.readFileSync("./config.json"));
|
const configFile = JSON.parse(fs.readFileSync("./config.json"));
|
||||||
|
|
||||||
let config;
|
const config = loadConfig(configFile);
|
||||||
|
|
||||||
function getClientConfig() {
|
function getClientConfig() {
|
||||||
return Object.assign({}, config, {
|
return Object.assign({}, config, {
|
||||||
@@ -33,6 +33,4 @@ function loadConfig(conf, root = {}) {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
config = loadConfig(configFile);
|
|
||||||
|
|
||||||
module.exports = Object.assign({}, config, { getClientConfig });
|
module.exports = Object.assign({}, config, { getClientConfig });
|
||||||
+10
-6
@@ -22,18 +22,19 @@ const RESOURCE_COLLECTION = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getClient() {
|
function getClient() {
|
||||||
if (!_clientPromise)
|
if (!_clientPromise) {
|
||||||
_clientPromise = new Promise((resolve, reject) => {
|
_clientPromise = new Promise((resolve, reject) => {
|
||||||
const CONNECTION_URL = config.server.mongodbConnectionUrl;
|
const CONNECTION_URL = config.server.mongodbConnectionUrl;
|
||||||
mongodb.MongoClient.connect(
|
mongodb.MongoClient.connect(
|
||||||
CONNECTION_URL,
|
CONNECTION_URL,
|
||||||
{ useNewUrlParser: true },
|
{ useNewUrlParser: true },
|
||||||
(err, client) => {
|
(err, client) => {
|
||||||
if (err) return reject(err);
|
if (err) return void reject(err);
|
||||||
resolve(client);
|
resolve(client);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return _clientPromise;
|
return _clientPromise;
|
||||||
}
|
}
|
||||||
@@ -61,12 +62,13 @@ function query(resource, filter, options, callback) {
|
|||||||
.db(RESOURCE_DB[resource])
|
.db(RESOURCE_DB[resource])
|
||||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||||
const cursor = collection.find(q);
|
const cursor = collection.find(q);
|
||||||
if (options.projection)
|
if (options.projection) {
|
||||||
cursor.project(
|
cursor.project(
|
||||||
resource === "devices"
|
resource === "devices"
|
||||||
? mongodbFunctions.processDeviceProjection(options.projection)
|
? mongodbFunctions.processDeviceProjection(options.projection)
|
||||||
: options.projection
|
: options.projection
|
||||||
);
|
);
|
||||||
|
}
|
||||||
if (options.skip) cursor.skip(options.skip);
|
if (options.skip) cursor.skip(options.skip);
|
||||||
if (options.limit) cursor.limit(options.limit);
|
if (options.limit) cursor.limit(options.limit);
|
||||||
|
|
||||||
@@ -83,7 +85,7 @@ function query(resource, filter, options, callback) {
|
|||||||
cursor.sort(s);
|
cursor.sort(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!callback)
|
if (!callback) {
|
||||||
cursor.toArray((err, docs) => {
|
cursor.toArray((err, docs) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
if (resource === "devices")
|
if (resource === "devices")
|
||||||
@@ -98,7 +100,7 @@ function query(resource, filter, options, callback) {
|
|||||||
docs = docs.map(d => mongodbFunctions.flattenFile(d));
|
docs = docs.map(d => mongodbFunctions.flattenFile(d));
|
||||||
return resolve(docs);
|
return resolve(docs);
|
||||||
});
|
});
|
||||||
else
|
} else {
|
||||||
cursor.forEach(
|
cursor.forEach(
|
||||||
doc => {
|
doc => {
|
||||||
if (resource === "devices")
|
if (resource === "devices")
|
||||||
@@ -118,6 +120,7 @@ function query(resource, filter, options, callback) {
|
|||||||
else resolve();
|
else resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -152,10 +155,11 @@ function count(resource, filter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
if (_clientPromise)
|
if (_clientPromise) {
|
||||||
_clientPromise.then(client => {
|
_clientPromise.then(client => {
|
||||||
client.close();
|
client.close();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.query = query;
|
exports.query = query;
|
||||||
|
|||||||
+4
-3
@@ -153,9 +153,10 @@ function formatSimple(details, systemd) {
|
|||||||
flatten(details);
|
flatten(details);
|
||||||
|
|
||||||
let remote = "";
|
let remote = "";
|
||||||
if (details.remoteAddress)
|
if (details.remoteAddress) {
|
||||||
if (details.user) remote = `${details.user}@${details.remoteAddress}: `;
|
if (details.user) remote = `${details.user}@${details.remoteAddress}: `;
|
||||||
else remote = `${details.remoteAddress}: `;
|
else remote = `${details.remoteAddress}: `;
|
||||||
|
}
|
||||||
|
|
||||||
const keys = Object.keys(details);
|
const keys = Object.keys(details);
|
||||||
const skip = {
|
const skip = {
|
||||||
@@ -168,8 +169,8 @@ function formatSimple(details, systemd) {
|
|||||||
|
|
||||||
let meta = "";
|
let meta = "";
|
||||||
|
|
||||||
let kv = [];
|
const kv = [];
|
||||||
for (let k of keys)
|
for (const k of keys)
|
||||||
if (!skip[k]) kv.push(`${k}=${JSON.stringify(details[k])}`);
|
if (!skip[k]) kv.push(`${k}=${JSON.stringify(details[k])}`);
|
||||||
|
|
||||||
if (kv.length) meta = `; ${kv.join(" ")}`;
|
if (kv.length) meta = `; ${kv.join(" ")}`;
|
||||||
|
|||||||
+37
-28
@@ -44,7 +44,7 @@ function processDeviceFilter(filter) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (typeof e[2] === "number") {
|
if (typeof e[2] === "number") {
|
||||||
let alt = e.slice();
|
const alt = e.slice();
|
||||||
alt[2] = new Date(e[2]);
|
alt[2] = new Date(e[2]);
|
||||||
e = ["OR", e, alt];
|
e = ["OR", e, alt];
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ function processTasksFilter(filter) {
|
|||||||
return expressionParser.map(filter, exp => {
|
return expressionParser.map(filter, exp => {
|
||||||
if (!Array.isArray(exp)) return exp;
|
if (!Array.isArray(exp)) return exp;
|
||||||
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
|
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
|
||||||
let e = exp.slice();
|
const e = exp.slice();
|
||||||
if (e[1][0] === "PARAM" && e[1][1] === "_id")
|
if (e[1][0] === "PARAM" && e[1][1] === "_id")
|
||||||
e[2] = new mongodb.ObjectID(e[2]);
|
e[2] = new mongodb.ObjectID(e[2]);
|
||||||
else if (e[1][0] === "PARAM" && e[1][1] === "timestamp")
|
else if (e[1][0] === "PARAM" && e[1][1] === "timestamp")
|
||||||
@@ -75,7 +75,7 @@ function processFaultsFilter(filter) {
|
|||||||
return expressionParser.map(filter, exp => {
|
return expressionParser.map(filter, exp => {
|
||||||
if (!Array.isArray(exp)) return exp;
|
if (!Array.isArray(exp)) return exp;
|
||||||
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
|
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
|
||||||
let e = exp.slice();
|
const e = exp.slice();
|
||||||
if (e[1][0] === "PARAM" && e[1][1] === "timestamp") e[2] = new Date(e[2]);
|
if (e[1][0] === "PARAM" && e[1][1] === "timestamp") e[2] = new Date(e[2]);
|
||||||
else if (e[1][0] === "PARAM" && e[1][1] === "expiry")
|
else if (e[1][0] === "PARAM" && e[1][1] === "expiry")
|
||||||
e[2] = new Date(e[2]);
|
e[2] = new Date(e[2]);
|
||||||
@@ -101,7 +101,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
|
|||||||
filterToMongoQuery(filter[1], !negate, res);
|
filterToMongoQuery(filter[1], !negate, res);
|
||||||
} else if (op === "=") {
|
} else if (op === "=") {
|
||||||
const param = filter[1][1];
|
const param = filter[1][1];
|
||||||
let p = (res[param] = res[param] || {});
|
const p = (res[param] = res[param] || {});
|
||||||
if (negate) p["$ne"] = filter[2];
|
if (negate) p["$ne"] = filter[2];
|
||||||
else p["$eq"] = filter[2];
|
else p["$eq"] = filter[2];
|
||||||
} else if (op === "<>") {
|
} else if (op === "<>") {
|
||||||
@@ -141,17 +141,19 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
|
|||||||
let param;
|
let param;
|
||||||
let flags;
|
let flags;
|
||||||
if (filter[1][0] === "FUNC" && filter[1][1] === "UPPER") {
|
if (filter[1][0] === "FUNC" && filter[1][1] === "UPPER") {
|
||||||
if (filter[2] !== filter[2].toUpperCase())
|
if (filter[2] !== filter[2].toUpperCase()) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Cannot compare UPPER() against non upper case pattern"
|
"Cannot compare UPPER() against non upper case pattern"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
param = filter[1][2][1];
|
param = filter[1][2][1];
|
||||||
flags = "i";
|
flags = "i";
|
||||||
} else if (filter[1][0] === "FUNC" && filter[1][1] === "LOWER") {
|
} else if (filter[1][0] === "FUNC" && filter[1][1] === "LOWER") {
|
||||||
if (filter[2] !== filter[2].toLowerCase())
|
if (filter[2] !== filter[2].toLowerCase()) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Cannot compare LOWER() against non lower case pattern"
|
"Cannot compare LOWER() against non lower case pattern"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
param = filter[1][2][1];
|
param = filter[1][2][1];
|
||||||
flags = "i";
|
flags = "i";
|
||||||
} else {
|
} else {
|
||||||
@@ -171,7 +173,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
|
|||||||
function processDeviceProjection(projection) {
|
function processDeviceProjection(projection) {
|
||||||
if (!projection) return projection;
|
if (!projection) return projection;
|
||||||
const p = {};
|
const p = {};
|
||||||
for (const [k, v] of Object.entries(projection))
|
for (const [k, v] of Object.entries(projection)) {
|
||||||
if (k === "DeviceID.ID") {
|
if (k === "DeviceID.ID") {
|
||||||
p["_id"] = 1;
|
p["_id"] = 1;
|
||||||
} else if (k.startsWith("DeviceID")) {
|
} else if (k.startsWith("DeviceID")) {
|
||||||
@@ -189,6 +191,7 @@ function processDeviceProjection(projection) {
|
|||||||
} else {
|
} else {
|
||||||
p[k] = v;
|
p[k] = v;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -196,18 +199,19 @@ function processDeviceProjection(projection) {
|
|||||||
function processDeviceSort(sort) {
|
function processDeviceSort(sort) {
|
||||||
if (!sort) return sort;
|
if (!sort) return sort;
|
||||||
const s = {};
|
const s = {};
|
||||||
for (const [k, v] of Object.entries(sort))
|
for (const [k, v] of Object.entries(sort)) {
|
||||||
if (k === "DeviceID.ID") s["_id"] = v;
|
if (k === "DeviceID.ID") s["_id"] = v;
|
||||||
else if (k === "Events.Inform") s["_lastInform"] = v;
|
else if (k === "Events.Inform") s["_lastInform"] = v;
|
||||||
else s[k] = v;
|
else s[k] = v;
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenDevice(device) {
|
function flattenDevice(device) {
|
||||||
function recursive(input, root, output, timestamp) {
|
function recursive(input, root, output, timestamp) {
|
||||||
for (let [name, tree] of Object.entries(input)) {
|
for (const [name, tree] of Object.entries(input)) {
|
||||||
if (!root)
|
if (!root) {
|
||||||
if (name === "_lastInform") {
|
if (name === "_lastInform") {
|
||||||
output["Events.Inform"] = {
|
output["Events.Inform"] = {
|
||||||
value: [Date.parse(tree), "xsd:dateTime"],
|
value: [Date.parse(tree), "xsd:dateTime"],
|
||||||
@@ -294,7 +298,7 @@ function flattenDevice(device) {
|
|||||||
objectTimestamp: timestamp
|
objectTimestamp: timestamp
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let t of tree)
|
for (const t of tree) {
|
||||||
output[`Tags.${t}`] = {
|
output[`Tags.${t}`] = {
|
||||||
value: [true, "xsd:boolean"],
|
value: [true, "xsd:boolean"],
|
||||||
valueTimestamp: timestamp,
|
valueTimestamp: timestamp,
|
||||||
@@ -304,6 +308,8 @@ function flattenDevice(device) {
|
|||||||
objectTimestamp: timestamp
|
objectTimestamp: timestamp
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (name.startsWith("_")) continue;
|
if (name.startsWith("_")) continue;
|
||||||
|
|
||||||
@@ -329,7 +335,7 @@ function flattenDevice(device) {
|
|||||||
attrs["writableTimestamp"] = childrenTimestamp;
|
attrs["writableTimestamp"] = childrenTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = root ? `${root}.${name}` : name;
|
const r = root ? `${root}.${name}` : name;
|
||||||
output[r] = attrs;
|
output[r] = attrs;
|
||||||
|
|
||||||
if (attrs["object"] || tree["object"] == null)
|
if (attrs["object"] || tree["object"] == null)
|
||||||
@@ -348,14 +354,14 @@ function flattenFault(fault) {
|
|||||||
if (f.timestamp) f.timestamp = +f.timestamp;
|
if (f.timestamp) f.timestamp = +f.timestamp;
|
||||||
if (f.expiry) f.expiry = +f.expiry;
|
if (f.expiry) f.expiry = +f.expiry;
|
||||||
if (f.detail) {
|
if (f.detail) {
|
||||||
for (let [k, v] of Object.entries(f.detail)) fault[`detail.${k}`] = v;
|
for (const [k, v] of Object.entries(f.detail)) fault[`detail.${k}`] = v;
|
||||||
delete f.detail;
|
delete f.detail;
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenTask(task) {
|
function flattenTask(task) {
|
||||||
let t = Object.assign({}, task);
|
const t = Object.assign({}, task);
|
||||||
t._id = "" + t._id;
|
t._id = "" + t._id;
|
||||||
if (t.timestamp) t.timestamp = +t.timestamp;
|
if (t.timestamp) t.timestamp = +t.timestamp;
|
||||||
if (t.expiry) t.expiry = +t.expiry;
|
if (t.expiry) t.expiry = +t.expiry;
|
||||||
@@ -363,20 +369,21 @@ function flattenTask(task) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mongoQueryToFilter(query) {
|
function mongoQueryToFilter(query) {
|
||||||
let expressions = [];
|
const expressions = [];
|
||||||
function recursive(_query) {
|
function recursive(_query) {
|
||||||
for (let [k, v] of Object.entries(_query))
|
for (const [k, v] of Object.entries(_query)) {
|
||||||
if (k[0] == "$") {
|
if (k[0] == "$") {
|
||||||
if (k == "$and") for (let vv of Object.values(v)) recursive(vv);
|
if (k == "$and") for (const vv of Object.values(v)) recursive(vv);
|
||||||
else throw new Error(`Operator ${k} not supported`);
|
else throw new Error(`Operator ${k} not supported`);
|
||||||
} else if (k == "_tags") {
|
} else if (k == "_tags") {
|
||||||
if (typeof v === "object") {
|
if (typeof v === "object") {
|
||||||
if (Array.isArray(v)) throw new Error(`Invalid type`);
|
if (Array.isArray(v)) throw new Error(`Invalid type`);
|
||||||
|
|
||||||
for (let [kk, vv] of Object.entries(v))
|
for (const [kk, vv] of Object.entries(v)) {
|
||||||
if (kk == "$ne")
|
if (kk == "$ne")
|
||||||
expressions.push(["IS NULL", ["PARAM", `Tags.${vv}`]]);
|
expressions.push(["IS NULL", ["PARAM", `Tags.${vv}`]]);
|
||||||
else throw new Error(`Operator ${kk} not supported`);
|
else throw new Error(`Operator ${kk} not supported`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
expressions.push(["IS NOT NULL", ["PARAM", `Tags.${v}`]]);
|
expressions.push(["IS NOT NULL", ["PARAM", `Tags.${v}`]]);
|
||||||
}
|
}
|
||||||
@@ -388,7 +395,7 @@ function mongoQueryToFilter(query) {
|
|||||||
} else if (typeof v === "object") {
|
} else if (typeof v === "object") {
|
||||||
if (Array.isArray(v)) throw new Error(`Invalid type`);
|
if (Array.isArray(v)) throw new Error(`Invalid type`);
|
||||||
|
|
||||||
for (let [kk, vv] of Object.entries(v)) {
|
for (const [kk, vv] of Object.entries(v)) {
|
||||||
let op;
|
let op;
|
||||||
switch (kk) {
|
switch (kk) {
|
||||||
case "$eq":
|
case "$eq":
|
||||||
@@ -418,6 +425,7 @@ function mongoQueryToFilter(query) {
|
|||||||
expressions.push(["=", ["PARAM", k], v]);
|
expressions.push(["=", ["PARAM", k], v]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
recursive(query);
|
recursive(query);
|
||||||
|
|
||||||
if (expressions.length > 1) return ["AND"].concat(expressions);
|
if (expressions.length > 1) return ["AND"].concat(expressions);
|
||||||
@@ -428,7 +436,7 @@ function mongoQueryToFilter(query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function flattenPreset(preset) {
|
function flattenPreset(preset) {
|
||||||
let p = Object.assign({}, preset);
|
const p = Object.assign({}, preset);
|
||||||
if (p.precondition) {
|
if (p.precondition) {
|
||||||
p.precondition = mongoQueryToFilter(JSON.parse(p.precondition));
|
p.precondition = mongoQueryToFilter(JSON.parse(p.precondition));
|
||||||
p.precondition = p.precondition.length
|
p.precondition = p.precondition.length
|
||||||
@@ -437,12 +445,12 @@ function flattenPreset(preset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p.events) {
|
if (p.events) {
|
||||||
let e = [];
|
const e = [];
|
||||||
for (let [k, v] of Object.entries(p.events)) e.push(v ? k : `-${k}`);
|
for (const [k, v] of Object.entries(p.events)) e.push(v ? k : `-${k}`);
|
||||||
p.events = e.join(", ");
|
p.events = e.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let provision = p.configurations[0];
|
const provision = p.configurations[0];
|
||||||
if (
|
if (
|
||||||
p.configurations.length != 1 ||
|
p.configurations.length != 1 ||
|
||||||
provision.type != "provision" ||
|
provision.type != "provision" ||
|
||||||
@@ -459,7 +467,7 @@ function flattenPreset(preset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function flattenFile(file) {
|
function flattenFile(file) {
|
||||||
let f = {};
|
const f = {};
|
||||||
f["_id"] = file["_id"];
|
f["_id"] = file["_id"];
|
||||||
if (file.metadata) {
|
if (file.metadata) {
|
||||||
f["metadata.fileType"] = file.metadata.fileType || "";
|
f["metadata.fileType"] = file.metadata.fileType || "";
|
||||||
@@ -471,14 +479,14 @@ function flattenFile(file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function preProcessPreset(data) {
|
function preProcessPreset(data) {
|
||||||
let preset = Object.assign({}, data);
|
const preset = Object.assign({}, data);
|
||||||
preset.precondition = preset.precondition
|
preset.precondition = preset.precondition
|
||||||
? filterToMongoQuery(expression.parse(preset.precondition))
|
? filterToMongoQuery(expression.parse(preset.precondition))
|
||||||
: {};
|
: {};
|
||||||
preset.precondition = JSON.stringify(preset.precondition);
|
preset.precondition = JSON.stringify(preset.precondition);
|
||||||
|
|
||||||
let events = {};
|
const events = {};
|
||||||
if (preset.events)
|
if (preset.events) {
|
||||||
for (let e of preset.events.split(",")) {
|
for (let e of preset.events.split(",")) {
|
||||||
let v = true;
|
let v = true;
|
||||||
e = e.trim();
|
e = e.trim();
|
||||||
@@ -488,12 +496,13 @@ function preProcessPreset(data) {
|
|||||||
}
|
}
|
||||||
if (e) events[e] = v;
|
if (e) events[e] = v;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
preset.events = events;
|
preset.events = events;
|
||||||
|
|
||||||
if (!preset.provision) throw new Error("Invalid preset provision");
|
if (!preset.provision) throw new Error("Invalid preset provision");
|
||||||
|
|
||||||
let configuration = {
|
const configuration = {
|
||||||
type: "provision",
|
type: "provision",
|
||||||
name: preset.provision
|
name: preset.provision
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-10
@@ -42,10 +42,9 @@ function getPermissionSets(roles) {
|
|||||||
"virtualParameters",
|
"virtualParameters",
|
||||||
"files"
|
"files"
|
||||||
].includes(k)
|
].includes(k)
|
||||||
)
|
) {
|
||||||
if (v.filter == null || v.filter === "") v.filter = true;
|
if (v.filter == null || v.filter === "") v.filter = true;
|
||||||
else {
|
else v.filter = expression.parse(v.filter);
|
||||||
v.filter = expression.parse(v.filter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
@@ -76,11 +75,13 @@ koa.use(
|
|||||||
);
|
);
|
||||||
|
|
||||||
koa.use(async (ctx, next) => {
|
koa.use(async (ctx, next) => {
|
||||||
if (ctx.state.user && ctx.state.user.roles)
|
if (ctx.state.user && ctx.state.user.roles) {
|
||||||
ctx.state.authorizer = new Authorizer(
|
ctx.state.authorizer = new Authorizer(
|
||||||
getPermissionSets(ctx.state.user.roles)
|
getPermissionSets(ctx.state.user.roles)
|
||||||
);
|
);
|
||||||
else ctx.state.authorizer = new Authorizer([]);
|
} else {
|
||||||
|
ctx.state.authorizer = new Authorizer([]);
|
||||||
|
}
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
@@ -89,7 +90,7 @@ router.post("/login", async ctx => {
|
|||||||
const username = ctx.request.body.username;
|
const username = ctx.request.body.username;
|
||||||
const password = ctx.request.body.password;
|
const password = ctx.request.body.password;
|
||||||
|
|
||||||
let log = {
|
const log = {
|
||||||
message: "Log in",
|
message: "Log in",
|
||||||
context: ctx,
|
context: ctx,
|
||||||
username: username
|
username: username
|
||||||
@@ -110,10 +111,9 @@ router.post("/login", async ctx => {
|
|||||||
logger.accessWarn(log);
|
logger.accessWarn(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
let roles;
|
const roles = await authSimple(username, password);
|
||||||
roles = await authSimple(username, password);
|
|
||||||
|
|
||||||
if (roles) return success(roles, "simple");
|
if (roles) return void success(roles, "simple");
|
||||||
|
|
||||||
failure();
|
failure();
|
||||||
});
|
});
|
||||||
@@ -172,7 +172,7 @@ koa.use(
|
|||||||
koa.use(router.routes());
|
koa.use(router.routes());
|
||||||
koa.use(koaStatic("./public"));
|
koa.use(koaStatic("./public"));
|
||||||
|
|
||||||
let server = http
|
const server = http
|
||||||
.createServer(koa.callback())
|
.createServer(koa.callback())
|
||||||
.listen(config.server.port, config.server.interface);
|
.listen(config.server.port, config.server.interface);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user