Files
vps-manager/app/main.py
T

82 lines
2.5 KiB
Python

"""vps-manager 后端入口
- 注册资产 CRUD 与统计路由
- 挂载本地静态资源(Vue/Tailwind/前端逻辑)
- 渲染前端 SPA 入口页面
- 启用 CORS(便于外部程序/其他 AI 跨域调用写入接口)
"""
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from jinja2 import Environment, FileSystemLoader
from sqlmodel import Session
from app.core.config import settings
from app.core.seed import seed_providers
from app.database import assets_engine, init_db
from app.routers import agent, assets, monitor, providers, stats, sync
BASE_DIR = Path(__file__).resolve().parent.parent
STATIC_DIR = BASE_DIR / "static"
TEMPLATE_DIR = BASE_DIR / "app" / "templates"
STATIC_DIR.mkdir(parents=True, exist_ok=True)
# 直接使用 Jinja2 Environment 渲染(规避 Starlette Jinja2Templates 在 Python 3.14 下的缓存兼容问题)
jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_DIR), autoescape=True)
@asynccontextmanager
async def lifespan(_: FastAPI):
"""应用启动时自动建表并初始化预设平台"""
init_db()
with Session(assets_engine) as session:
seed_providers(session)
yield
app = FastAPI(title=settings.APP_NAME, version="0.2.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(assets.router)
app.include_router(providers.router)
app.include_router(stats.router)
app.include_router(agent.router)
app.include_router(monitor.router)
app.include_router(sync.router)
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
@app.get("/", include_in_schema=False)
def index() -> HTMLResponse:
"""前端 SPA 入口"""
html = jinja_env.get_template("index.html").render(app_name=settings.APP_NAME)
return HTMLResponse(html)
@app.get("/sw.js", include_in_schema=False)
def service_worker() -> FileResponse:
"""Service Worker(置于根路径以使 scope 覆盖全站)"""
return FileResponse(
STATIC_DIR / "sw.js",
media_type="application/javascript",
headers={"Service-Worker-Allowed": "/"},
)
@app.get("/health")
def health_check() -> dict:
"""健康检查(version 动态读取,便于验证自动更新)"""
return {"status": "ok", "app": "vps-manager", "version": app.version}