95 lines
3.8 KiB
JavaScript
95 lines
3.8 KiB
JavaScript
// komari 实时监控客户端: 公开 API, 无鉴权, 请求时实时获取, 不落库
|
|
// GET /api/nodes 节点列表
|
|
// GET /api/recent/{uuid} 最近实时状态 (数组, 多个时间点)
|
|
// GET /api/records/load?uuid=... 1 分钟粒度历史 (komari 保留 24h)
|
|
|
|
const BASE = process.env.KOMARI_BASE || 'https://komari.honor3.com'
|
|
const TIMEOUT = 15000
|
|
|
|
async function jget(path) {
|
|
const ctrl = new AbortController()
|
|
const timer = setTimeout(() => ctrl.abort(), TIMEOUT)
|
|
try {
|
|
const r = await fetch(BASE + path, { headers: { 'User-Agent': 'iboard-monitor/1.0' }, signal: ctrl.signal })
|
|
if (!r.ok) throw new Error('komari ' + path + ' HTTP ' + r.status)
|
|
const j = await r.json()
|
|
if (j.status && j.status !== 'success') throw new Error('komari ' + path + ' status: ' + j.status + ' ' + (j.message || ''))
|
|
return j.data
|
|
} finally {
|
|
clearTimeout(timer)
|
|
}
|
|
}
|
|
|
|
// 节点列表 + 每节点最新实时快照 (并行拉取, 单节点失败不拖垮整体)
|
|
export async function fetchServers() {
|
|
const nodes = await jget('/api/nodes')
|
|
if (!Array.isArray(nodes)) return []
|
|
const snaps = await Promise.all(
|
|
nodes
|
|
.filter((n) => n.uuid)
|
|
.map(async (n) => {
|
|
try {
|
|
const recent = await jget('/api/recent/' + n.uuid)
|
|
return { node: n, snap: Array.isArray(recent) && recent.length ? recent[recent.length - 1] : null }
|
|
} catch (e) {
|
|
console.error('[komari] 节点 ' + n.uuid + ' 快照失败:', e.message)
|
|
return { node: n, snap: null }
|
|
}
|
|
}),
|
|
)
|
|
return snaps.map(({ node: n, snap }) => {
|
|
const uuid = n.uuid
|
|
return {
|
|
uuid,
|
|
name: n.name || uuid,
|
|
cpuName: n.cpu_name || '',
|
|
os: n.os || '',
|
|
region: n.region || '',
|
|
memTotal: snap ? Number(snap.ram?.total || 0) : Number(n.mem_total || 0),
|
|
diskTotal: snap ? Number(snap.disk?.total || 0) : Number(n.disk_total || 0),
|
|
cpuCores: Number(n.cpu_cores || 0),
|
|
cpuUsage: snap ? Number(snap.cpu?.usage || 0) : 0,
|
|
ramUsed: snap ? Number(snap.ram?.used || 0) : 0,
|
|
swapUsed: snap ? Number(snap.swap?.used || 0) : 0,
|
|
load1: snap ? Number(snap.load?.load1 || 0) : 0,
|
|
load5: snap ? Number(snap.load?.load5 || 0) : 0,
|
|
load15: snap ? Number(snap.load?.load15 || 0) : 0,
|
|
diskUsed: snap ? Number(snap.disk?.used || 0) : 0,
|
|
netIn: snap ? Number(snap.network?.down || 0) : 0,
|
|
netOut: snap ? Number(snap.network?.up || 0) : 0,
|
|
uptime: snap ? Number(snap.uptime || 0) : 0,
|
|
process: snap ? Number(snap.process || 0) : 0,
|
|
connections: snap ? Number(snap.connections?.tcp || 0) : 0,
|
|
status: snap ? 'online' : 'unknown',
|
|
recordedAt: snap && snap.updated_at ? new Date(snap.updated_at).toISOString() : new Date().toISOString(),
|
|
}
|
|
})
|
|
}
|
|
|
|
// 节点时序: komari 保留最近 24h, 1 分钟粒度
|
|
// 节点未开启 record 或已下线 (komari 404) 时返回空列表, 不向上抛 500
|
|
export async function fetchHistory(uuid) {
|
|
let data
|
|
try {
|
|
data = await jget('/api/records/load?uuid=' + encodeURIComponent(uuid))
|
|
} catch (e) {
|
|
console.error('[komari] ' + uuid + ' history 获取失败:', e.message)
|
|
return []
|
|
}
|
|
const records = data && Array.isArray(data.records) ? data.records : []
|
|
return records
|
|
.map((r) => ({
|
|
period: new Date(r.time).toISOString(),
|
|
cpu: Number(r.cpu || 0),
|
|
ram: Number(r.ram || 0),
|
|
load: Number(r.load || 0),
|
|
disk: Number(r.disk || 0),
|
|
// net_in/net_out 为 komari 原始值 (字节口径), 前端展示时再按需换算
|
|
netIn: Number(r.net_in || 0),
|
|
netOut: Number(r.net_out || 0),
|
|
process: Number(r.process || 0),
|
|
connections: Number(r.connections || 0),
|
|
}))
|
|
.filter((r) => !isNaN(Date.parse(r.period)))
|
|
}
|