1
0

config_proxy.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from fastapi import APIRouter, Depends, HTTPException, Body, status, Query
  2. from fastapi.responses import JSONResponse
  3. import httpx
  4. from app.api.users import require_admin_user
  5. from ..core.logger import get_logger
  6. from ..core.config import settings
  7. from contextlib import asynccontextmanager
  8. logger = get_logger(__name__)
  9. router = APIRouter()
  10. # 1. 创建一个全局的 client (配置好超时和连接池)
  11. # limits: max_keepalive_connections 控制连接池大小
  12. http_client = httpx.AsyncClient(timeout=10.0, limits=httpx.Limits(max_keepalive_connections=20, max_connections=100))
  13. def _extract_upstream_error_detail(resp: httpx.Response) -> str:
  14. try:
  15. data = resp.json()
  16. if isinstance(data, dict):
  17. detail = data.get("detail")
  18. if detail:
  19. return str(detail)
  20. message = data.get("message")
  21. if message:
  22. return str(message)
  23. return str(data)
  24. except Exception:
  25. text = (resp.text or "").strip()
  26. if text:
  27. return text[:2000]
  28. return "上游服务错误"
  29. @router.get("/scoring_config", summary="(管理员) 获取评分配置 [用户调用]")
  30. async def proxy_get_scoring_config(current_user: dict = Depends(require_admin_user)):
  31. target_url = settings.SCORE_SERVER_CONFIG_URL
  32. try:
  33. # 2. 直接使用全局 client,不需要 async with
  34. resp = await http_client.get(target_url)
  35. if resp.status_code != 200:
  36. logger.error(f"项目1返回错误: {resp.status_code} - {resp.text}")
  37. raise HTTPException(
  38. status_code=resp.status_code,
  39. detail=_extract_upstream_error_detail(resp)
  40. )
  41. return resp.json()
  42. except httpx.RequestError as exc:
  43. logger.error(f"连接项目1失败: {exc}")
  44. raise HTTPException(status_code=503, detail=str(exc))
  45. @router.put("/scoring_config", summary="(管理员) 更新评分配置 [用户调用]")
  46. async def proxy_update_scoring_config(
  47. config_data: dict = Body(...),
  48. current_user: dict = Depends(require_admin_user)
  49. ):
  50. target_url = settings.SCORE_SERVER_CONFIG_URL
  51. try:
  52. # 2. 复用连接
  53. resp = await http_client.put(target_url, json=config_data)
  54. if resp.status_code != 200:
  55. raise HTTPException(status_code=resp.status_code, detail=f"保存失败: {_extract_upstream_error_detail(resp)}")
  56. try:
  57. payload = resp.json()
  58. except Exception:
  59. payload = None
  60. message = payload.get("message") if isinstance(payload, dict) else None
  61. return_data = {"detail": f"{message or '保存成功'}"}
  62. return JSONResponse(status_code=resp.status_code, content=return_data)
  63. except httpx.RequestError as exc:
  64. logger.error(f"连接项目1失败: {exc}")
  65. raise HTTPException(status_code=503, detail=str(exc))
  66. @router.get("/severity_names", summary="[代理] 获取缺陷严重程度列表 [用户调用]")
  67. async def proxy_get_severity_names(defect_label: str = Query(..., description="缺陷标签")):
  68. target_url = f"{settings.SCORE_SERVER_CONFIG_URL.replace('/scoring_config', '')}/severity_names"
  69. # 注意:settings.SCORE_SERVER_CONFIG_URL 通常是 http://host:port/api/config/scoring_config
  70. # 我们需要构建 http://host:port/api/config/severity_names
  71. # 更加稳健的写法是基于 base URL 拼接,这里假设 replace 可行,或者直接使用 query params
  72. # 假设 settings.SCORE_SERVER_CONFIG_URL 是完整路径 ".../scoring_config"
  73. # 我们需要把它替换掉
  74. temp = settings.SCORE_SERVER_CONFIG_URL
  75. base_url = temp.rsplit('/', 1)[0]
  76. final_url = f"{base_url}/severity_names"
  77. try:
  78. # 发送带参数的 GET 请求
  79. resp = await http_client.get(final_url, params={"defect_label": defect_label})
  80. if resp.status_code != 200:
  81. logger.error(f"项目1返回错误: {resp.status_code} - {resp.text}")
  82. raise HTTPException(
  83. status_code=resp.status_code,
  84. detail=_extract_upstream_error_detail(resp)
  85. )
  86. return resp.json()
  87. except httpx.RequestError as exc:
  88. logger.error(f"连接项目1失败: {exc}")
  89. raise HTTPException(status_code=503, detail=str(exc))