main.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import logging
  2. import os
  3. from typing import List
  4. import httpx
  5. from fastapi import FastAPI, Request, Response, HTTPException
  6. from starlette.status import HTTP_502_BAD_GATEWAY
  7. from alien_gateway.config import settings
  8. app = FastAPI(
  9. title=f"{settings.PROJECT_NAME} - Gateway & Auth Service",
  10. version="1.0.0"
  11. )
  12. # ------------------- 日志配置 -------------------
  13. LOG_DIR = os.path.join("common", "logs", "alien_gateway")
  14. os.makedirs(LOG_DIR, exist_ok=True)
  15. def _init_logger():
  16. logger = logging.getLogger("alien_gateway")
  17. if logger.handlers:
  18. return logger
  19. logger.setLevel(logging.INFO)
  20. fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(message)s")
  21. # 文件日志
  22. info_handler = logging.FileHandler(os.path.join(LOG_DIR, "info.log"), encoding="utf-8")
  23. info_handler.setLevel(logging.INFO)
  24. info_handler.setFormatter(fmt)
  25. error_handler = logging.FileHandler(os.path.join(LOG_DIR, "error.log"), encoding="utf-8")
  26. error_handler.setLevel(logging.ERROR)
  27. error_handler.setFormatter(fmt)
  28. # 控制台日志
  29. console_handler = logging.StreamHandler()
  30. console_handler.setFormatter(fmt)
  31. logger.addHandler(info_handler)
  32. logger.addHandler(error_handler)
  33. logger.addHandler(console_handler)
  34. return logger
  35. logger = _init_logger()
  36. @app.get("/health")
  37. async def health():
  38. return {"service": "alien_gateway", "status": "ok"}
  39. # 此模块未来将承担 JWT 签发、权限校验中间件、路由聚合等核心功能
  40. @app.post("/auth/login")
  41. async def login():
  42. return {"message": "Auth logic here"}
  43. HOP_BY_HOP_HEADERS: List[str] = [
  44. "connection",
  45. "keep-alive",
  46. "proxy-authenticate",
  47. "proxy-authorization",
  48. "te",
  49. "trailers",
  50. "transfer-encoding",
  51. "upgrade",
  52. ]
  53. def _clean_headers(headers):
  54. """移除 hop-by-hop 头,避免转发问题。"""
  55. return {k: v for k, v in headers.items() if k.lower() not in HOP_BY_HOP_HEADERS}
  56. @app.api_route("/api/store/{full_path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
  57. async def proxy_to_store(full_path: str, request: Request):
  58. """
  59. 简易网关:监听 33333 端口,将 /api/store/* 转发到 alien_store 服务。
  60. """
  61. target_url = f"{settings.STORE_BASE_URL}/api/store/{full_path}"
  62. client_ip = request.client.host if request.client else "-"
  63. # 读取请求体
  64. body = await request.body()
  65. # 过滤头部
  66. headers = _clean_headers(dict(request.headers))
  67. try:
  68. async with httpx.AsyncClient(timeout=30.0) as client:
  69. resp = await client.request(
  70. request.method,
  71. target_url,
  72. content=body,
  73. headers=headers,
  74. params=request.query_params,
  75. )
  76. except Exception as exc:
  77. logger.error("proxy to store failed ip=%s url=%s err=%s", client_ip, target_url, exc)
  78. raise HTTPException(status_code=HTTP_502_BAD_GATEWAY, detail="Upstream unavailable")
  79. # 返回下游响应
  80. return Response(
  81. content=resp.content,
  82. status_code=resp.status_code,
  83. headers=_clean_headers(resp.headers),
  84. media_type=resp.headers.get("content-type"),
  85. )
  86. if __name__ == "__main__":
  87. import uvicorn
  88. uvicorn.run(app, host="0.0.0.0", port=33333)