"""代理相关路由(含统计信息)""" from typing import Optional from fastapi import APIRouter, Depends, Query from app.services.proxy_service import ProxyService from app.services.scheduler_service import SchedulerService from app.models.schemas import ProxyListRequest, BatchDeleteRequest from app.api.deps import get_proxy_service, get_scheduler_service from app.api.common import success_response, error_response, format_proxy from app.core.log import logger router = APIRouter(prefix="/api/proxies", tags=["proxies"]) @router.get("/stats") async def get_stats( proxy_service: ProxyService = Depends(get_proxy_service), scheduler_service: SchedulerService = Depends(get_scheduler_service), ): try: stats = await proxy_service.get_stats() stats["scheduler_running"] = scheduler_service.running return success_response("获取统计信息成功", stats) except Exception as e: logger.error(f"Get stats failed: {e}") return error_response("获取统计信息失败", 500) @router.post("") async def list_proxies( request: ProxyListRequest, service: ProxyService = Depends(get_proxy_service), ): try: proxies, total = await service.list_proxies( page=request.page, page_size=request.page_size, protocol=request.protocol, min_score=request.min_score, max_score=request.max_score, sort_by=request.sort_by, sort_order=request.sort_order, ) return success_response( "获取代理列表成功", { "list": [format_proxy(p) for p in proxies], "total": total, "page": request.page, "page_size": request.page_size, }, ) except Exception as e: logger.error(f"List proxies failed: {e}") return error_response("获取代理列表失败", 500) @router.get("/random") async def get_random_proxy(service: ProxyService = Depends(get_proxy_service)): try: proxy = await service.get_random_proxy() if not proxy: return error_response("没有找到可用的代理", 404) return success_response("获取随机代理成功", format_proxy(proxy)) except Exception as e: logger.error(f"Get random proxy failed: {e}") return error_response("获取随机代理失败", 500) @router.get("/export/{fmt}") async def export_proxies( fmt: str, protocol: Optional[str] = None, limit: int = Query(default=10000, ge=1, le=100000), service: ProxyService = Depends(get_proxy_service), ): if fmt not in ("csv", "txt", "json"): return error_response("不支持的导出格式", 400) from fastapi.responses import StreamingResponse media_types = {"csv": "text/csv", "txt": "text/plain", "json": "application/json"} async def generate(): async for chunk in service.export_proxies(fmt, protocol, limit): yield chunk return StreamingResponse( generate(), media_type=media_types[fmt], headers={"Content-Disposition": f"attachment; filename=proxies.{fmt}"}, ) @router.delete("/{ip}/{port}") async def delete_proxy(ip: str, port: int, service: ProxyService = Depends(get_proxy_service)): try: await service.delete_proxy(ip, port) return success_response("删除代理成功") except Exception as e: logger.error(f"Delete proxy failed: {e}") return error_response("删除代理失败", 500) @router.post("/batch-delete") async def batch_delete( request: BatchDeleteRequest, service: ProxyService = Depends(get_proxy_service), ): try: proxies = [(item.ip, item.port) for item in request.proxies] deleted = await service.batch_delete(proxies) return success_response(f"批量删除 {deleted} 个代理成功", {"deleted_count": deleted}) except Exception as e: logger.error(f"Batch delete failed: {e}") return error_response("批量删除失败", 500) @router.delete("/clean-invalid") async def clean_invalid(service: ProxyService = Depends(get_proxy_service)): try: count = await service.clean_invalid() return success_response(f"清理了 {count} 个无效代理", {"deleted_count": count}) except Exception as e: logger.error(f"Clean invalid failed: {e}") return error_response("清理无效代理失败", 500)