首次提交
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from starlette.requests import Request
|
||||
|
||||
from backend.database import init_db
|
||||
from backend.routers import api, admin
|
||||
from backend.config import get_settings
|
||||
from backend.bootstrap import ensure_self_signed_cert
|
||||
from backend.routers.admin import (
|
||||
login, logout, check_auth,
|
||||
get_stats, list_servers, get_server, create_server, update_server, delete_server,
|
||||
list_domains, get_domain, create_domain, update_domain, delete_domain,
|
||||
list_logs, get_acme_config, update_acme_config,
|
||||
issue_cert, renew_cert, auto_renew_all, list_acme_logs, get_cert_info,
|
||||
)
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logger = logging.getLogger("certcenter")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
await init_db()
|
||||
settings = get_settings()
|
||||
ensure_self_signed_cert(settings.base_url, settings.cert_dir)
|
||||
logger.info("=== 服务启动完成 ===")
|
||||
# 打印所有已注册的路由
|
||||
for route in app.routes:
|
||||
methods = getattr(route, "methods", None)
|
||||
path = getattr(route, "path", None)
|
||||
if path:
|
||||
logger.info(f" {methods} {path}")
|
||||
else:
|
||||
logger.info(f" [included router] {type(route).__name__}")
|
||||
yield
|
||||
|
||||
|
||||
app = FastAPI(title="CertCenter", version="1.0.0", lifespan=lifespan)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# ── 请求日志中间件 ──
|
||||
@app.middleware("http")
|
||||
async def log_request(request: Request, call_next):
|
||||
logger.info(f">>> {request.method} {request.url.path}")
|
||||
response = await call_next(request)
|
||||
logger.info(f"<<< {request.method} {request.url.path} -> {response.status_code}")
|
||||
return response
|
||||
|
||||
|
||||
# ── 客户端 API ──
|
||||
app.include_router(api.router, prefix="/api")
|
||||
|
||||
# ── 管理 API(直接注册)──
|
||||
app.post("/admin/api/login")(login)
|
||||
app.post("/admin/api/logout")(logout)
|
||||
app.get("/admin/api/me")(check_auth)
|
||||
app.get("/admin/api/stats")(get_stats)
|
||||
app.get("/admin/api/servers")(list_servers)
|
||||
app.get("/admin/api/servers/{server_id}")(get_server)
|
||||
app.post("/admin/api/servers", status_code=201)(create_server)
|
||||
app.put("/admin/api/servers/{server_id}")(update_server)
|
||||
app.delete("/admin/api/servers/{server_id}")(delete_server)
|
||||
app.get("/admin/api/domains")(list_domains)
|
||||
app.get("/admin/api/domains/{domain_id}")(get_domain)
|
||||
app.post("/admin/api/domains", status_code=201)(create_domain)
|
||||
app.put("/admin/api/domains/{domain_id}")(update_domain)
|
||||
app.delete("/admin/api/domains/{domain_id}")(delete_domain)
|
||||
app.get("/admin/api/logs")(list_logs)
|
||||
app.get("/admin/api/acme/config")(get_acme_config)
|
||||
app.put("/admin/api/acme/config")(update_acme_config)
|
||||
app.post("/admin/api/acme/issue/{domain_id}")(issue_cert)
|
||||
app.post("/admin/api/acme/renew/{domain_id}")(renew_cert)
|
||||
app.post("/admin/api/acme/auto-renew")(auto_renew_all)
|
||||
app.get("/admin/api/acme/logs")(list_acme_logs)
|
||||
app.get("/admin/api/cert-info/{domain_id}")(get_cert_info)
|
||||
|
||||
logger.info("=== 路由注册完成 ===")
|
||||
for route in app.routes:
|
||||
methods = getattr(route, "methods", None)
|
||||
path = getattr(route, "path", None)
|
||||
if path:
|
||||
logger.info(f" {methods} {path}")
|
||||
else:
|
||||
logger.info(f" [included router] {type(route).__name__}")
|
||||
|
||||
|
||||
# ── 前端静态文件 ──
|
||||
frontend_dist = Path(__file__).parent.parent / "frontend" / "dist"
|
||||
if frontend_dist.exists():
|
||||
assets_dir = frontend_dist / "assets"
|
||||
if assets_dir.exists():
|
||||
app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")
|
||||
|
||||
index_file = frontend_dist / "index.html"
|
||||
|
||||
@app.middleware("http")
|
||||
async def spa_fallback(request: Request, call_next):
|
||||
response = await call_next(request)
|
||||
if request.method == "GET" and response.status_code == 404:
|
||||
path = request.url.path
|
||||
if not path.startswith("/api/") and not path.startswith("/admin/api/") and not path.startswith("/assets/"):
|
||||
return FileResponse(str(index_file))
|
||||
return response
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run("backend.main:app", host="0.0.0.0", port=8000, reload=True)
|
||||
Reference in New Issue
Block a user