main.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import asyncio
  2. import uuid
  3. import uvicorn
  4. from typing import Dict, Optional
  5. from fastapi import FastAPI, APIRouter, UploadFile, BackgroundTasks
  6. from fastapi.templating import Jinja2Templates
  7. from loguru import logger
  8. from starlette.requests import Request
  9. from starlette.responses import HTMLResponse
  10. from starlette.staticfiles import StaticFiles
  11. from utils import parse_line
  12. from core import AsyncGrassWs
  13. app = FastAPI()
  14. templates = Jinja2Templates(directory="templates")
  15. app.mount("/static", StaticFiles(directory="static"), name="static")
  16. client_router = APIRouter(prefix='/client')
  17. all_client: Dict[str, AsyncGrassWs] = {}
  18. all_client_ids = []
  19. CLIENT_INDEX = 0
  20. # 或者,如果有多个 task
  21. background_tasks = set()
  22. def run_client(client_id):
  23. task = asyncio.create_task(all_client[client_id].run())
  24. # 将 task 添加到集合中,以保持强引用:
  25. background_tasks.add(task)
  26. # 为了防止 task 被永远保持强引用,而无法被垃圾回收
  27. # 让每个 task 在结束后将自己从集合中移除:
  28. task.add_done_callback(background_tasks.discard)
  29. return client_id
  30. def add_client(grass_client: AsyncGrassWs):
  31. client_id = uuid.uuid4().__str__()
  32. all_client[client_id] = grass_client
  33. all_client_ids.append(client_id)
  34. return client_id
  35. async def delete_client(client_id):
  36. logger.info(f'[退出] {all_client[client_id].user_id}')
  37. await all_client[client_id].stop()
  38. del all_client[client_id]
  39. all_client_ids.remove(client_id)
  40. def load_file_clients(data):
  41. new_clients = []
  42. index = 0
  43. for line in data.split('\n'):
  44. user_id, proxy_url = parse_line(line)
  45. if not user_id:
  46. continue
  47. index += 1
  48. client = AsyncGrassWs(user_id=user_id, proxy_url=proxy_url)
  49. new_clients.append(add_client(client))
  50. return new_clients
  51. async def threading_run_clients(clients):
  52. for client_id in clients:
  53. run_client(client_id)
  54. @app.get("/", response_class=HTMLResponse)
  55. async def read_item(request: Request):
  56. return templates.TemplateResponse(request=request, name="index.html")
  57. @client_router.get("/{client_id}")
  58. def find_one(client_id: str):
  59. client = all_client.get(client_id)
  60. data = {
  61. 'data': {
  62. 'status': None,
  63. "proxy_url": None,
  64. "logs": []
  65. },
  66. 'message': "failed"
  67. }
  68. if client is not None:
  69. data = {
  70. 'data': {
  71. 'status': client.status,
  72. "proxy_url": client.proxy_url,
  73. "logs": list(reversed(client.logs[-50:]))
  74. },
  75. 'message': "success"
  76. }
  77. return data
  78. @client_router.get("/")
  79. def find_all():
  80. data = []
  81. for client_id in all_client_ids:
  82. try:
  83. data.append({
  84. 'id': client_id,
  85. 'user_id': all_client[client_id].user_id,
  86. 'status': all_client[client_id].status,
  87. "proxy_url": all_client[client_id].proxy_url
  88. })
  89. except:
  90. continue
  91. return {
  92. 'data': data,
  93. 'message': "success"
  94. }
  95. @client_router.post("/")
  96. async def add(user_id: str, proxy_url: Optional[str] = None):
  97. client = AsyncGrassWs(user_id=user_id, proxy_url=proxy_url or None)
  98. client_id = add_client(client)
  99. run_client(client_id)
  100. return {'data': client_id, 'message': 'create success'}
  101. @client_router.delete("/{user_id}")
  102. async def delete_one(user_id: str):
  103. await delete_client(user_id)
  104. return {'data': user_id, 'message': 'success'}
  105. @client_router.delete("/")
  106. async def delete_all():
  107. all_client_ids_copy = all_client_ids[::]
  108. for client_id in all_client_ids_copy:
  109. await delete_client(client_id)
  110. return {'data': [], 'message': 'success'}
  111. @app.post("/upload/")
  112. async def run_by_file(file: UploadFile, background_task: BackgroundTasks):
  113. data = (await file.read()).decode()
  114. new_clients = load_file_clients(data)
  115. background_task.add_task(threading_run_clients, new_clients)
  116. return {"data": None, 'message': 'success'}
  117. app.include_router(client_router)
  118. if __name__ == '__main__':
  119. uvicorn.run("main:app", host='0.0.0.0')