xxy 43f3e0b746 Initial commit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 18:41:06 +08:00

102 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
main.py
报告模板管理模块 FastAPI 应用入口。
启动:
uvicorn main:app --host 0.0.0.0 --port 8100
或:
python main.py
"""
from __future__ import annotations
import time
import uuid
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from config import settings
from database import init_database
from log import configure_logging, get_logger
from routers import template
# 在创建应用前完成日志配置
configure_logging()
logger = get_logger("app")
access_logger = get_logger("app.access")
@asynccontextmanager
async def lifespan(_app: FastAPI):
logger.info("应用启动 | %s v%s", settings.APP_TITLE, settings.APP_VERSION)
if settings.DB_AUTO_CREATE_TABLES:
try:
init_database()
except Exception as e: # noqa: BLE001
logger.warning("启动建表失败(不影响已存在表的使用): %s", e)
yield
logger.info("应用关闭")
app = FastAPI(
title=settings.APP_TITLE,
version=settings.APP_VERSION,
description=settings.APP_DESCRIPTION,
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def log_requests(request: Request, call_next):
if not settings.LOG_HTTP_ACCESS:
return await call_next(request)
req_id = uuid.uuid4().hex[:8]
start = time.perf_counter()
client = request.client.host if request.client else "-"
access_logger.info("→ [%s] %s %s | client=%s", req_id, request.method, request.url.path, client)
try:
response = await call_next(request)
except Exception:
cost = (time.perf_counter() - start) * 1000
access_logger.exception("✗ [%s] %s %s | %.1fms | 未处理异常", req_id, request.method, request.url.path, cost)
raise
cost = (time.perf_counter() - start) * 1000
access_logger.info(
"← [%s] %s %s | %s | %.1fms",
req_id, request.method, request.url.path, response.status_code, cost,
)
response.headers["X-Request-ID"] = req_id
return response
app.include_router(template.router)
@app.get("/health", tags=["健康检查"])
def health() -> dict:
return {"status": "ok", "version": settings.APP_VERSION}
if __name__ == "__main__":
import uvicorn
# log_config=None沿用本模块 configure_logging() 的配置,避免被 uvicorn 覆盖
uvicorn.run(
"main:app",
host=settings.HOST,
port=settings.PORT,
reload=settings.RELOAD,
log_config=None,
)