144 lines
6.5 KiB
HTML
144 lines
6.5 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>HY App Monitor</title>
|
|
<link rel="stylesheet" href="/app.css" />
|
|
<script defer src="/vue.js"></script>
|
|
<script defer src="/app.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="boot-indicator" class="boot-indicator">加载中...</div>
|
|
<noscript>
|
|
<div class="boot-indicator">请启用 JavaScript</div>
|
|
</noscript>
|
|
|
|
<div id="app" v-cloak class="shell">
|
|
<header class="hero">
|
|
<div class="hero-copy">
|
|
<p class="eyebrow">HY APP MONITOR</p>
|
|
<h1>服务与主机监控</h1>
|
|
</div>
|
|
<div class="hero-side">
|
|
<div class="stat-block">
|
|
<span>服务总数</span>
|
|
<strong>{{ summary.total }}</strong>
|
|
</div>
|
|
<div class="stat-block ok">
|
|
<span>服务正常</span>
|
|
<strong>{{ summary.ok }}</strong>
|
|
</div>
|
|
<div class="stat-block bad">
|
|
<span>服务异常</span>
|
|
<strong>{{ summary.down }}</strong>
|
|
</div>
|
|
<div class="stat-block neutral">
|
|
<span>主机数</span>
|
|
<strong>{{ summary.hostTotal }}</strong>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="toolbar">
|
|
<div class="toolbar-left">
|
|
<button :class="['filter-chip', filter === 'all' ? 'active' : '']" @click="filter = 'all'">全部</button>
|
|
<button :class="['filter-chip', filter === 'down' ? 'active' : '']" @click="filter = 'down'">只看异常</button>
|
|
<button :class="['filter-chip', filter === 'java' ? 'active' : '']" @click="filter = 'java'">Java</button>
|
|
<button :class="['filter-chip', filter === 'golang' ? 'active' : '']" @click="filter = 'golang'">Golang</button>
|
|
</div>
|
|
<div class="toolbar-right">
|
|
<input v-model.trim="keyword" class="search" placeholder="搜索主机 / 服务 / IP" />
|
|
<button class="refresh" @click="refresh" :disabled="loading">{{ loading ? '刷新中...' : '立即刷新' }}</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="meta-strip">
|
|
<span>最后刷新:{{ formattedUpdatedAt }}</span>
|
|
<span>主机指标正常:{{ summary.hostMetricOk }} / {{ summary.hostTotal }}</span>
|
|
<span v-if="error" class="bad-text">加载失败:{{ error }}</span>
|
|
</section>
|
|
|
|
<section class="hosts">
|
|
<article v-for="group in groupedHosts" :key="group.host" class="host-panel">
|
|
<header class="host-header">
|
|
<div class="host-identity">
|
|
<h2>{{ group.host }}</h2>
|
|
<p>{{ group.ip }} · {{ group.groupLabel }}</p>
|
|
</div>
|
|
<div class="host-badges">
|
|
<span class="badge neutral">{{ group.serviceSummary.total }} 项服务</span>
|
|
<span :class="['badge', group.downCount > 0 ? 'bad' : 'ok']">
|
|
{{ group.downCount > 0 ? `${group.downCount} 异常` : '服务正常' }}
|
|
</span>
|
|
<span :class="['badge', group.metrics && group.metrics.ok ? 'ok' : 'bad']">
|
|
{{ group.metrics && group.metrics.ok ? '主机指标正常' : '主机指标异常' }}
|
|
</span>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="host-metrics">
|
|
<div class="metric-card">
|
|
<small>CPU</small>
|
|
<strong>{{ formatPercent(group.metrics.cpuPercent) }}</strong>
|
|
</div>
|
|
<div class="metric-card">
|
|
<small>内存</small>
|
|
<strong>{{ formatPercent(group.metrics.memoryPercent) }}</strong>
|
|
<span>{{ formatGb(group.metrics.memoryUsedGb, group.metrics.memoryTotalGb) }}</span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<small>磁盘</small>
|
|
<strong>{{ formatPercent(group.metrics.diskPercent) }}</strong>
|
|
<span>{{ formatGb(group.metrics.diskUsedGb, group.metrics.diskTotalGb) }}</span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<small>进程数</small>
|
|
<strong>{{ group.metrics.processCount ?? '-' }}</strong>
|
|
<span>{{ group.metrics.hostname || group.instanceId }}</span>
|
|
</div>
|
|
</section>
|
|
|
|
<p v-if="group.metrics && group.metrics.error" class="metric-error">{{ group.metrics.error }}</p>
|
|
|
|
<div class="service-grid">
|
|
<div v-for="item in group.items" :key="item.host + '-' + item.service" class="service-card">
|
|
<div class="service-top">
|
|
<div>
|
|
<div class="service-name">{{ item.service }}</div>
|
|
<div class="service-kind">{{ item.kind }}</div>
|
|
</div>
|
|
<div :class="['status-pill', item.ok ? 'ok' : 'bad']">
|
|
<span class="dot"></span>
|
|
{{ item.ok ? 'UP' : 'DOWN' }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="service-data">
|
|
<div>
|
|
<small>HTTP</small>
|
|
<strong>{{ item.statusCode || '-' }}</strong>
|
|
</div>
|
|
<div>
|
|
<small>耗时</small>
|
|
<strong>{{ item.latencyMs }}ms</strong>
|
|
</div>
|
|
<div>
|
|
<small>端口</small>
|
|
<strong>{{ item.port }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="service-url">{{ item.url }}</div>
|
|
<div class="service-detail">{{ item.detail || '无返回内容' }}</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</section>
|
|
</div>
|
|
</body>
|
|
|
|
</html>
|