feat(proxy): 重构 Proxy 节点管理与隧道系统

- 重构 proxy_nodes 管理端,支持节点注册、心跳、隧道生命周期管理
- 增强 tunnel 嵌入式 hub 和隧道协议
- 重构 aether-proxy 配置、隧道客户端、心跳和调度机制
- 调整 admin OAuth/配额/导入等处理器的参数传递
- 扩展数据迁移模块
- 补充 proxy nodes、OAuth、配额、系统导入等测试
- 更新前端 proxy nodes 视图和 API
This commit is contained in:
fawney19
2026-04-14 22:51:02 +08:00
parent fb31928e44
commit a4e7ac1df6
55 changed files with 3477 additions and 892 deletions
+4 -15
View File
@@ -172,7 +172,7 @@ impl App {
value: DEFAULT_HEARTBEAT_INTERVAL_SECS.to_string(),
kind: FieldKind::Text,
required: false,
help: "Heartbeat interval in seconds; default is 30",
help: "Heartbeat interval in seconds; default is 5",
},
Field {
label: "Redirect Replay Budget",
@@ -264,22 +264,11 @@ impl App {
}
// Server tabs
let servers = cfg.effective_servers();
let servers = cfg.servers.clone();
if servers.is_empty() {
let mut tab = ServerTab::new();
// Single-server fallback: use top-level node_name
if let Some(ref name) = cfg.node_name {
tab.fields[2].value = name.clone();
}
self.server_tabs = vec![tab];
self.server_tabs = vec![ServerTab::new()];
} else {
self.server_tabs = servers.iter().map(ServerTab::from_entry).collect();
// For single-server mode, node_name might be in top-level only
if self.server_tabs.len() == 1 && self.server_tabs[0].fields[2].value.is_empty() {
if let Some(ref name) = cfg.node_name {
self.server_tabs[0].fields[2].value = name.clone();
}
}
}
self.active_tab = 0;
self.selected = 0;
@@ -387,7 +376,7 @@ impl App {
..ConfigFile::default()
};
// Always write [[servers]] format; old top-level fields are read-only compat
// Always write [[servers]] format.
cfg.servers = self
.server_tabs
.iter()