feat(record): 增加测试结果保存和展示功能

This commit is contained in:
2025-12-25 11:29:10 +08:00
parent d5a44d2862
commit 7fdaf4da3d
22 changed files with 2103 additions and 64 deletions

View File

@@ -1,4 +1,4 @@
import { LatencyResult, TEST_NODES } from '@shared/types'
import { LatencyResult, TEST_NODES, IpInfo } from '@shared/types'
const API_BASE = '/api'
@@ -17,6 +17,34 @@ export interface BatchResultResponse {
latency: number | null
success: boolean
}>
resolvedAddress?: string
ipInfo?: IpInfo | null
}
export interface SaveResultRequest {
type: 'single' | 'compare'
input: { target: string } | { leftTarget: string; rightTarget: string }
results: Array<{ nodeId: string; latency: number | null; success: boolean }> | {
left: Array<{ nodeId: string; latency: number | null; success: boolean }>
right: Array<{ nodeId: string; latency: number | null; success: boolean }>
}
ipInfo?: IpInfo | { left: IpInfo | null; right: IpInfo | null } | null
}
export interface SaveResultResponse {
id: string
shareUrl: string
}
export interface SavedResultData {
type: 'single' | 'compare'
input: { target: string } | { leftTarget: string; rightTarget: string }
results: Array<{ nodeId: string; latency: number | null; success: boolean }> | {
left: Array<{ nodeId: string; latency: number | null; success: boolean }>
right: Array<{ nodeId: string; latency: number | null; success: boolean }>
}
ipInfo?: IpInfo | { left: IpInfo | null; right: IpInfo | null } | null
createdAt: string
}
export async function fetchUserIp(): Promise<string> {
@@ -26,10 +54,15 @@ export async function fetchUserIp(): Promise<string> {
return data.ip
}
export interface TestResult {
resolvedAddress?: string
ipInfo?: IpInfo | null
}
export async function testAllNodes(
target: string,
onProgress: (result: LatencyResult) => void
): Promise<void> {
): Promise<TestResult> {
for (const node of TEST_NODES) {
onProgress({ nodeId: node.id, latency: null, status: 'pending' })
}
@@ -44,7 +77,7 @@ export async function testAllNodes(
for (const node of TEST_NODES) {
onProgress({ nodeId: node.id, latency: null, status: 'failed' })
}
return
return {}
}
const { measurementId }: BatchMeasurementResponse = await res.json()
@@ -56,6 +89,8 @@ export async function testAllNodes(
const startTime = Date.now()
const timeout = 60000
const completedNodes = new Set<string>()
let resolvedAddress: string | undefined
let ipInfo: IpInfo | null | undefined
while (Date.now() - startTime < timeout) {
await new Promise(r => setTimeout(r, 800))
@@ -65,6 +100,16 @@ export async function testAllNodes(
const data: BatchResultResponse = await pollRes.json()
// Capture resolved IP address
if (!resolvedAddress && data.resolvedAddress) {
resolvedAddress = data.resolvedAddress
}
// Capture IP info when available
if (!ipInfo && data.ipInfo) {
ipInfo = data.ipInfo
}
for (const result of data.results) {
if (result.success && !completedNodes.has(result.nodeId)) {
completedNodes.add(result.nodeId)
@@ -89,4 +134,30 @@ export async function testAllNodes(
break
}
}
return { resolvedAddress, ipInfo }
}
export async function saveResult(data: SaveResultRequest): Promise<SaveResultResponse> {
const res = await fetch(`${API_BASE}/results`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
if (!res.ok) {
throw new Error('Failed to save result')
}
return res.json()
}
export async function fetchSavedResult(id: string): Promise<SavedResultData> {
const res = await fetch(`${API_BASE}/results/${id}`)
if (!res.ok) {
throw new Error('Failed to fetch result')
}
return res.json()
}