131 lines
3.3 KiB
JavaScript

const { createApp } = Vue;
createApp({
data() {
return {
loading: false,
error: "",
filter: "all",
keyword: "",
payload: {
updatedAt: "",
summary: {
total: 0,
ok: 0,
down: 0,
hostTotal: 0,
hostMetricOk: 0,
hostMetricDown: 0,
},
hosts: [],
},
timer: null,
};
},
computed: {
summary() {
return this.payload.summary || {
total: 0,
ok: 0,
down: 0,
hostTotal: 0,
hostMetricOk: 0,
hostMetricDown: 0,
};
},
formattedUpdatedAt() {
if (!this.payload.updatedAt) {
return "-";
}
const date = new Date(this.payload.updatedAt);
return Number.isNaN(date.getTime()) ? this.payload.updatedAt : date.toLocaleString();
},
groupedHosts() {
const keyword = this.keyword.trim().toLowerCase();
return (this.payload.hosts || [])
.map((host) => {
const items = (host.items || []).filter((item) => {
if (this.filter === "down" && item.ok) {
return false;
}
if (this.filter === "java" && item.kind !== "java") {
return false;
}
if (this.filter === "golang" && item.kind !== "golang") {
return false;
}
if (!keyword) {
return true;
}
return [item.host, item.ip, item.service, item.kind, item.group]
.join(" ")
.toLowerCase()
.includes(keyword);
});
return {
...host,
groupLabel: this.groupLabel(host.group),
items,
downCount: items.filter((item) => !item.ok).length,
};
})
.filter((host) => host.items.length > 0 || !keyword);
},
},
methods: {
groupLabel(group) {
return {
app: "应用节点",
pay: "支付节点",
gateway: "网关节点",
}[group] || group;
},
formatPercent(value) {
if (value == null || Number.isNaN(Number(value))) {
return "-";
}
return `${Number(value).toFixed(2)}%`;
},
formatGb(used, total) {
if (used == null || total == null) {
return "-";
}
return `${Number(used).toFixed(2)} / ${Number(total).toFixed(2)} GB`;
},
async refresh() {
this.loading = true;
this.error = "";
try {
const response = await fetch("/api/monitor/services", { cache: "no-store" });
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
this.payload = await response.json();
} catch (error) {
this.error = error instanceof Error ? error.message : String(error);
} finally {
this.loading = false;
}
},
},
mounted() {
const root = document.getElementById("app");
const bootIndicator = document.getElementById("boot-indicator");
if (root) {
root.removeAttribute("v-cloak");
}
if (bootIndicator) {
bootIndicator.hidden = true;
}
this.refresh();
this.timer = window.setInterval(() => this.refresh(), 10000);
},
beforeUnmount() {
if (this.timer) {
window.clearInterval(this.timer);
}
},
}).mount("#app");