mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: 代理节点架构重构与功能增强
aether-proxy: - 重构 main.rs,拆分为 app/state/hardware/net 模块 - setup.rs 拆分为 setup/tui.rs + setup/service.rs,支持 systemd 服务管理子命令 - 新增 delegate 端点,支持后端通过代理节点转发请求而非传统 CONNECT 代理 - 注册时上报硬件信息(CPU/内存/fd_limit)和估算最大并发数 - 心跳上报活跃连接数,支持远程下发 node_name 配置 - HTTP 转发时剥离 X-Forwarded-* 等敏感头部 - 切换到 rustls-tls,降低日志级别减少噪音 后端: - 从 http_client.py 提取代理相关逻辑至 proxy_node/resolver.py - 从 routes.py 提取业务逻辑至 proxy_node/service.py - handler 支持 delegate 模式(通过代理节点 HTTP 端点转发而非 CONNECT 隧道) - ProxyNode 模型新增 hardware_info 和 estimated_max_concurrency 字段 前端: - 新增 HardwareTooltip 组件展示节点硬件信息 - 远程配置支持下发 node_name
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import apiClient from './client'
|
||||
|
||||
export interface ProxyNodeRemoteConfig {
|
||||
node_name?: string
|
||||
allowed_ports?: number[]
|
||||
log_level?: string
|
||||
heartbeat_interval?: number
|
||||
@@ -19,6 +20,9 @@ export interface ProxyNode {
|
||||
proxy_url?: string
|
||||
proxy_username?: string
|
||||
proxy_password?: string // 脱敏后的密码
|
||||
// 硬件信息(aether-proxy 节点)
|
||||
hardware_info: Record<string, any> | null
|
||||
estimated_max_concurrency: number | null
|
||||
// 远程配置(aether-proxy 节点)
|
||||
remote_config: ProxyNodeRemoteConfig | null
|
||||
config_version: number
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
:z-index="70"
|
||||
@update:model-value="$emit('update:open', $event)"
|
||||
>
|
||||
<template v-if="providerId && items.length > 0" #header-actions>
|
||||
<template
|
||||
v-if="providerId && items.length > 0"
|
||||
#header-actions
|
||||
>
|
||||
<DropdownMenu :modal="false">
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button
|
||||
|
||||
@@ -107,7 +107,10 @@
|
||||
>
|
||||
|
||||
<!-- 主区域:拖拽 或 粘贴输入框(同一位置切换) -->
|
||||
<div v-if="!importText" class="mt-3">
|
||||
<div
|
||||
v-if="!importText"
|
||||
class="mt-3"
|
||||
>
|
||||
<!-- 拖拽模式 -->
|
||||
<div
|
||||
v-if="!showManualInput"
|
||||
@@ -168,7 +171,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 已有内容(文件导入后):显示文本框 -->
|
||||
<div v-if="importText" class="space-y-2">
|
||||
<div
|
||||
v-if="importText"
|
||||
class="space-y-2"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-muted-foreground">{{ importFileName || '已粘贴内容' }}</span>
|
||||
<button
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="space-y-6 pb-8">
|
||||
<Card variant="default" class="overflow-hidden">
|
||||
<Card
|
||||
variant="default"
|
||||
class="overflow-hidden"
|
||||
>
|
||||
<!-- 标题和筛选器 -->
|
||||
<div class="px-4 sm:px-6 py-3.5 border-b border-border/60">
|
||||
<!-- 移动端 -->
|
||||
@@ -39,10 +42,18 @@
|
||||
<SelectValue placeholder="状态" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部</SelectItem>
|
||||
<SelectItem value="online">在线</SelectItem>
|
||||
<SelectItem value="unhealthy">异常</SelectItem>
|
||||
<SelectItem value="offline">离线</SelectItem>
|
||||
<SelectItem value="all">
|
||||
全部
|
||||
</SelectItem>
|
||||
<SelectItem value="online">
|
||||
在线
|
||||
</SelectItem>
|
||||
<SelectItem value="unhealthy">
|
||||
异常
|
||||
</SelectItem>
|
||||
<SelectItem value="offline">
|
||||
离线
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -69,10 +80,18 @@
|
||||
<SelectValue placeholder="全部状态" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部状态</SelectItem>
|
||||
<SelectItem value="online">在线</SelectItem>
|
||||
<SelectItem value="unhealthy">异常</SelectItem>
|
||||
<SelectItem value="offline">离线</SelectItem>
|
||||
<SelectItem value="all">
|
||||
全部状态
|
||||
</SelectItem>
|
||||
<SelectItem value="online">
|
||||
在线
|
||||
</SelectItem>
|
||||
<SelectItem value="unhealthy">
|
||||
异常
|
||||
</SelectItem>
|
||||
<SelectItem value="offline">
|
||||
离线
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div class="h-4 w-px bg-border" />
|
||||
@@ -98,15 +117,33 @@
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow class="border-b border-border/60 hover:bg-transparent">
|
||||
<TableHead class="w-[160px] h-12 font-semibold">名称</TableHead>
|
||||
<TableHead class="w-[180px] h-12 font-semibold">地址</TableHead>
|
||||
<TableHead class="w-[100px] h-12 font-semibold">区域</TableHead>
|
||||
<TableHead class="w-[90px] 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">总请求</TableHead>
|
||||
<TableHead class="w-[100px] h-12 font-semibold text-center">延迟</TableHead>
|
||||
<TableHead class="w-[160px] h-12 font-semibold">最后心跳</TableHead>
|
||||
<TableHead class="w-[80px] h-12 font-semibold text-center">操作</TableHead>
|
||||
<TableHead class="w-[160px] h-12 font-semibold">
|
||||
名称
|
||||
</TableHead>
|
||||
<TableHead class="w-[180px] h-12 font-semibold">
|
||||
地址
|
||||
</TableHead>
|
||||
<TableHead class="w-[100px] h-12 font-semibold">
|
||||
区域
|
||||
</TableHead>
|
||||
<TableHead class="w-[90px] 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">
|
||||
总请求
|
||||
</TableHead>
|
||||
<TableHead class="w-[100px] h-12 font-semibold text-center">
|
||||
延迟
|
||||
</TableHead>
|
||||
<TableHead class="w-[160px] h-12 font-semibold">
|
||||
最后心跳
|
||||
</TableHead>
|
||||
<TableHead class="w-[80px] h-12 font-semibold text-center">
|
||||
操作
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -125,6 +162,7 @@
|
||||
>
|
||||
手动
|
||||
</Badge>
|
||||
<HardwareTooltip :node="node" />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="py-4">
|
||||
@@ -134,7 +172,10 @@
|
||||
<span class="text-sm text-muted-foreground">{{ node.region || '-' }}</span>
|
||||
</TableCell>
|
||||
<TableCell class="py-4 text-center">
|
||||
<Badge :variant="statusVariant(node.status)" class="font-medium px-2.5 py-0.5 text-xs">
|
||||
<Badge
|
||||
:variant="statusVariant(node.status)"
|
||||
class="font-medium px-2.5 py-0.5 text-xs"
|
||||
>
|
||||
{{ statusLabel(node.status) }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
@@ -160,8 +201,14 @@
|
||||
:disabled="testingNodes.has(node.id)"
|
||||
@click="handleTest(node)"
|
||||
>
|
||||
<Loader2 v-if="testingNodes.has(node.id)" class="h-4 w-4 animate-spin" />
|
||||
<Activity v-else class="h-4 w-4" />
|
||||
<Loader2
|
||||
v-if="testingNodes.has(node.id)"
|
||||
class="h-4 w-4 animate-spin"
|
||||
/>
|
||||
<Activity
|
||||
v-else
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-if="node.is_manual"
|
||||
@@ -196,7 +243,10 @@
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-if="paginatedNodes.length === 0">
|
||||
<TableCell colspan="9" class="py-12 text-center text-muted-foreground text-sm">
|
||||
<TableCell
|
||||
colspan="9"
|
||||
class="py-12 text-center text-muted-foreground text-sm"
|
||||
>
|
||||
{{ store.loading ? '加载中...' : '暂无代理节点' }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -222,10 +272,14 @@
|
||||
>
|
||||
手动
|
||||
</Badge>
|
||||
<HardwareTooltip :node="node" />
|
||||
</div>
|
||||
<code class="text-xs text-muted-foreground">{{ node.is_manual ? (node.proxy_url || `${node.ip}:${node.port}`) : `${node.ip}:${node.port}` }}</code>
|
||||
</div>
|
||||
<Badge :variant="statusVariant(node.status)" class="text-xs">
|
||||
<Badge
|
||||
:variant="statusVariant(node.status)"
|
||||
class="text-xs"
|
||||
>
|
||||
{{ statusLabel(node.status) }}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -253,8 +307,14 @@
|
||||
:disabled="testingNodes.has(node.id)"
|
||||
@click="handleTest(node)"
|
||||
>
|
||||
<Loader2 v-if="testingNodes.has(node.id)" class="h-3 w-3 mr-1 animate-spin" />
|
||||
<Activity v-else class="h-3 w-3 mr-1" />
|
||||
<Loader2
|
||||
v-if="testingNodes.has(node.id)"
|
||||
class="h-3 w-3 mr-1 animate-spin"
|
||||
/>
|
||||
<Activity
|
||||
v-else
|
||||
class="h-3 w-3 mr-1"
|
||||
/>
|
||||
{{ testingNodes.has(node.id) ? '测试中' : '测试' }}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -289,7 +349,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="paginatedNodes.length === 0" class="p-8 text-center text-muted-foreground text-sm">
|
||||
<div
|
||||
v-if="paginatedNodes.length === 0"
|
||||
class="p-8 text-center text-muted-foreground text-sm"
|
||||
>
|
||||
{{ store.loading ? '加载中...' : '暂无代理节点' }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -390,25 +453,40 @@
|
||||
size="md"
|
||||
@update:model-value="handleConfigDialogClose"
|
||||
>
|
||||
<form class="space-y-4" @submit.prevent>
|
||||
<form
|
||||
class="space-y-4"
|
||||
@submit.prevent
|
||||
>
|
||||
<div class="space-y-1.5">
|
||||
<Label>允许的端口</Label>
|
||||
<Input
|
||||
v-model="configForm.allowed_ports"
|
||||
placeholder="80, 443, 8080, 8443"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">逗号分隔的目标端口白名单</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
逗号分隔的目标端口白名单
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label>日志级别</Label>
|
||||
<Select v-model="configForm.log_level">
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="trace">trace</SelectItem>
|
||||
<SelectItem value="debug">debug</SelectItem>
|
||||
<SelectItem value="info">info</SelectItem>
|
||||
<SelectItem value="warn">warn</SelectItem>
|
||||
<SelectItem value="error">error</SelectItem>
|
||||
<SelectItem value="trace">
|
||||
trace
|
||||
</SelectItem>
|
||||
<SelectItem value="debug">
|
||||
debug
|
||||
</SelectItem>
|
||||
<SelectItem value="info">
|
||||
info
|
||||
</SelectItem>
|
||||
<SelectItem value="warn">
|
||||
warn
|
||||
</SelectItem>
|
||||
<SelectItem value="error">
|
||||
error
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -432,7 +510,10 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="configNode" class="text-xs text-muted-foreground">
|
||||
<div
|
||||
v-if="configNode"
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
配置版本: v{{ configNode.config_version }}
|
||||
</div>
|
||||
</form>
|
||||
@@ -484,6 +565,7 @@ import {
|
||||
} from '@/components/ui'
|
||||
|
||||
import { Search, Trash2, Plus, SquarePen, Activity, Loader2, Settings } from 'lucide-vue-next'
|
||||
import HardwareTooltip from './components/HardwareTooltip.vue'
|
||||
|
||||
const { success, error: toastError } = useToast()
|
||||
const { confirmDanger } = useConfirm()
|
||||
|
||||
51
frontend/src/views/admin/components/HardwareTooltip.vue
Normal file
51
frontend/src/views/admin/components/HardwareTooltip.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProxyNode } from '@/api/proxy-nodes'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui'
|
||||
import { Cpu } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{ node: ProxyNode }>()
|
||||
|
||||
function formatMemory(mb: number | null) {
|
||||
if (mb == null) return '-'
|
||||
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`
|
||||
return `${mb} MB`
|
||||
}
|
||||
|
||||
function formatNumber(n: number) {
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
|
||||
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
|
||||
return String(n)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider v-if="!node.is_manual && node.hardware_info">
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Cpu class="h-3.5 w-3.5 text-muted-foreground cursor-default" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="right"
|
||||
class="text-xs space-y-0.5"
|
||||
>
|
||||
<div v-if="node.hardware_info.cpu_cores">
|
||||
CPU: {{ node.hardware_info.cpu_cores }} cores
|
||||
</div>
|
||||
<div v-if="node.hardware_info.total_memory_mb">
|
||||
RAM: {{ formatMemory(node.hardware_info.total_memory_mb) }}
|
||||
</div>
|
||||
<div v-if="node.hardware_info.os_info">
|
||||
OS: {{ node.hardware_info.os_info }}
|
||||
</div>
|
||||
<div v-if="node.estimated_max_concurrency">
|
||||
Max Concurrency: ~{{ formatNumber(node.estimated_max_concurrency) }}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
Reference in New Issue
Block a user