proxy.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import asyncio
  2. from copy import deepcopy
  3. import hashlib
  4. from pathlib import Path
  5. import random
  6. from typing import Dict, List, Optional
  7. from fastapi import APIRouter, HTTPException
  8. from pydantic import BaseModel
  9. from cachetools import TTLCache
  10. from utils.win import get_proxy_settings
  11. from utils.mihomo import get_sub,update_config,save_yaml_dump,find_free_port,port_is_using
  12. from utils.config import Config, config, PROXY_POLL_DIR, Sub, Proxy
  13. from utils.process_mgr import process_manager
  14. from utils.logu import get_logger,logger
  15. from src.services.subscription_manager import SubscriptionManager
  16. sub_mgr = SubscriptionManager(config=config)
  17. proxy_lock = asyncio.Lock() # 全局异步锁
  18. router = APIRouter()
  19. cache = TTLCache(maxsize=100, ttl=360)
  20. class SysProxyResponse(BaseModel):
  21. sys_open: bool
  22. proxy_server: str
  23. class SubUrlPost(BaseModel):
  24. sub_url: str
  25. @router.get("/sys")
  26. def read_root():
  27. proxy_enable, proxy_server = get_proxy_settings()
  28. return SysProxyResponse(sys_open=proxy_enable, proxy_server=proxy_server)
  29. class ProxyResponse(BaseModel):
  30. name: str
  31. port: int
  32. reachable: bool
  33. file_path: str
  34. mgr_url: str
  35. process_info: Optional[Dict] = None
  36. ping: Optional[Dict[str, int]] = None
  37. async def get_proxy_response(port: int, use_cache: bool = True):
  38. cache_key = f"get_proxy_response_{port}"
  39. porxy_model = sub_mgr.sub.proxies.get(port)
  40. proxy_mgr = sub_mgr.list_proxies_mgr.get(port)
  41. mgr_url = proxy_mgr.get_management_url()
  42. reachable = await port_is_using(port, timeout=0.5)
  43. name = porxy_model.name or ''
  44. process_info = None
  45. ping = {}
  46. if reachable:
  47. process_info = proxy_mgr.get_process_info()
  48. response = await proxy_mgr.get_now_selected_proxy()
  49. reachable = response.get("err", 1) == 0
  50. name = response.get("name", '')
  51. if name:
  52. if porxy_model.name and porxy_model.name != name:
  53. porxy_model.name = name
  54. sub_mgr.save_config()
  55. else:
  56. name = porxy_model.name or ''
  57. # ping = await proxy_mgr.ping_proxies()
  58. # logger.info(f"{response}")
  59. result = ProxyResponse(
  60. name=name,
  61. port=porxy_model.port,
  62. reachable=reachable,
  63. file_path=porxy_model.file_path,
  64. mgr_url=mgr_url,
  65. process_info=process_info,
  66. ping=ping
  67. )
  68. return result
  69. async def get_all_proxy_response(use_cache: bool = True):
  70. global sub_mgr
  71. ret = []
  72. tasks = []
  73. for port,porxy_model in sub_mgr.sub.proxies.items():
  74. tasks.append(get_proxy_response(port, use_cache))
  75. ret = await asyncio.gather(*tasks)
  76. return ret
  77. async def health_check_proxy_task(interval: int = 80):
  78. """定时检查所有代理的健康状态"""
  79. while True:
  80. try:
  81. logger.info("Running health check...")
  82. proxies = await get_all_proxy_response(use_cache=True)
  83. # if not proxies:
  84. # logger.error("Health check failed: No proxies available")
  85. # else:
  86. # logger.info(f"Health check succeeded: {len(proxies)} proxies are healthy")
  87. except Exception as e:
  88. logger.error(f"Health check failed: {e}")
  89. # 等待指定的时间间隔
  90. await asyncio.sleep(interval)
  91. @router.get("/ping")
  92. async def ping_proxies() -> Dict[str, int]:
  93. global sub_mgr,cache
  94. cache_key = f"ping_result"
  95. if cache_key not in cache:
  96. try:
  97. result = await sub_mgr.ping_proxies()
  98. cache[cache_key] = result
  99. except Exception as e:
  100. logger.error(f"ping_proxies error: {e}")
  101. return {"err": 1, "msg": str(e)}
  102. else:
  103. logger.info(f"use cache: {cache_key}")
  104. return cache[cache_key]
  105. @router.get("/proxies/{port}")
  106. @router.get("/proxies")
  107. async def get_proxies(port: int = None):
  108. if port:
  109. proxy_mgr = sub_mgr.get_proxy_manager(port)
  110. if not proxy_mgr:
  111. raise HTTPException(status_code=404, detail=f"Proxy with port {port} not found")
  112. return await get_proxy_response(port)
  113. else:
  114. ret = await get_all_proxy_response()
  115. logger.info(f"{ret}")
  116. return ret
  117. class ProxyPost(BaseModel):
  118. name: Optional[str] = None
  119. port: Optional[int] = None
  120. auto: Optional[bool] = False
  121. class ProxyPostResponse(BaseModel):
  122. err: int = 1
  123. msg: str = ''
  124. data: ProxyResponse
  125. async def auto_select_proxy(port: int):
  126. global sub_mgr
  127. ping_res = await ping_proxies()
  128. name = random.choice(list(ping_res.keys()))
  129. # sub_mgr.list_proxies_mgr.get(port).get_management_url()
  130. await sub_mgr.select_proxy(port, name)
  131. @router.delete("/proxies/{port}")
  132. async def delete_proxy(port: int):
  133. global sub_mgr
  134. try:
  135. proxy_mgr = sub_mgr.get_proxy_manager(port)
  136. if not proxy_mgr:
  137. raise HTTPException(status_code=404, detail=f"Proxy with port {port} not found")
  138. await sub_mgr.stop_proxy(port)
  139. if port in sub_mgr.sub.proxies:
  140. del sub_mgr.sub.proxies[port]
  141. sub_mgr.save_config()
  142. return await get_all_proxy_response()
  143. except Exception as e:
  144. logger.error(f"Failed to delete proxy {port}: {str(e)}")
  145. raise HTTPException(status_code=500, detail=str(e))
  146. @router.post("/proxies")
  147. async def create_proxy(request:ProxyPost):
  148. global sub_mgr,proxy_lock
  149. logger.info(f"request: {request}")
  150. proxy_mgr = None
  151. async with proxy_lock:
  152. if request.auto:
  153. porxy_port = await find_free_port((sub_mgr.sub.start_port, sub_mgr.sub.start_port + 10000))
  154. controler_port = await find_free_port((porxy_port + 1, porxy_port + 10001))
  155. elif request.port:
  156. porxy_port = request.port
  157. proxy_mgr = sub_mgr.get_proxy_manager(porxy_port)
  158. if proxy_mgr and proxy_mgr.running:
  159. # return {'err': 0, "msg": f"已开启,跳过 {porxy_port} ", "data": await get_proxy_response(porxy_port)}
  160. return ProxyPostResponse(err=0, msg=f"已开启,跳过 {porxy_port} ", data=await get_proxy_response(porxy_port))
  161. porxy_port_is_using = await port_is_using(porxy_port)
  162. controler_port = request.port + 1
  163. if porxy_port_is_using:
  164. # return ProxyPostResponse(err=1, msg=f"porxy_port={porxy_port} 端口已被占用")
  165. raise HTTPException(status_code=400, detail=ProxyPostResponse(err=1, msg=f"porxy_port={porxy_port} 端口已被占用"))
  166. if await port_is_using(controler_port):
  167. # return {"err": 1, "msg": f"controler_port={controler_port} 端口已被占用"}
  168. # return ProxyPostResponse(err=1, msg=f"controler_port={controler_port} 端口已被占用")
  169. raise HTTPException(status_code=400, detail=ProxyPostResponse(err=1, msg=f"controler_port={controler_port} 端口已被占用"))
  170. else:
  171. # return ProxyPostResponse(err=1, msg="port 或 auto 必须有一个")
  172. raise HTTPException(status_code=400, detail=ProxyPostResponse(err=1, msg="port 或 auto 必须有一个"))
  173. await sub_mgr.create_custom_config(porxy_port, controler_port)
  174. await sub_mgr.start_proxy(porxy_port)
  175. await auto_select_proxy(porxy_port)
  176. # return {"err": 0, "msg": "ok", "data": await get_proxy_response(porxy_port)}
  177. res = ProxyPostResponse(err=0, msg="ok", data=await get_proxy_response(porxy_port))
  178. logger.info(f"{res}")
  179. return res
  180. # return ProxyPostResponse(err=1, msg="proxy_lock error", data=sub_mgr.sub)
  181. return HTTPException(status_code=500, detail=ProxyPostResponse(err=1, msg="proxy_lock error", data=sub_mgr.sub))
  182. @router.post("/proxies/{port}/stop")
  183. async def stop_proxy(port: int):
  184. global sub_mgr
  185. proxy_mgr = sub_mgr.get_proxy_manager(port)
  186. if not proxy_mgr:
  187. raise HTTPException(status_code=404, detail=f"Proxy with port {port} not found")
  188. await sub_mgr.stop_proxy(port)
  189. return await get_proxy_response(port)
  190. @router.get("/subs")
  191. async def get_subscriptions():
  192. global sub_mgr
  193. return {"err": 0, "data": sub_mgr.sub}
  194. @router.post("/subs")
  195. async def create_subscription(request: SubUrlPost):
  196. try:
  197. global sub_mgr
  198. subscription = await sub_mgr.download_subscription(request.sub_url)
  199. return {"err": 0, "data": subscription.sub}
  200. except Exception as e:
  201. return {"err": 1, "msg": str(e)}
  202. def main():
  203. # 获取代理设置
  204. proxy_enable, proxy_server = get_proxy_settings()
  205. if proxy_enable:
  206. print(f"代理已启用,代理服务器地址和端口: {proxy_server}")
  207. else:
  208. print("代理未启用")
  209. if __name__ == "__main__":
  210. main()