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:
@@ -1,91 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { DemoResponse } from "../shared/api";
|
||||
|
||||
type DemoState =
|
||||
| { status: "loading" }
|
||||
| { status: "success"; data: DemoResponse }
|
||||
| { status: "error"; message: string };
|
||||
import { useSummary } from "./hooks/useSummary";
|
||||
import { useTargets } from "./hooks/useTargets";
|
||||
import { SummaryCards } from "./components/SummaryCards";
|
||||
import { TargetTable } from "./components/TargetTable";
|
||||
|
||||
export function App() {
|
||||
const [demoState, setDemoState] = useState<DemoState>({ status: "loading" });
|
||||
const { data: summary, loading: summaryLoading, error: summaryError } = useSummary();
|
||||
const { data: targets, loading: targetsLoading, error: targetsError } = useTargets();
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
async function loadDemo() {
|
||||
try {
|
||||
const response = await fetch("/api/demo", { signal: abortController.signal });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = (await response.json()) as DemoResponse;
|
||||
setDemoState({ status: "success", data });
|
||||
} catch (error) {
|
||||
if (abortController.signal.aborted) return;
|
||||
|
||||
setDemoState({
|
||||
status: "error",
|
||||
message: error instanceof Error ? error.message : "未知错误",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void loadDemo();
|
||||
|
||||
return () => abortController.abort();
|
||||
}, []);
|
||||
const error = summaryError || targetsError;
|
||||
|
||||
return (
|
||||
<main className="shell">
|
||||
<section className="hero" aria-labelledby="page-title">
|
||||
<p className="eyebrow">Vite + React + Bun</p>
|
||||
<h1 id="page-title">Gateway Checker Demo</h1>
|
||||
<p className="summary">这个页面用于验证前端开发、Bun 后端 API 和单可执行文件打包链路已经跑通。</p>
|
||||
</section>
|
||||
<main className="dashboard">
|
||||
<header className="dashboard-header">
|
||||
<h1>Gateway Checker</h1>
|
||||
<p className="dashboard-subtitle">HTTP 拨测监控面板</p>
|
||||
</header>
|
||||
|
||||
<section className="card" aria-live="polite">
|
||||
<div className="card-header">
|
||||
<span className="status-dot" data-state={demoState.status} />
|
||||
<h2>后端连接状态</h2>
|
||||
{error && (
|
||||
<div className="error-banner">
|
||||
请求失败: {error},将在下一次轮询周期自动重试
|
||||
</div>
|
||||
)}
|
||||
|
||||
{demoState.status === "loading" ? <p>正在请求 /api/demo...</p> : null}
|
||||
|
||||
{demoState.status === "error" ? (
|
||||
<div className="error">
|
||||
<strong>请求失败</strong>
|
||||
<p>{demoState.message}</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{demoState.status === "success" ? (
|
||||
<div className="result">
|
||||
<p className="message">{demoState.data.message}</p>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>运行模式</dt>
|
||||
<dd>{demoState.data.runtime.mode}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Bun 版本</dt>
|
||||
<dd>{demoState.data.runtime.bunVersion}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>平台</dt>
|
||||
<dd>
|
||||
{demoState.data.runtime.platform}/{demoState.data.runtime.arch}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>响应时间</dt>
|
||||
<dd>{demoState.data.runtime.timestamp}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
<SummaryCards summary={summary} loading={summaryLoading} />
|
||||
<TargetTable targets={targets} loading={targetsLoading} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user