refactor(proxy): 移除 unhealthy 状态、前端展示失败率替换连接数、命令提示修正

- 移除 ProxyNodeStatus.UNHEALTHY 枚举值,仅保留 online/offline
- 迁移脚本将已有 unhealthy 数据迁移为 offline,upgrade/downgrade 均有幂等性保护
- 前端代理节点列表用失败率列替换连接数列,超过 5% 高亮显示
- 节点列表排序从 updated_at DESC 改为 name ASC
- Rust 端命令行提示从 aether-proxy 改为 ./aether-proxy
This commit is contained in:
fawney19
2026-02-28 14:44:44 +08:00
parent 4b02078b60
commit 87d50052cc
7 changed files with 78 additions and 21 deletions

View File

@@ -124,10 +124,10 @@
状态
</TableHead>
<TableHead class="w-[100px] h-12 font-semibold text-center">
连接数
总请求
</TableHead>
<TableHead class="w-[100px] h-12 font-semibold text-center">
总请求
失败率
</TableHead>
<TableHead class="w-[100px] h-12 font-semibold text-center">
延迟
@@ -181,10 +181,13 @@
</Badge>
</TableCell>
<TableCell class="py-4 text-center">
<span class="text-sm tabular-nums">{{ node.active_connections }}</span>
<span class="text-sm tabular-nums">{{ formatNumber(node.total_requests) }}</span>
</TableCell>
<TableCell class="py-4 text-center">
<span class="text-sm tabular-nums">{{ formatNumber(node.total_requests) }}</span>
<span
class="text-sm tabular-nums"
:class="failureRate(node) > 5 ? 'text-destructive font-medium' : ''"
>{{ formatFailureRate(node) }}</span>
</TableCell>
<TableCell class="py-4 text-center">
<span class="text-sm tabular-nums">{{ node.avg_latency_ms != null ? `${node.avg_latency_ms.toFixed(0)}ms` : '-' }}</span>
@@ -301,14 +304,21 @@
{{ statusLabel(node.status) }}
</Badge>
</div>
<div class="grid grid-cols-3 gap-2 text-xs text-muted-foreground mb-3">
<div class="grid grid-cols-4 gap-2 text-xs text-muted-foreground mb-3">
<div>
<span class="block text-foreground/60">区域</span>
<span>{{ formatRegion(node.region) }}</span>
</div>
<div>
<span class="block text-foreground/60">连接</span>
<span class="tabular-nums">{{ node.active_connections }}</span>
<span class="block text-foreground/60">总请求</span>
<span class="tabular-nums">{{ formatNumber(node.total_requests) }}</span>
</div>
<div>
<span class="block text-foreground/60">失败率</span>
<span
class="tabular-nums"
:class="failureRate(node) > 5 ? 'text-destructive font-medium' : ''"
>{{ formatFailureRate(node) }}</span>
</div>
<div>
<span class="block text-foreground/60">延迟</span>
@@ -980,6 +990,20 @@ function formatTime(iso: string | null) {
return d.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
}
function failureRate(node: ProxyNode) {
if (!node.total_requests) return 0
const failed = (node.failed_requests || 0) + (node.dns_failures || 0) + (node.stream_errors || 0)
return (failed / node.total_requests) * 100
}
function formatFailureRate(node: ProxyNode) {
if (!node.total_requests) return '-'
const rate = failureRate(node)
if (rate === 0) return '0%'
if (rate < 0.1) return '<0.1%'
return `${rate.toFixed(1)}%`
}
function nodeAddress(node: ProxyNode) {
if (node.is_manual) return node.proxy_url || `${node.ip}:${node.port}`
if (node.tunnel_mode) return node.ip || 'WebSocket Tunnel'