瀏覽代碼

自动启动 mihomo 并获取 ping 值

mrh 1 年之前
父節點
當前提交
cbb35f7735
共有 4 個文件被更改,包括 88 次插入47 次删除
  1. 29 22
      backend/database/models/subscription.py
  2. 4 0
      backend/demo/client.py
  3. 54 24
      backend/routers/mihomo.py
  4. 1 1
      backend/routers/readme.md

+ 29 - 22
backend/database/models/subscription.py

@@ -22,14 +22,15 @@ class SubscriptFile(SQLModel, table=True):
 
 class MihomoMeta(SQLModel, table=True):
     id: Optional[int] = Field(default=None, primary_key=True)
-    provider_name: str
-    proxy_name: str
-    mixed_port: Optional[int]
-    external_controller: Optional[str]
-    temp_file_path: Optional[str]
-    pid: Optional[int]
+    provider_name: str = Field(default="")
+    proxy_name: str = Field(default="")
+    mixed_port: Optional[int] = Field(default=None)
+    external_controller: Optional[str] = Field(default=None)
+    temp_file_path: Optional[str] = Field(default=None)
+    pid: Optional[int] = Field(default=None)
     running: Optional[bool] = False
     updated_at: datetime = Field(default_factory=datetime.now)
+    delay: Optional[int] = Field(default=None)  # 新增延迟字段
 
     subscript_file_id: Optional[int] = Field(default=None, foreign_key="subscriptfile.id")
     subscript_file: SubscriptFile | None = Relationship(back_populates="mihomo_meta")
@@ -45,17 +46,17 @@ class SubscriptionManager:
                 logger.info(f"{sub_model.url} already exist, skip add")
                 return exist_sub
 
-            logger.info(f"exist_sub {exist_sub} overwrite {overwrite} proxies {proxies}")
-            if exist_sub and overwrite:
+            logger.info(f"exist_sub {exist_sub} overwrite {overwrite} proxies {len(proxies)}")
+            if overwrite:
                 # 删除与 exist_sub 相关的 MihomoMeta 记录
                 session.exec(select(MihomoMeta).where(MihomoMeta.subscript_file_id == exist_sub.id)).all()
                 for proxy in session.exec(select(MihomoMeta).where(MihomoMeta.subscript_file_id == exist_sub.id)).all():
                     session.delete(proxy)
             
-            # 删除旧的 SubscriptFile 记录
-            session.delete(exist_sub)
-            session.commit()
-
+                # 删除旧的 SubscriptFile 记录
+                session.delete(exist_sub)
+                session.commit()
+        logger.info(f"add {sub_model}")
         # 添加新的 SubscriptFile
         session.add(sub_model)
         session.commit()
@@ -90,6 +91,22 @@ class SubscriptionManager:
                 .where(MihomoMeta.pid.is_not(None))
             ).all()
             return all
+            
+    def update_proxy_delays(self, provider_name: str, delays: dict):
+        """更新指定服务商下所有代理的延迟"""
+        with Session(self.engine) as session:
+            proxies = session.exec(
+                select(MihomoMeta)
+                .where(MihomoMeta.provider_name == provider_name)
+            ).all()
+            
+            for proxy in proxies:
+                if proxy.proxy_name in delays:
+                    proxy.delay = delays[proxy.proxy_name]
+                    session.add(proxy)
+            
+            session.commit()
+
     def get_each_provider_proxies(self):
         with Session(self.engine) as session:
             # 子查询:获取每个 provider_name 的最小 id
@@ -163,13 +180,3 @@ class SubscriptionManager:
             if not any(keyword in proxy.get("name", "") for keyword in keywords):
                 fileter_proxies.append(proxy)
         return name, groups,proxies
-            # ret.append(
-        #     SubscriptionResponse(
-        #         file_name=Path(sub.file_path).name,
-        #         provider_name=name,
-        #         updated_at=sub.updated_at,
-        #         proxies=sub_yaml.get("proxies", []),
-
-        #     )
-        # )
-        # return ret

+ 4 - 0
backend/demo/client.py

@@ -21,6 +21,10 @@ def startup():
     return response.json()
 
 def proxy_delay(provider_name, external_controller: str):
+    '''
+    失败: {'message': 'get delay: all proxies timeout'}
+    成功: {'自动选择': 34, '🇦🇺澳大利亚悉尼': 159, '🇦🇺澳大利亚悉尼2': 1577,...}
+    '''
     # http://127.0.0.1:9351/group/FSCloud/delay?url=https%3A%2F%2Fwww.gstatic.com%2Fgenerate_204&timeout=2000
     url = f"http://{external_controller}/group/{provider_name}/delay?url=https%3A%2F%2Fwww.gstatic.com%2Fgenerate_204&timeout=2000"
     response = httpx.get(url,timeout=30)

+ 54 - 24
backend/routers/mihomo.py

@@ -21,11 +21,10 @@ from utils.mihomo_service import port_is_using,find_free_port
 from utils.sub import update_config
 from utils.processes_mgr import process_manager
 from database.models.subscription import SubscriptionManager,SubscriptFile,MihomoMeta
-# 初始化全局变量来保存进程池
+
 POOL = None
 mihomo_router = APIRouter()
 
-
 class MihomoBatchRequest(BaseModel):
     id: int
     port: Optional[int] = None
@@ -42,8 +41,7 @@ class MihomoResponse(MihomoBatchRequest):
     detail: Optional[Dict] = None
 
 class MihomoMetaWithURL(MihomoMeta, table=False):
-    # 覆盖导致问题的关系字段
-    subscript_file: Optional[int] = None  # 使用外键类型替代关系对象
+    subscript_file: Optional[int] = None
     external_controller_url: Optional[str] = None
 
     class Config:
@@ -57,20 +55,30 @@ async def request_select_proxy_name(external_ctl: str, provider_name: str, proxy
         for attempt in range(max_retries):
             try:
                 response = await client.put(url, json=payload)
-                response.raise_for_status()  # 如果请求失败,抛出异常
-                return response  # 该接口没有返回值,直接返回response
+                response.raise_for_status()
+                return response
             except (httpx.HTTPError, httpx.RequestError) as e:
                 if attempt < max_retries - 1:
-                    await asyncio.sleep(delay)  # 等待一段时间后重试
+                    await asyncio.sleep(delay)
                 else:
                     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:
+    """异步获取代理延迟"""
+    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:
+            response = await client.get(url, timeout=30)
+            response.raise_for_status()
+            return response.json()
+        except Exception as e:
+            return {"error": str(e)}
+
 @mihomo_router.post("/start")
 async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
     db = SubscriptionManager()
     logger.info(f"{request}")
-    # 获取对应的订阅文件
     with Session(db.engine) as session:
-        # 查找对应的订阅文件
         miho_model = session.exec(
             select(MihomoMeta)
             .where(MihomoMeta.id == request.id)
@@ -79,19 +87,15 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
         if not miho_model:
             raise HTTPException(status_code=404, detail="Provider not found")
         sub_file = miho_model.subscript_file
-        # logger.info(f"miho_model.subscript_file {miho_model.subscript_file}")    
-        # return miho_model
+            
         if miho_model.pid:
             return miho_model
         mixed_port = request.port
-        # 如果端口未指定,查找可用端口
         if not mixed_port:
             mixed_port = find_free_port()
             external_controller_port = find_free_port((mixed_port+1, 18000))
         config = {}
-        # 保存临时配置文件
         temp_path = settings.MIHOMO_TEMP_PATH / f"{miho_model.provider_name}_{external_controller_port}.yaml"
-        # 更新端口配置
         config['mixed-port'] = mixed_port
         config['external-controller'] = f'127.0.0.1:{external_controller_port}'
         config['bind-address'] = '127.0.0.1'
@@ -100,7 +104,6 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
         logger.info(f"config {config}")
         res = update_config(Path(sub_file.file_path), config, Path(temp_path))
         
-        # 启动进程
         try:
             command = [str(settings.MIHOMO_BIN_PATH), "-f", str(temp_path)]
             logger.info(f"Executing command: {' '.join(command)}")
@@ -119,7 +122,6 @@ async def post_start_mihomo(request: MihomoBatchRequest) -> MihomoMetaWithURL:
                 logger.error(f"Failed to select proxy: {str(e)}")
                 process_manager.stop_process(external_controller_port)
                 raise HTTPException(status_code=500, detail=str(e))
-            # 更新数据库记录
             
             session.add(miho_model)
             session.commit()
@@ -147,6 +149,7 @@ async def post_start_each_provider() -> List[MihomoMetaWithURL]:
             logger.exception(f"Failed to start mihomo: {str(e)}")
             raise HTTPException(status_code=500, detail=str(e))
     return ret
+
 @mihomo_router.post("/stop")
 async def post_stop_mihomo(request: MihomoBatchRequest):
     db = SubscriptionManager()
@@ -188,19 +191,47 @@ async def get_mihomo_running_status():
     
     return result
 
-@mihomo_router.get("/proxies_reachability")
+@mihomo_router.post("/proxies_reachability")
 async def get_proxies_reachability():
+    # 1. 启动所有服务商
+    await post_start_each_provider()
+    
+    # 2. 获取所有运行中的代理
     db = SubscriptionManager()
-    res = db.get_each_provider_running_proxies()
-    logger.info(f"get_each_provider_running_proxies {res}")
-
-
+    running_proxies = db.get_running_proxies()
+    
+    # 3. 测试延迟并更新数据库
+    results = []
+    for proxy in running_proxies:
+        if not proxy.external_controller:
+            continue
+            
+        try:
+            # 获取延迟数据
+            delay_data = await async_proxy_delay(proxy.provider_name, proxy.external_controller)
+            
+            db.update_proxy_delays(
+                provider_name=proxy.provider_name,
+                delays=delay_data
+            )
+            
+            # 收集结果
+            results.append({
+                "provider": proxy.provider_name,
+                "delays": delay_data
+            })
+        except Exception as e:
+            logger.error(f"Failed to update delays for {proxy.provider_name}: {str(e)}")
+            results.append({
+                "provider": proxy.provider_name,
+                "error": str(e)
+            })
+    
+    return results
 
 @mihomo_router.get("/external-controller")
 async def get_controller_urls():
     running_list = await get_mihomo_running_status()
-    logger.info(f"running_list {running_list}")
-    # https://yacd.metacubex.one/?hostname=127.0.0.1&port=9351&secret=#/proxies
     urls = []
     for item in running_list:
         host, port = item.external_controller.split(":")
@@ -213,4 +244,3 @@ async def stop_all_mihomo():
         if item.pid:
             logger.info(f"stop mihomo {item}")
             await post_stop_mihomo(MihomoBatchRequest(id=item.id))
-    # logger.info(f"running_list {running_list}")

+ 1 - 1
backend/routers/readme.md

@@ -7,7 +7,7 @@ POST /subscriptions
     "https://www.yfjc.xyz/api/v1/client/subscribe?token=b74f2207492053926f7511a8e474048f",
     "http://subscr.xpoti.com/v3/subscr?id=90bd6ff1da374a89b4ba763f354f20bf"
   ],
-  "overwrite": true,
+  "overwrite": true
 }
 
 Response: