|
|
@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
|
|
|
from pydantic import BaseModel
|
|
|
from typing import Dict, List, Optional, Union
|
|
|
import httpx
|
|
|
-from sqlmodel import Session, select, or_,func
|
|
|
+from sqlmodel import Session, select, or_,func, update
|
|
|
import yaml
|
|
|
import signal
|
|
|
from asyncio import subprocess
|
|
|
@@ -43,9 +43,16 @@ class MihomoResponse(MihomoBatchRequest):
|
|
|
class MihomoMetaWithURL(MihomoMeta, table=False):
|
|
|
subscript_file: Optional[int] = None
|
|
|
external_controller_url: Optional[str] = None
|
|
|
-
|
|
|
+ error: Optional[str] = None # 新增错误信息字段
|
|
|
+ detail: Optional[dict] = None
|
|
|
class Config:
|
|
|
arbitrary_types_allowed = True
|
|
|
+
|
|
|
+class ProxiesReachabilityResponse(BaseModel):
|
|
|
+ provider_name: str
|
|
|
+ delays: Optional[Dict[str, int]] = None
|
|
|
+ error: Optional[str] = None
|
|
|
+ external_controller_url: Optional[str] = None
|
|
|
|
|
|
async def request_select_proxy_name(external_ctl: str, provider_name: str, proxy_name: str, max_retries: int = 5, delay: float = 2.0) -> Optional[dict]:
|
|
|
url = f"http://{external_ctl}/proxies/{provider_name}"
|
|
|
@@ -64,7 +71,10 @@ async def request_select_proxy_name(external_ctl: str, provider_name: str, proxy
|
|
|
raise HTTPException(status_code=500, detail=f"Failed to select proxy after {max_retries} attempts: {str(e)}")
|
|
|
|
|
|
async def async_proxy_delay(provider_name: str, external_controller: str) -> dict:
|
|
|
- """异步获取代理延迟"""
|
|
|
+ """异步获取代理延迟
|
|
|
+ 失败: {'message': 'get delay: all proxies timeout'}
|
|
|
+ 成功: {'自动选择': 34, '🇦🇺澳大利亚悉尼': 159, '🇦🇺澳大利亚悉尼2': 1577,...}
|
|
|
+ """
|
|
|
url = f"http://{external_controller}/group/{provider_name}/delay?url=https%3A%2F%2Fwww.gstatic.com%2Fgenerate_204&timeout=2000"
|
|
|
async with httpx.AsyncClient() as client:
|
|
|
try:
|
|
|
@@ -75,7 +85,7 @@ async def async_proxy_delay(provider_name: str, external_controller: str) -> dic
|
|
|
return {"error": str(e)}
|
|
|
|
|
|
@mihomo_router.post("/start")
|
|
|
-async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
|
|
|
+async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL :
|
|
|
db = SubscriptionManager()
|
|
|
logger.info(f"{request}")
|
|
|
with Session(db.engine) as session:
|
|
|
@@ -85,7 +95,7 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
|
|
|
).first()
|
|
|
|
|
|
if not miho_model:
|
|
|
- raise HTTPException(status_code=404, detail="Provider not found")
|
|
|
+ return MihomoMetaWithURL(error=1, detail="mihomo not found")
|
|
|
sub_file = miho_model.subscript_file
|
|
|
|
|
|
if miho_model.pid:
|
|
|
@@ -121,7 +131,7 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
|
|
|
except Exception as e:
|
|
|
logger.error(f"Failed to select proxy: {str(e)}")
|
|
|
process_manager.stop_process(external_controller_port)
|
|
|
- raise HTTPException(status_code=500, detail=str(e))
|
|
|
+ return MihomoMetaWithURL(error=1, detail=str(e))
|
|
|
|
|
|
session.add(miho_model)
|
|
|
session.commit()
|
|
|
@@ -134,7 +144,7 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.exception(f"Failed to start mihomo: {str(e)}")
|
|
|
- raise HTTPException(status_code=500, detail=str(e))
|
|
|
+ return MihomoMetaWithURL(error=1, detail=str(e))
|
|
|
|
|
|
@mihomo_router.post("/startup")
|
|
|
async def post_start_each_provider():
|
|
|
@@ -198,7 +208,7 @@ async def post_stop_mihomo(request: MihomoBatchRequest):
|
|
|
raise HTTPException(status_code=400, detail="Provider is not running")
|
|
|
|
|
|
@mihomo_router.get("/")
|
|
|
-async def get_mihomo_running_status():
|
|
|
+async def get_mihomo_running_status() -> List[MihomoMetaWithURL]:
|
|
|
db = SubscriptionManager()
|
|
|
all = db.get_running_proxies()
|
|
|
result = []
|
|
|
@@ -211,10 +221,19 @@ async def get_mihomo_running_status():
|
|
|
|
|
|
return result
|
|
|
|
|
|
-@mihomo_router.post("/proxies_reachability")
|
|
|
+@mihomo_router.post("/proxies_reachability", response_model=List[ProxiesReachabilityResponse])
|
|
|
async def get_proxies_reachability():
|
|
|
- # 获取所有运行中的代理
|
|
|
db = SubscriptionManager()
|
|
|
+
|
|
|
+ # 清空所有代理的延迟值
|
|
|
+ with Session(db.engine) as session:
|
|
|
+ session.exec(
|
|
|
+ update(MihomoMeta)
|
|
|
+ .values(delay=None)
|
|
|
+ )
|
|
|
+ session.commit()
|
|
|
+
|
|
|
+ # 获取所有运行中的代理
|
|
|
running_proxies = db.get_each_provider_running_proxies()
|
|
|
|
|
|
# 过滤掉没有 external_controller 的代理
|
|
|
@@ -232,23 +251,24 @@ async def get_proxies_reachability():
|
|
|
# 处理结果并更新数据库
|
|
|
results = []
|
|
|
for proxy, delay_result in zip(valid_proxies, delay_results):
|
|
|
+ response = ProxiesReachabilityResponse(
|
|
|
+ provider_name=proxy.provider_name,
|
|
|
+ external_controller_url=f"https://yacd.metacubex.one/?hostname=127.0.0.1&port={proxy.external_controller.split(':')[1]}&secret=#/proxies"
|
|
|
+ )
|
|
|
+
|
|
|
if isinstance(delay_result, Exception):
|
|
|
# 处理错误情况
|
|
|
logger.error(f"Failed to update delays for {proxy.provider_name}: {str(delay_result)}")
|
|
|
- results.append({
|
|
|
- "provider": proxy.provider_name,
|
|
|
- "error": str(delay_result)
|
|
|
- })
|
|
|
+ response.error = str(delay_result)
|
|
|
else:
|
|
|
# 更新数据库并收集结果
|
|
|
db.update_proxy_delays(
|
|
|
provider_name=proxy.provider_name,
|
|
|
delays=delay_result
|
|
|
)
|
|
|
- results.append({
|
|
|
- "provider": proxy.provider_name,
|
|
|
- "delays": delay_result
|
|
|
- })
|
|
|
+ response.delays = delay_result
|
|
|
+
|
|
|
+ results.append(response)
|
|
|
|
|
|
return results
|
|
|
|