84 lines
2.2 KiB
JavaScript
84 lines
2.2 KiB
JavaScript
// Shared helpers for the Intrusion Detection web UI.
|
|
|
|
async function api(path, options = {}) {
|
|
const opts = { headers: {}, ...options };
|
|
if (opts.body && typeof opts.body !== "string") {
|
|
opts.headers["Content-Type"] = "application/json";
|
|
opts.body = JSON.stringify(opts.body);
|
|
}
|
|
const res = await fetch(path, opts);
|
|
let data = null;
|
|
try {
|
|
data = await res.json();
|
|
} catch (e) {
|
|
/* non-JSON */
|
|
}
|
|
if (!res.ok) {
|
|
const msg = (data && (data.error || data.message)) || `HTTP ${res.status}`;
|
|
throw new Error(msg);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return String(s ?? "").replace(
|
|
/[&<>"']/g,
|
|
(c) =>
|
|
({
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
'"': """,
|
|
"'": "'",
|
|
})[c],
|
|
);
|
|
}
|
|
|
|
function toast(message, type = "info") {
|
|
const box = document.getElementById("toasts");
|
|
if (!box) return;
|
|
const node = document.createElement("div");
|
|
node.className = `toast toast-${type}`;
|
|
node.textContent = message;
|
|
box.appendChild(node);
|
|
setTimeout(() => node.remove(), 4000);
|
|
}
|
|
|
|
function fmtTime(s) {
|
|
if (!s) return "—";
|
|
const d = new Date(s);
|
|
return isNaN(d) ? s : d.toLocaleString();
|
|
}
|
|
|
|
function badge(state) {
|
|
const node = document.createElement("span");
|
|
node.className = `badge ${state}`;
|
|
node.textContent = state;
|
|
return node;
|
|
}
|
|
|
|
function el(tag, attrs = {}, ...children) {
|
|
const node = document.createElement(tag);
|
|
// Allow el("tr", child1, child2, ...): if the 2nd argument is a node or an
|
|
// array, treat it as the first child instead of an attributes object.
|
|
if (attrs && (attrs.nodeType === 1 || Array.isArray(attrs))) {
|
|
children.unshift(attrs);
|
|
attrs = {};
|
|
}
|
|
for (const [k, v] of Object.entries(attrs || {})) {
|
|
if (k === "class") node.className = v;
|
|
else if (k === "text") node.textContent = v;
|
|
else if (k === "html") node.innerHTML = v;
|
|
else if (k.startsWith("on")) node.addEventListener(k.slice(2), v);
|
|
else node.setAttribute(k, v);
|
|
}
|
|
for (const c of children) {
|
|
node.append(c == null ? "" : c);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
async function startScan(kind, params) {
|
|
return api(`/api/scan/${kind}`, { method: "POST", body: params });
|
|
}
|