124 lines
5.3 KiB
HTML
124 lines
5.3 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">
|
||
<strong>HY App Monitor</strong>
|
||
<span>正在加载页面资源,请稍候。</span>
|
||
</div>
|
||
<noscript>
|
||
<div class="boot-indicator noscript-indicator">
|
||
<strong>浏览器已禁用 JavaScript</strong>
|
||
<span>当前页面依赖脚本渲染,请启用 JavaScript 后再访问。</span>
|
||
</div>
|
||
</noscript>
|
||
<div id="app" v-cloak class="shell">
|
||
<header class="hero">
|
||
<div class="hero-copy">
|
||
<p class="eyebrow">HY / Deploy Monitor</p>
|
||
<h1>内网服务实时巡检面板</h1>
|
||
<p class="hero-text">
|
||
页面本身不直接请求内网服务。所有探测都由 deploy 机器代发,适合直接通过公网 6666 访问。
|
||
</p>
|
||
</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>
|
||
</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 v-if="error" class="bad-text">加载失败:{{ error }}</span>
|
||
<span v-else>自动刷新:10 秒</span>
|
||
</section>
|
||
|
||
<section class="hosts">
|
||
<article v-for="group in groupedHosts" :key="group.host" class="host-panel">
|
||
<header class="host-header">
|
||
<div>
|
||
<h2>{{ group.host }}</h2>
|
||
<p>{{ group.ip }} · {{ group.groupLabel }}</p>
|
||
</div>
|
||
<div class="host-badges">
|
||
<span class="badge neutral">{{ group.items.length }} 项</span>
|
||
<span :class="['badge', group.downCount > 0 ? 'bad' : 'ok']">
|
||
{{ group.downCount > 0 ? `${group.downCount} 异常` : '全部正常' }}
|
||
</span>
|
||
</div>
|
||
</header>
|
||
|
||
<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>
|