|
- {probeName(p.probe_id)}
+ {probeName(p.probe_id)} {p.ip_version === 'ipv6' ? 'IPv6' : 'IPv4'}
{p.probe_id}
|
{p.version ?? '—'} |
@@ -179,7 +185,7 @@ export function AgentDetail() {
-
+
测试
@@ -209,8 +215,12 @@ export function AgentDetail() {
- ID
- {a.id}
- - 公开地址
+ - IPv4 地址
- {a.public_host || '—'}
+ - IPv6 地址
+ - {a.ipv6_host || '未配置'}
+ - IPv6 监听
+ - {a.ipv6_supported ? '已启用' : '未启用,请升级 Agent 并配置双栈监听'}
- 控制端口
- {a.probe_tcp_port}/tcp
- 数据端口
diff --git a/web/admin/src/pages/Probes.tsx b/web/admin/src/pages/Probes.tsx
index 5629884..2c19aed 100644
--- a/web/admin/src/pages/Probes.tsx
+++ b/web/admin/src/pages/Probes.tsx
@@ -209,7 +209,7 @@ export function Probes() {
) : (
<>
{p.online ? '已连面板' : '未连面板'}
- 0 ? 'ok' : ''}>{p.connections.length > 0 ? `连着 ${p.connections.length} 个 Agent` : '没有连接'}
+ 0 ? 'ok' : ''}>{p.connections.length > 0 ? `连着 ${new Set(p.connections.map((c) => c.agent_id)).size} 个 Agent` : '没有连接'}
>
)}
{p.id}
@@ -252,10 +252,11 @@ export function Probes() {
{p.connections.map((c) => (
-
+
|
{c.agent_name}
+ {c.ip_version === 'ipv6' ? 'IPv6' : 'IPv4'}
|
{c.version ?? '—'} |
diff --git a/web/admin/src/pages/Tests.tsx b/web/admin/src/pages/Tests.tsx
index 70c3da1..0dfb38f 100644
--- a/web/admin/src/pages/Tests.tsx
+++ b/web/admin/src/pages/Tests.tsx
@@ -1,5 +1,6 @@
import { FlaskConical, Square } from 'lucide-react'
import { useEffect, useState } from 'react'
+import { IpVersionPicker, ipVersionOf } from '@shared/ipVersions'
import { useSearchParams } from 'react-router-dom'
import { api } from '@shared/api/client'
import type { AgentDto, Page, PlanDto, ProbeDto, TestDto } from '@shared/api/types'
@@ -84,6 +85,7 @@ function Result({ test }: { test: TestDto }) {
export function Tests() {
const [params, setParams] = useSearchParams()
+ const ipVersion = ipVersionOf(params.get('ip_version'))
const agents = useQuery(() => api.get>('/api/admin/agents'), [], 10_000)
const probes = useQuery(() => api.get>('/api/admin/probes'), [])
const plans = useQuery(() => api.get>('/api/public/catalog/plans'), [])
@@ -92,16 +94,18 @@ export function Tests() {
const active = (agents.data?.items ?? []).filter((a) => a.status === 'active')
const agentId = params.get('agent') ?? active[0]?.id ?? ''
const agent = active.find((a) => a.id === agentId)
- const connected = agent?.probes ?? []
+ const connected = (agent?.probes ?? []).filter((p) => ipVersionOf(p.ip_version) === ipVersion)
const probeId = params.get('probe') ?? connected[0]?.probe_id ?? ''
const [planId, setPlanId] = useState('')
const [selected, setSelected] = useState()
- const line = agentId && probeId ? `${agentId}~${probeId}` : ''
+ const line = agentId && probeId ? `${agentId}~${probeId}${ipVersion === 'ipv6' ? '~ipv6' : ''}` : ''
useEffect(() => {
if (!planId && plans.data?.items[0]) setPlanId(plans.data.items[0].id)
}, [plans.data, planId])
+ useEffect(() => { setSelected(undefined) }, [line])
+
const tests = useQuery(() => (line ? api.get>(`/api/admin/lines/${encodeURIComponent(line)}/tests`, { limit: 20 }) : Promise.resolve(undefined)), [line], 3000)
const detail = useQuery(() => (selected ? api.get(`/api/public/tests/${selected}`) : Promise.resolve(undefined)), [selected, tests.data?.items[0]?.status], 3000)
const probeName = (id: string) => probes.data?.items.find((p) => p.id === id)?.name ?? id
@@ -118,6 +122,7 @@ export function Tests() {
<>
按需测试
+ set({ ip_version: v })} />
{agents.loading && !agents.data ? : null}
@@ -190,7 +195,7 @@ export function Tests() {
) : (
- {tests.data?.items.map((t) => (
+ {tests.data?.items.filter((t) => t.agent_id === agentId && t.probe_id === probeId && ipVersionOf(t.ip_version) === ipVersion).map((t) => (
setSelected(t.id)} style={selected === t.id ? { background: 'var(--app-card-hover)' } : undefined}>
{STATUS[t.status]?.label ?? t.status}
@@ -221,7 +226,7 @@ export function Tests() {
结果
- {detail.data ? (
+ {detail.data && detail.data.id === selected && ipVersionOf(detail.data.ip_version) === ipVersion ? (
<>
{detail.data.plan} · {STATUS[detail.data.status]?.label ?? detail.data.status}
diff --git a/web/display/src/App.tsx b/web/display/src/App.tsx
index cd5165a..a9279dd 100644
--- a/web/display/src/App.tsx
+++ b/web/display/src/App.tsx
@@ -1,20 +1,28 @@
import { BuildFooter } from './BuildFooter'
-import { NavLink, Navigate, Route, Routes } from 'react-router-dom'
+import { NavLink, Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom'
import { SourceButton, ThemeToggle } from '@shared/ui'
import { Compare } from './pages/Compare'
import { Line } from './pages/Line'
import { Overview } from './pages/Overview'
+function LineRoute() {
+ const { id } = useParams()
+ return
+}
+
export function App() {
+ const location = useLocation()
+ const ipv6 = location.pathname.endsWith('~ipv6') || new URLSearchParams(location.search).get('ip_version') === 'ipv6'
+ const familyQuery = ipv6 ? '?ip_version=ipv6' : ''
return (
<>
| |