proxy.py 8.2 KB

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