main.py 3.8 KB

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