feat: publish standalone PIGWay Agent Console plugin
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { state as appState } from "/assets/js/state.js";
|
||||
import { $, esc } from "/assets/js/shared.js";
|
||||
import { get, rows } from "/assets/js/navigation.js";
|
||||
import { loadConfig } from "/assets/js/settings.js";
|
||||
|
||||
export function tokenHeaders() {
|
||||
return { "Content-Type": "application/json" };
|
||||
}
|
||||
export async function loadServices() {
|
||||
if (!$("systemServices")) return;
|
||||
try {
|
||||
appState.serviceReadOnly = false;
|
||||
const d = await get(
|
||||
`/api/v1/services?host=${encodeURIComponent(appState.currentConfigHost)}`,
|
||||
);
|
||||
appState.serviceCache = d.services;
|
||||
renderServiceLists();
|
||||
} catch (e) {
|
||||
$("systemServices").innerHTML =
|
||||
`<div class="error">${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
export function renderServiceLists() {
|
||||
if (!$("systemServices")) return;
|
||||
const q = $("serviceSearch").value.trim().toLowerCase(),
|
||||
filtered = appState.serviceCache.filter(
|
||||
(s) =>
|
||||
!q || `${s.unit} ${s.description} ${s.note}`.toLowerCase().includes(q),
|
||||
);
|
||||
rows(
|
||||
"systemServices",
|
||||
filtered.map(
|
||||
(s) =>
|
||||
`<div class="row"><span><b>${esc(s.unit)}</b><br><span class="muted">${esc(s.description || "暂无服务说明")} · ${esc(s.active === "active" ? "正在运行" : "未运行")}</span></span><span class="row-actions"><span class="badge ${s.monitored && !s.paused ? "good" : ""}">${s.monitored ? (s.paused ? "已暂停" : "已监控") : "未监控"}</span><button data-select-service="${esc(s.unit)}">${s.monitored ? "编辑监控" : "加入监控"}</button></span></div>`,
|
||||
),
|
||||
"没有匹配的服务",
|
||||
);
|
||||
const monitored = appState.serviceCache.filter((s) => s.monitored);
|
||||
rows(
|
||||
"monitoredServices",
|
||||
monitored.map(
|
||||
(s) =>
|
||||
`<div class="row"><span><b>${esc(s.monitor_name)}</b> <span class="badge ${s.paused ? "" : s.active === "active" ? "good" : "bad"}">${s.paused ? "监控已暂停" : s.active === "active" ? "运行中" : "服务已停止"}</span><br><span class="muted">${esc(s.unit)}</span><br><span class="muted">备注:${esc(s.note || "未填写")}</span></span><span class="row-actions"><button data-monitor-action="${s.paused ? "resume" : "pause"}" data-unit="${esc(s.unit)}">${s.paused ? "恢复监控" : "暂停监控"}</button><button data-service-action="${s.active === "active" ? "stop" : "start"}" data-unit="${esc(s.unit)}" ${s.protected && s.active === "active" ? 'disabled title="API 服务不能停止自身"' : ""}>${s.active === "active" ? "停止服务" : "启动服务"}</button><button data-select-service="${esc(s.unit)}">编辑</button><button class="danger" data-delete-monitor="${esc(s.unit)}">删除监控</button></span></div>`,
|
||||
),
|
||||
"尚未监控任何服务",
|
||||
);
|
||||
if (appState.serviceReadOnly)
|
||||
document
|
||||
.querySelectorAll("#configRows button")
|
||||
.forEach((b) => (b.disabled = true));
|
||||
document
|
||||
.querySelectorAll("[data-select-service]")
|
||||
.forEach((b) => (b.onclick = () => selectService(b.dataset.selectService)));
|
||||
document
|
||||
.querySelectorAll("[data-monitor-action]")
|
||||
.forEach(
|
||||
(b) =>
|
||||
(b.onclick = () =>
|
||||
monitorAction(b.dataset.unit, b.dataset.monitorAction)),
|
||||
);
|
||||
document
|
||||
.querySelectorAll("[data-service-action]")
|
||||
.forEach(
|
||||
(b) =>
|
||||
(b.onclick = () =>
|
||||
serviceAction(b.dataset.unit, b.dataset.serviceAction)),
|
||||
);
|
||||
document
|
||||
.querySelectorAll("[data-delete-monitor]")
|
||||
.forEach((b) => (b.onclick = () => deleteMonitor(b.dataset.deleteMonitor)));
|
||||
}
|
||||
export function selectService(unit) {
|
||||
const s = appState.serviceCache.find((x) => x.unit === unit);
|
||||
if (!s) return;
|
||||
appState.editingServiceName = s.monitor_name || "";
|
||||
$("monitorTarget").value = s.unit;
|
||||
$("monitorName").value =
|
||||
s.monitor_name ||
|
||||
s.unit
|
||||
.replace(/\.service$/, "")
|
||||
.replace(/[^A-Za-z0-9_.-]/g, "_")
|
||||
.slice(0, 16)
|
||||
.toUpperCase();
|
||||
$("monitorNote").value = s.note || s.description || "";
|
||||
$("serviceFormTitle").textContent = s.monitored
|
||||
? "编辑服务监控"
|
||||
: "新建服务监控";
|
||||
$("serviceMessage").textContent = "已自动填写,可继续修改。";
|
||||
$("monitorName").scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
export function resetMonitorForm() {
|
||||
appState.editingServiceName = "";
|
||||
$("monitorName").value = "";
|
||||
$("monitorTarget").value = "";
|
||||
$("monitorNote").value = "";
|
||||
$("serviceFormTitle").textContent = "新建服务监控";
|
||||
$("serviceMessage").textContent = "";
|
||||
$("serviceMessage").className = "muted";
|
||||
}
|
||||
export async function saveMonitor() {
|
||||
const body = {
|
||||
host: appState.currentConfigHost,
|
||||
operation: "save",
|
||||
name: $("monitorName").value.trim(),
|
||||
target: $("monitorTarget").value.trim(),
|
||||
note: $("monitorNote").value.trim(),
|
||||
previous_name: appState.editingServiceName,
|
||||
};
|
||||
try {
|
||||
$("serviceMessage").textContent = "正在保存…";
|
||||
await get("/api/v1/services", {
|
||||
method: "PUT",
|
||||
headers: tokenHeaders(),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
resetMonitorForm();
|
||||
await loadServices();
|
||||
$("serviceMessage").textContent = "保存成功";
|
||||
$("serviceMessage").className = "success";
|
||||
} catch (e) {
|
||||
$("serviceMessage").textContent = e.message;
|
||||
$("serviceMessage").className = "error";
|
||||
}
|
||||
}
|
||||
export async function deleteMonitor(unit) {
|
||||
const s = appState.serviceCache.find((x) => x.unit === unit);
|
||||
if (
|
||||
!s ||
|
||||
!confirm(
|
||||
appState.LANG === "zh"
|
||||
? `确定删除 ${s.monitor_name} 的监控吗?\n不会删除或停止 ${unit}。`
|
||||
: `Remove monitoring for ${s.monitor_name}?\nThis will not remove or stop ${unit}.`,
|
||||
)
|
||||
)
|
||||
return;
|
||||
try {
|
||||
await get("/api/v1/services", {
|
||||
method: "PUT",
|
||||
headers: tokenHeaders(),
|
||||
body: JSON.stringify({
|
||||
host: appState.currentConfigHost,
|
||||
operation: "delete",
|
||||
name: s.monitor_name,
|
||||
target: s.unit,
|
||||
note: "",
|
||||
}),
|
||||
});
|
||||
await loadConfig();
|
||||
document.querySelector('[data-config-section="services"]')?.click();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
export async function monitorAction(unit, action) {
|
||||
const s = appState.serviceCache.find((x) => x.unit === unit);
|
||||
if (!s) return;
|
||||
try {
|
||||
await get("/api/v1/services", {
|
||||
method: "PUT",
|
||||
headers: tokenHeaders(),
|
||||
body: JSON.stringify({
|
||||
host: appState.currentConfigHost,
|
||||
operation: action,
|
||||
name: s.monitor_name,
|
||||
target: s.unit,
|
||||
note: s.note || "",
|
||||
}),
|
||||
});
|
||||
await loadServices();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
export async function serviceAction(unit, action) {
|
||||
if (
|
||||
!confirm(
|
||||
appState.LANG === "zh"
|
||||
? `确定${action === "start" ? "启动服务" : "停止服务"} ${unit} 吗?\n这会直接执行 systemctl ${action}。`
|
||||
: `${action === "start" ? "Start" : "Stop"} ${unit}?\nThis runs systemctl ${action} directly.`,
|
||||
)
|
||||
)
|
||||
return;
|
||||
try {
|
||||
await get("/api/v1/services/control", {
|
||||
method: "PUT",
|
||||
headers: tokenHeaders(),
|
||||
body: JSON.stringify({ host: appState.currentConfigHost, unit, action }),
|
||||
});
|
||||
setTimeout(loadServices, 700);
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user