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

View File

@@ -92,26 +92,21 @@ impl AppState {
&self,
transport: &GatewayProviderTransportSnapshot,
) -> Option<ProxySnapshot> {
if let Some(snapshot) = self
.resolve_proxy_snapshot_from_config(transport.key.proxy.as_ref())
.await
{
return Some(snapshot);
}
if let Some(snapshot) = self
.resolve_proxy_snapshot_from_config(transport.provider.proxy.as_ref())
.await
{
return Some(snapshot);
}
if let Some(snapshot) = self.resolve_system_proxy_snapshot().await {
return Some(snapshot);
}
self.resolve_proxy_snapshot_from_config(transport.endpoint.proxy.as_ref())
self.resolve_transport_proxy_with_source_with_tunnel_affinity(transport)
.await
.map(|(snapshot, _)| snapshot)
}
async fn resolve_proxy_snapshot_from_config(
pub(crate) async fn resolve_transport_proxy_source_with_tunnel_affinity(
&self,
transport: &GatewayProviderTransportSnapshot,
) -> Option<&'static str> {
self.resolve_transport_proxy_with_source_with_tunnel_affinity(transport)
.await
.map(|(_, source)| source)
}
pub(crate) async fn resolve_configured_proxy_snapshot_with_tunnel_affinity(
&self,
raw: Option<&Value>,
) -> Option<ProxySnapshot> {
@@ -127,6 +122,37 @@ impl AppState {
proxy_snapshot_from_object(object)
}
async fn resolve_transport_proxy_with_source_with_tunnel_affinity(
&self,
transport: &GatewayProviderTransportSnapshot,
) -> Option<(ProxySnapshot, &'static str)> {
if let Some(snapshot) = self
.resolve_configured_proxy_snapshot_with_tunnel_affinity(transport.key.proxy.as_ref())
.await
{
return Some((snapshot, "key"));
}
if let Some(snapshot) = self
.resolve_configured_proxy_snapshot_with_tunnel_affinity(
transport.endpoint.proxy.as_ref(),
)
.await
{
return Some((snapshot, "endpoint"));
}
if let Some(snapshot) = self
.resolve_configured_proxy_snapshot_with_tunnel_affinity(
transport.provider.proxy.as_ref(),
)
.await
{
return Some((snapshot, "provider"));
}
self.resolve_system_proxy_snapshot()
.await
.map(|snapshot| (snapshot, "system"))
}
}
fn proxy_enabled(object: &Map<String, Value>) -> bool {
@@ -206,12 +232,22 @@ fn proxy_url_with_node_auth(
if parsed.set_username(username).is_err() {
return None;
}
let password = password
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or_default();
if parsed.set_password(Some(password)).is_err() {
let password = password.map(str::trim).filter(|value| !value.is_empty());
if parsed.set_password(password).is_err() {
return None;
}
Some(parsed.to_string())
}
#[cfg(test)]
mod tests {
use super::proxy_url_with_node_auth;
#[test]
fn proxy_url_with_node_auth_omits_empty_password_separator() {
assert_eq!(
proxy_url_with_node_auth("socks5://proxy.example:1080", Some("alice"), None).as_deref(),
Some("socks5://alice@proxy.example:1080")
);
}
}