mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30: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:
@@ -914,7 +914,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
ctx.selected_base_url = envelope.capture_selected_base_url() if envelope else None
|
||||
|
||||
# 记录代理信息
|
||||
from src.clients.http_client import get_proxy_label, resolve_proxy_info
|
||||
from src.services.proxy_node.resolver import get_proxy_label, resolve_proxy_info
|
||||
|
||||
ctx.proxy_info = resolve_proxy_info(provider.proxy)
|
||||
proxy_label = get_proxy_label(ctx.proxy_info)
|
||||
@@ -928,19 +928,23 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
# simulate streaming to the client (sync -> stream bridge).
|
||||
if not upstream_is_stream:
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import build_post_kwargs, resolve_delegate_config
|
||||
|
||||
request_timeout_sync = provider.request_timeout or config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = await HTTPClientPool.get_upstream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy
|
||||
)
|
||||
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout_sync),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout_sync,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.TimeoutException) as e:
|
||||
if envelope:
|
||||
envelope.on_connection_error(base_url=ctx.selected_base_url, exc=e)
|
||||
@@ -971,12 +975,15 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
ctx.provider_request_headers = provider_headers
|
||||
|
||||
# retry once
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout_sync),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout_sync,
|
||||
refresh_auth=True,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
ctx.status_code = resp.status_code
|
||||
ctx.response_headers = dict(resp.headers)
|
||||
if envelope:
|
||||
@@ -1120,10 +1127,11 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
|
||||
# 创建 HTTP 客户端(支持代理配置,从 Provider 读取)
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import build_stream_kwargs, resolve_delegate_config
|
||||
|
||||
http_client = HTTPClientPool.create_client_with_proxy(
|
||||
proxy_config=provider.proxy,
|
||||
timeout=timeout_config,
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = HTTPClientPool.create_upstream_stream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy, timeout=timeout_config
|
||||
)
|
||||
|
||||
# 用于存储内部函数的结果(必须在函数定义前声明,供 nonlocal 使用)
|
||||
@@ -1134,9 +1142,18 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
async def _connect_and_prefetch() -> None:
|
||||
"""建立连接并预读首字节(受整体超时控制)"""
|
||||
nonlocal byte_iterator, prefetched_chunks, response_ctx
|
||||
response_ctx = http_client.stream(
|
||||
"POST", url, json=provider_payload, headers=provider_headers
|
||||
_skw = build_stream_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
payload=provider_payload,
|
||||
timeout=(
|
||||
provider.request_timeout or config.http_request_timeout
|
||||
if delegate_cfg
|
||||
else None
|
||||
),
|
||||
)
|
||||
response_ctx = http_client.stream(**_skw)
|
||||
stream_response = await response_ctx.__aenter__()
|
||||
|
||||
ctx.status_code = stream_response.status_code
|
||||
@@ -1547,7 +1564,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
selected_base_url_cached = envelope.capture_selected_base_url() if envelope else None
|
||||
|
||||
# 记录代理信息
|
||||
from src.clients.http_client import get_proxy_label, resolve_proxy_info
|
||||
from src.services.proxy_node.resolver import get_proxy_label, resolve_proxy_info
|
||||
|
||||
sync_proxy_info = resolve_proxy_info(provider.proxy)
|
||||
_proxy_label = get_proxy_label(sync_proxy_info)
|
||||
@@ -1562,12 +1579,19 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
# 获取复用的 HTTP 客户端(支持代理配置,从 Provider 读取)
|
||||
# 注意:使用 get_proxy_client 复用连接池,不再每次创建新客户端
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import (
|
||||
build_post_kwargs,
|
||||
build_stream_kwargs,
|
||||
resolve_delegate_config,
|
||||
)
|
||||
|
||||
# 非流式请求使用 http_request_timeout 作为整体超时
|
||||
# 优先使用 Provider 配置,否则使用全局配置
|
||||
request_timeout = provider.request_timeout or config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = await HTTPClientPool.get_upstream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy
|
||||
)
|
||||
|
||||
# 注意:不使用 async with,因为复用的客户端不应该被关闭
|
||||
@@ -1575,12 +1599,14 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
resp: httpx.Response | None = None
|
||||
if not upstream_is_stream:
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_hdrs,
|
||||
timeout=httpx.Timeout(request_timeout),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.TimeoutException) as e:
|
||||
if envelope:
|
||||
envelope.on_connection_error(base_url=selected_base_url_cached, exc=e)
|
||||
@@ -1596,13 +1622,14 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
)
|
||||
|
||||
try:
|
||||
async with http_client.stream(
|
||||
"POST",
|
||||
url,
|
||||
json=provider_payload,
|
||||
_stream_args = build_stream_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_hdrs,
|
||||
timeout=httpx.Timeout(request_timeout),
|
||||
) as stream_resp:
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout,
|
||||
)
|
||||
async with http_client.stream(**_stream_args) as stream_resp:
|
||||
resp = stream_resp
|
||||
|
||||
status_code = stream_resp.status_code
|
||||
|
||||
@@ -891,8 +891,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
ctx.selected_base_url = envelope.capture_selected_base_url() if envelope else None
|
||||
|
||||
# 记录代理信息(sync-bridge 路径,早于流式路径执行)
|
||||
from src.clients.http_client import get_proxy_label as _gpl
|
||||
from src.clients.http_client import resolve_proxy_info as _rpi
|
||||
from src.services.proxy_node.resolver import get_proxy_label as _gpl
|
||||
from src.services.proxy_node.resolver import resolve_proxy_info as _rpi
|
||||
|
||||
ctx.proxy_info = _rpi(provider.proxy)
|
||||
|
||||
@@ -900,19 +900,23 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
# simulate streaming to the client (sync -> stream bridge).
|
||||
if not upstream_is_stream:
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import build_post_kwargs, resolve_delegate_config
|
||||
|
||||
request_timeout_sync = provider.request_timeout or config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = await HTTPClientPool.get_upstream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy
|
||||
)
|
||||
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout_sync),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout_sync,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.TimeoutException) as e:
|
||||
if envelope:
|
||||
envelope.on_connection_error(base_url=ctx.selected_base_url, exc=e)
|
||||
@@ -943,12 +947,15 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
ctx.provider_request_headers = provider_headers
|
||||
|
||||
# retry once
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout_sync),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout_sync,
|
||||
refresh_auth=True,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
ctx.status_code = resp.status_code
|
||||
ctx.response_headers = dict(resp.headers)
|
||||
if envelope:
|
||||
@@ -1091,10 +1098,11 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
|
||||
# 创建 HTTP 客户端(支持代理配置,从 Provider 读取)
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import build_stream_kwargs, resolve_delegate_config
|
||||
|
||||
http_client = HTTPClientPool.create_client_with_proxy(
|
||||
proxy_config=provider.proxy,
|
||||
timeout=timeout_config,
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = HTTPClientPool.create_upstream_stream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy, timeout=timeout_config
|
||||
)
|
||||
|
||||
# 用于存储内部函数的结果(必须在函数定义前声明,供 nonlocal 使用)
|
||||
@@ -1105,9 +1113,18 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
async def _connect_and_prefetch() -> None:
|
||||
"""建立连接并预读首字节(受整体超时控制)"""
|
||||
nonlocal byte_iterator, prefetched_chunks, response_ctx
|
||||
response_ctx = http_client.stream(
|
||||
"POST", url, json=provider_payload, headers=provider_headers
|
||||
_skw = build_stream_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
payload=provider_payload,
|
||||
timeout=(
|
||||
provider.request_timeout or config.http_request_timeout
|
||||
if delegate_cfg
|
||||
else None
|
||||
),
|
||||
)
|
||||
response_ctx = http_client.stream(**_skw)
|
||||
stream_response = await response_ctx.__aenter__()
|
||||
|
||||
ctx.status_code = stream_response.status_code
|
||||
@@ -2962,7 +2979,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
selected_base_url_cached = envelope.capture_selected_base_url() if envelope else None
|
||||
|
||||
# 记录代理信息
|
||||
from src.clients.http_client import get_proxy_label, resolve_proxy_info
|
||||
from src.services.proxy_node.resolver import get_proxy_label, resolve_proxy_info
|
||||
|
||||
sync_proxy_info = resolve_proxy_info(provider.proxy)
|
||||
_proxy_label = get_proxy_label(sync_proxy_info)
|
||||
@@ -2978,12 +2995,19 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
# 获取复用的 HTTP 客户端(支持代理配置,从 Provider 读取)
|
||||
# 注意:使用 get_proxy_client 复用连接池,不再每次创建新客户端
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.services.proxy_node.resolver import (
|
||||
build_post_kwargs,
|
||||
build_stream_kwargs,
|
||||
resolve_delegate_config,
|
||||
)
|
||||
|
||||
# 非流式请求使用 http_request_timeout 作为整体超时
|
||||
# 优先使用 Provider 配置,否则使用全局配置
|
||||
request_timeout = provider.request_timeout or config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
|
||||
delegate_cfg = resolve_delegate_config(provider.proxy)
|
||||
http_client = await HTTPClientPool.get_upstream_client(
|
||||
delegate_cfg, proxy_config=provider.proxy
|
||||
)
|
||||
|
||||
# 注意:不使用 async with,因为复用的客户端不应该被关闭
|
||||
@@ -2991,12 +3015,14 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
resp: httpx.Response | None = None
|
||||
if not upstream_is_stream:
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
json=provider_payload,
|
||||
_pkw = build_post_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout),
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout,
|
||||
)
|
||||
resp = await http_client.post(**_pkw)
|
||||
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.TimeoutException) as e:
|
||||
if envelope:
|
||||
envelope.on_connection_error(base_url=selected_base_url_cached, exc=e)
|
||||
@@ -3013,13 +3039,14 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
)
|
||||
|
||||
try:
|
||||
async with http_client.stream(
|
||||
"POST",
|
||||
url,
|
||||
json=provider_payload,
|
||||
_stream_args = build_stream_kwargs(
|
||||
delegate_cfg,
|
||||
url=url,
|
||||
headers=provider_headers,
|
||||
timeout=httpx.Timeout(request_timeout),
|
||||
) as stream_resp:
|
||||
payload=provider_payload,
|
||||
timeout=request_timeout,
|
||||
)
|
||||
async with http_client.stream(**_stream_args) as stream_resp:
|
||||
resp = stream_resp
|
||||
|
||||
status_code = stream_resp.status_code
|
||||
|
||||
Reference in New Issue
Block a user