Files
minerops/backend/app/frontend/static/agent-management.js
T
2026-08-27 17:00:56 +00:00

415 lines
12 KiB
JavaScript

(function () {
"use strict";
if (document.body.dataset.view !== "agent_management") return;
const ids = [
"agentManagementLoading",
"agentManagementError",
"agentManagementErrorText",
"agentManagementRetry",
"agentManagementContent",
"agentManagementRefresh",
"agentCount",
"agentZoneCount",
"agentWaitingEnrollmentCount",
"agentRows",
"agentEmpty",
"agentZoneRows",
"agentZoneEmpty",
"agentEnrollmentRows",
"agentEnrollmentEmpty",
"agentCreateForm",
"agentSite",
"agentZone",
"agentDisplayName",
"agentPlatform",
"agentCreateSubmit",
"agentCreateStatus",
"agentEnrollmentResult",
"agentEnrollmentToken",
"agentEnrollmentExpires",
"agentEnrollmentCopy",
"agentEnrollmentDismiss",
"agentEnrollmentCopyStatus"
];
const el = {};
ids.forEach(function (id) {
el[id] = document.getElementById(id);
});
let registry = {
agents: [],
sites: [],
zones: []
};
let enrollments = [];
function text(value, fallback) {
if (value === null || value === undefined || value === "") {
return fallback || "Unavailable";
}
return String(value);
}
function dateTime(value) {
if (!value) return "Never";
const parsed = new Date(value);
if (Number.isNaN(parsed.valueOf())) return "Unavailable";
return parsed.toLocaleString();
}
function words(value) {
return text(value, "Unknown")
.replaceAll("_", " ")
.replace(/\b\w/g, function (letter) {
return letter.toUpperCase();
});
}
function cell(value, className) {
const node = document.createElement("td");
if (className) node.className = className;
node.textContent = value;
return node;
}
function setState(name) {
el.agentManagementLoading.hidden = name !== "loading";
el.agentManagementError.hidden = name !== "error";
el.agentManagementContent.hidden = name !== "ready";
}
async function api(path, options) {
const response = await fetch(path, Object.assign({
credentials: "same-origin",
cache: "no-store",
headers: {
"Accept": "application/json"
}
}, options || {}));
const payload = await response.json().catch(function () {
return {};
});
if (!response.ok) {
const detail = payload && payload.detail
? String(payload.detail)
: "Request failed with status " + response.status + ".";
throw new Error(detail);
}
return payload;
}
function renderAgents() {
el.agentRows.replaceChildren();
registry.agents.forEach(function (agent) {
const row = document.createElement("tr");
const identity = document.createElement("td");
const name = document.createElement("strong");
const id = document.createElement("span");
name.textContent = text(agent.display_name, "Unnamed Agent");
id.className = "secondary";
id.textContent = agent.agent_id;
identity.append(name, id);
const location = [
text(agent.site_name, "Unknown site"),
text(agent.agent_zone_name, "No Agent Zone")
].join(" / ");
const host = [
text(agent.hostname, "Unknown host"),
text(agent.ip_address, "No IP")
].join(" / ");
row.append(
identity,
cell(location),
cell(host),
cell(text(agent.version, "Unknown")),
cell(dateTime(agent.last_checkin_at)),
cell(words(agent.credential_state)),
cell(words(agent.routing_state))
);
el.agentRows.appendChild(row);
});
el.agentEmpty.hidden = registry.agents.length !== 0;
}
function renderZones() {
el.agentZoneRows.replaceChildren();
registry.zones.forEach(function (zone) {
const row = document.createElement("tr");
const networks = Array.isArray(zone.networks) && zone.networks.length
? zone.networks.join(", ")
: "No networks recorded";
row.append(
cell(zone.site_name),
cell(zone.name),
cell(networks),
cell(text(zone.assigned_agent_id, "Unassigned")),
cell(zone.is_enabled ? "Enabled" : "Disabled")
);
el.agentZoneRows.appendChild(row);
});
el.agentZoneEmpty.hidden = registry.zones.length !== 0;
}
function renderEnrollments() {
el.agentEnrollmentRows.replaceChildren();
enrollments.forEach(function (enrollment) {
const row = document.createElement("tr");
row.append(
cell(enrollment.display_name),
cell(
enrollment.site_name + " / " +
enrollment.agent_zone_name
),
cell(words(enrollment.status)),
cell(dateTime(enrollment.created_at)),
cell(dateTime(enrollment.expires_at)),
cell(text(enrollment.enrolled_agent_id, "Not enrolled"))
);
el.agentEnrollmentRows.appendChild(row);
});
el.agentEnrollmentEmpty.hidden = enrollments.length !== 0;
}
function populateSites() {
if (!el.agentSite) return;
const selected = el.agentSite.value;
el.agentSite.replaceChildren();
const placeholder = document.createElement("option");
placeholder.value = "";
placeholder.textContent = "Choose a site";
el.agentSite.appendChild(placeholder);
registry.sites.forEach(function (site) {
const option = document.createElement("option");
option.value = site.id;
option.textContent = site.name;
el.agentSite.appendChild(option);
});
if ([...el.agentSite.options].some(function (option) {
return option.value === selected;
})) {
el.agentSite.value = selected;
}
populateZones();
}
function populateZones() {
if (!el.agentZone || !el.agentSite) return;
const siteId = el.agentSite.value;
const selected = el.agentZone.value;
el.agentZone.replaceChildren();
const placeholder = document.createElement("option");
placeholder.value = "";
if (!siteId) {
placeholder.textContent = "Choose a site first";
el.agentZone.disabled = true;
el.agentZone.appendChild(placeholder);
return;
}
placeholder.textContent = "Choose an Agent Zone";
el.agentZone.appendChild(placeholder);
const available = registry.zones.filter(function (zone) {
return (
zone.site_id === siteId &&
zone.is_enabled === true &&
!zone.assigned_agent_id
);
});
available.forEach(function (zone) {
const option = document.createElement("option");
option.value = zone.id;
option.textContent = zone.name;
el.agentZone.appendChild(option);
});
el.agentZone.disabled = available.length === 0;
if ([...el.agentZone.options].some(function (option) {
return option.value === selected;
})) {
el.agentZone.value = selected;
}
}
function renderSummary() {
el.agentCount.textContent = String(registry.agents.length);
el.agentZoneCount.textContent = String(registry.zones.length);
const waiting = enrollments.filter(function (item) {
return item.status === "waiting";
}).length;
el.agentWaitingEnrollmentCount.textContent = String(waiting);
}
function render() {
renderSummary();
renderAgents();
renderZones();
renderEnrollments();
populateSites();
}
async function refreshData(showLoading) {
if (showLoading !== false) setState("loading");
try {
const results = await Promise.all([
api("/api/agents"),
api("/api/agent-enrollments")
]);
registry = results[0] && results[0].data
? results[0].data
: { agents: [], sites: [], zones: [] };
enrollments = results[1] && Array.isArray(results[1].data)
? results[1].data
: [];
render();
setState("ready");
} catch (error) {
el.agentManagementErrorText.textContent =
error.message || "Agent information could not be loaded.";
setState("error");
}
}
async function createEnrollment(event) {
event.preventDefault();
if (!el.agentCreateForm) return;
el.agentCreateSubmit.disabled = true;
el.agentCreateStatus.textContent = "Creating enrollment...";
try {
const payload = await api("/api/agent-enrollments", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
site_id: el.agentSite.value,
agent_zone_id: el.agentZone.value,
display_name: el.agentDisplayName.value.trim()
})
});
const enrollment = payload.data;
el.agentEnrollmentToken.textContent =
enrollment.enrollment_token;
el.agentEnrollmentExpires.textContent =
"Expires " + dateTime(enrollment.expires_at);
el.agentEnrollmentCopyStatus.textContent = "";
el.agentEnrollmentResult.hidden = false;
el.agentCreateStatus.textContent = "Enrollment created.";
el.agentDisplayName.value = "";
await refreshData(false);
} catch (error) {
el.agentCreateStatus.textContent =
error.message || "Enrollment could not be created.";
} finally {
el.agentCreateSubmit.disabled = false;
}
}
async function copyToken() {
const token = el.agentEnrollmentToken.textContent;
if (!token) return;
try {
await navigator.clipboard.writeText(token);
el.agentEnrollmentCopyStatus.textContent =
"Enrollment token copied.";
} catch (error) {
el.agentEnrollmentCopyStatus.textContent =
"Copy was unavailable. Select and copy the token manually.";
}
}
function dismissToken() {
el.agentEnrollmentToken.textContent = "";
el.agentEnrollmentExpires.textContent = "";
el.agentEnrollmentCopyStatus.textContent = "";
el.agentEnrollmentResult.hidden = true;
}
el.agentManagementRetry.addEventListener("click", function () {
refreshData(true);
});
if (el.agentManagementRefresh) {
el.agentManagementRefresh.addEventListener("click", function () {
refreshData(true);
});
}
if (el.agentSite) {
el.agentSite.addEventListener("change", populateZones);
}
if (el.agentCreateForm) {
el.agentCreateForm.addEventListener("submit", createEnrollment);
}
if (el.agentEnrollmentCopy) {
el.agentEnrollmentCopy.addEventListener("click", copyToken);
}
if (el.agentEnrollmentDismiss) {
el.agentEnrollmentDismiss.addEventListener(
"click",
dismissToken
);
}
refreshData(true);
}());