1
0

feat: 将 demo 项目转化为 HTTP 拨测监控工具

新增 YAML 配置解析(Bun 内置 YAML)、SQLite 数据存储(bun:sqlite)、按 interval 分组并发拨测引擎、REST API(/api/summary、/api/targets、/api/targets/:id/history、/api/targets/:id/trend)、React 前端 Dashboard(统计卡片、目标表格、可展开详情面板、recharts 趋势图)。CLI 简化为仅接受配置文件路径。移除 /api/demo 路由和相关 demo 代码。保留 /health、静态资源服务和 SPA fallback。
This commit is contained in:
2026-05-09 17:04:25 +08:00
parent 9267f6585c
commit 57d3a5cfb4
43 changed files with 2910 additions and 525 deletions

View File

@@ -21,155 +21,308 @@ body {
margin: 0;
}
.shell {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(320px, 460px);
gap: 32px;
align-items: center;
min-height: 100vh;
padding: 56px;
background:
radial-gradient(circle at top left, rgba(55, 125, 255, 0.18), transparent 34rem),
linear-gradient(135deg, #f8fbff 0%, #e3edf7 100%);
.dashboard {
max-width: 1100px;
margin: 0 auto;
padding: 32px 24px;
}
.hero,
.card {
border: 1px solid rgba(49, 83, 126, 0.14);
border-radius: 28px;
background: rgba(255, 255, 255, 0.78);
box-shadow: 0 24px 80px rgba(34, 57, 91, 0.16);
backdrop-filter: blur(18px);
.dashboard-header {
margin-bottom: 32px;
}
.hero {
padding: 48px;
.dashboard-header h1 {
margin: 0 0 4px;
font-size: 1.75rem;
letter-spacing: -0.03em;
}
.eyebrow {
margin: 0 0 18px;
color: #356dd2;
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.16em;
text-transform: uppercase;
}
h1,
h2,
p {
margin-top: 0;
}
h1 {
max-width: 760px;
margin-bottom: 20px;
font-size: clamp(3rem, 8vw, 7rem);
line-height: 0.9;
letter-spacing: -0.08em;
}
.summary {
max-width: 620px;
margin-bottom: 0;
color: #42546c;
font-size: 1.2rem;
line-height: 1.8;
}
.card {
padding: 32px;
}
.card-header {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 24px;
}
.card-header h2 {
.dashboard-subtitle {
margin: 0;
font-size: 1.25rem;
color: #61728a;
font-size: 0.9rem;
}
.error-banner {
padding: 12px 16px;
margin-bottom: 16px;
border: 1px solid rgba(229, 72, 77, 0.25);
border-radius: 12px;
color: #9f2228;
background: rgba(255, 240, 240, 0.8);
font-size: 0.85rem;
}
.summary-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 32px;
}
.summary-card {
padding: 20px;
border: 1px solid rgba(49, 83, 126, 0.12);
border-radius: 16px;
background: rgba(255, 255, 255, 0.85);
box-shadow: 0 4px 16px rgba(34, 57, 91, 0.08);
}
.card-value {
font-size: 1.75rem;
font-weight: 700;
letter-spacing: -0.02em;
}
.card-label {
margin-top: 4px;
color: #61728a;
font-size: 0.8rem;
}
.card-up .card-value {
color: #1fbf75;
}
.card-down .card-value {
color: #e5484d;
}
.card-latency .card-value {
color: #356dd2;
}
.target-table {
width: 100%;
border-collapse: collapse;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(49, 83, 126, 0.12);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(34, 57, 91, 0.08);
}
.target-table thead th {
padding: 12px 16px;
text-align: left;
font-size: 0.78rem;
font-weight: 600;
color: #61728a;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid rgba(49, 83, 126, 0.1);
background: rgba(236, 243, 252, 0.5);
}
.target-row {
cursor: pointer;
transition: background 0.15s;
}
.target-row:hover {
background: rgba(236, 243, 252, 0.6);
}
.target-row.expanded {
background: rgba(236, 243, 252, 0.5);
}
.target-row td {
padding: 12px 16px;
border-bottom: 1px solid rgba(49, 83, 126, 0.06);
font-size: 0.9rem;
}
.col-status {
width: 48px;
}
.col-name {
font-weight: 600;
}
.col-url {
color: #61728a;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.82rem;
max-width: 260px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.col-method {
width: 64px;
text-align: center;
}
.col-latency {
width: 80px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.col-sparkline {
width: 100px;
}
.status-dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 999px;
background: #f5a524;
box-shadow: 0 0 0 8px rgba(245, 165, 36, 0.14);
}
.status-dot[data-state="success"] {
.status-up {
background: #1fbf75;
box-shadow: 0 0 0 8px rgba(31, 191, 117, 0.14);
box-shadow: 0 0 0 6px rgba(31, 191, 117, 0.14);
}
.status-dot[data-state="error"] {
.status-down {
background: #e5484d;
box-shadow: 0 0 0 8px rgba(229, 72, 77, 0.14);
box-shadow: 0 0 0 6px rgba(229, 72, 77, 0.14);
}
.error {
padding: 16px;
border: 1px solid rgba(229, 72, 77, 0.25);
border-radius: 18px;
color: #9f2228;
background: rgba(255, 240, 240, 0.8);
.sparkline-empty {
color: #94a3b8;
font-size: 0.85rem;
}
.error p,
.message {
margin-bottom: 0;
.detail-cell {
padding: 0 !important;
border-bottom: 1px solid rgba(49, 83, 126, 0.1) !important;
background: rgba(240, 246, 252, 0.6);
}
.result {
.target-detail {
padding: 20px 24px;
}
.detail-stats {
display: grid;
gap: 24px;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.message {
color: #1c3f73;
font-size: 1.05rem;
font-weight: 700;
line-height: 1.6;
.detail-stat {
padding: 12px 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.7);
}
dl {
display: grid;
gap: 12px;
margin: 0;
}
dl div {
display: grid;
gap: 4px;
padding: 14px 16px;
border-radius: 16px;
background: rgba(236, 243, 252, 0.74);
}
dt {
.detail-stat-label {
display: block;
font-size: 0.75rem;
color: #61728a;
margin-bottom: 4px;
}
.detail-stat-value {
font-size: 1.15rem;
font-weight: 700;
}
.text-up {
color: #1fbf75;
}
.text-down {
color: #e5484d;
}
.detail-trend {
margin-bottom: 20px;
}
.detail-trend h4,
.detail-history h4 {
margin: 0 0 12px;
font-size: 0.9rem;
color: #42546c;
}
.trend-loading,
.trend-empty {
padding: 24px;
text-align: center;
color: #94a3b8;
font-size: 0.85rem;
}
.detail-history {
margin-top: 16px;
}
.history-item {
display: flex;
gap: 12px;
align-items: center;
padding: 8px 12px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.6);
font-size: 0.85rem;
}
.history-status {
font-weight: 700;
font-size: 0.78rem;
}
dd {
margin: 0;
overflow-wrap: anywhere;
.history-time {
color: #61728a;
}
.history-code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
color: #42546c;
}
@media (max-width: 860px) {
.shell {
grid-template-columns: 1fr;
padding: 24px;
.history-latency {
font-variant-numeric: tabular-nums;
color: #356dd2;
}
.history-error {
color: #e5484d;
font-size: 0.8rem;
}
.history-empty {
color: #94a3b8;
font-size: 0.85rem;
margin: 0;
}
.table-loading,
.table-empty {
padding: 40px;
text-align: center;
color: #94a3b8;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(49, 83, 126, 0.12);
border-radius: 16px;
}
@media (max-width: 768px) {
.dashboard {
padding: 16px;
}
.hero,
.card {
padding: 28px;
border-radius: 22px;
.summary-cards {
grid-template-columns: repeat(2, 1fr);
}
.detail-stats {
grid-template-columns: repeat(2, 1fr);
}
.col-method,
.col-sparkline {
display: none;
}
.target-row td {
padding: 10px 12px;
}
}