feat(proxy): 添加动态代理支持

- 在代理获取逻辑中集成动态代理 API 调用
- 新增动态代理配置界面和 API 接口
- 扩展设置模型以支持动态代理参数
- 更新前端设置页面和 JavaScript 逻辑
This commit is contained in:
cnlimiter
2026-03-16 02:06:21 +08:00
parent 9dbb6e4e26
commit 97a8c01b9f
5 changed files with 242 additions and 5 deletions

View File

@@ -37,7 +37,8 @@ def get_proxy_for_registration(db) -> Tuple[Optional[str], Optional[int]]:
策略:
1. 优先从代理列表中随机选择一个启用的代理
2. 如果代理列表为空,使用系统设置中的默认代理
2. 如果代理列表为空且启用了动态代理,调用动态代理 API 获取
3. 否则使用系统设置中的静态默认代理
Returns:
Tuple[proxy_url, proxy_id]: 代理 URL 和代理 ID如果来自代理列表
@@ -47,10 +48,11 @@ def get_proxy_for_registration(db) -> Tuple[Optional[str], Optional[int]]:
if proxy:
return proxy.proxy_url, proxy.id
# 代理列表为空,使用系统设置中的默认代理
settings = get_settings()
if settings.proxy_enabled and settings.proxy_url:
return settings.proxy_url, None
# 代理列表为空,尝试动态代理或静态代理
from ...core.dynamic_proxy import get_proxy_url_for_task
proxy_url = get_proxy_url_for_task()
if proxy_url:
return proxy_url, None
return None, None

View File

@@ -79,6 +79,11 @@ async def get_all_settings():
"port": settings.proxy_port,
"username": settings.proxy_username,
"has_password": bool(settings.proxy_password),
"dynamic_enabled": settings.proxy_dynamic_enabled,
"dynamic_api_url": settings.proxy_dynamic_api_url,
"dynamic_api_key_header": settings.proxy_dynamic_api_key_header,
"dynamic_result_field": settings.proxy_dynamic_result_field,
"has_dynamic_api_key": bool(settings.proxy_dynamic_api_key and settings.proxy_dynamic_api_key.get_secret_value()),
},
"registration": {
"max_retries": settings.registration_max_retries,
@@ -199,6 +204,91 @@ async def test_proxy_settings(request: ProxySettings):
}
@router.get("/proxy/dynamic")
async def get_dynamic_proxy_settings():
"""获取动态代理设置"""
settings = get_settings()
return {
"enabled": settings.proxy_dynamic_enabled,
"api_url": settings.proxy_dynamic_api_url,
"api_key_header": settings.proxy_dynamic_api_key_header,
"result_field": settings.proxy_dynamic_result_field,
"has_api_key": bool(settings.proxy_dynamic_api_key and settings.proxy_dynamic_api_key.get_secret_value()),
}
class DynamicProxySettings(BaseModel):
"""动态代理设置"""
enabled: bool = False
api_url: str = ""
api_key: Optional[str] = None
api_key_header: str = "X-API-Key"
result_field: str = ""
@router.post("/proxy/dynamic")
async def update_dynamic_proxy_settings(request: DynamicProxySettings):
"""更新动态代理设置"""
update_dict = {
"proxy_dynamic_enabled": request.enabled,
"proxy_dynamic_api_url": request.api_url,
"proxy_dynamic_api_key_header": request.api_key_header,
"proxy_dynamic_result_field": request.result_field,
}
if request.api_key is not None:
update_dict["proxy_dynamic_api_key"] = request.api_key
update_settings(**update_dict)
return {"success": True, "message": "动态代理设置已更新"}
@router.post("/proxy/dynamic/test")
async def test_dynamic_proxy(request: DynamicProxySettings):
"""测试动态代理 API"""
from ...core.dynamic_proxy import fetch_dynamic_proxy
if not request.api_url:
raise HTTPException(status_code=400, detail="请填写动态代理 API 地址")
# 若未传入 api_key使用已保存的
api_key = request.api_key or ""
if not api_key:
settings = get_settings()
if settings.proxy_dynamic_api_key:
api_key = settings.proxy_dynamic_api_key.get_secret_value()
proxy_url = fetch_dynamic_proxy(
api_url=request.api_url,
api_key=api_key,
api_key_header=request.api_key_header,
result_field=request.result_field,
)
if not proxy_url:
return {"success": False, "message": "动态代理 API 返回为空或请求失败"}
# 用获取到的代理测试连通性
import time
from curl_cffi import requests as cffi_requests
try:
proxies = {"http": proxy_url, "https": proxy_url}
start = time.time()
resp = cffi_requests.get(
"https://api.ipify.org?format=json",
proxies=proxies,
timeout=10,
impersonate="chrome110"
)
elapsed = round((time.time() - start) * 1000)
if resp.status_code == 200:
ip = resp.json().get("ip", "")
return {"success": True, "proxy_url": proxy_url, "ip": ip, "response_time": elapsed,
"message": f"动态代理可用,出口 IP: {ip},响应时间: {elapsed}ms"}
return {"success": False, "proxy_url": proxy_url, "message": f"代理连接失败: HTTP {resp.status_code}"}
except Exception as e:
return {"success": False, "proxy_url": proxy_url, "message": f"代理连接失败: {e}"}
@router.get("/registration")
async def get_registration_settings():
"""获取注册设置"""