"""FastAPI:把 ck_post / ck_knowledge_item 暴露给 web 可视化,并托管单页前端。 启动: CK_ENV_FILE=/path/.env uvicorn creation_knowledge.api:app --host 0.0.0.0 --port 8900 API 在 /api/*,前端单页在 /。 """ from __future__ import annotations import os from functools import lru_cache from pathlib import Path from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from creation_knowledge.config import PgConfig from creation_knowledge.integrations.db import CkStore WEB_DIR = Path(__file__).resolve().parent.parent / "web" @lru_cache(maxsize=1) def _store() -> CkStore: env_file = os.getenv("CK_ENV_FILE", ".env") return CkStore(PgConfig.from_env(env_file)) app = FastAPI(title="创作知识") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] ) @app.get("/api/posts") def list_posts() -> list[dict]: return _store().posts_overview() @app.get("/api/posts/{post_id}") def get_post(post_id: str) -> dict: post = _store().read_post(post_id) if not post: raise HTTPException(status_code=404, detail="post not found") return post @app.get("/api/posts/{post_id}/items") def get_items(post_id: str) -> list[dict]: return _store().read_items(post_id) # 单页前端挂在最后(catch-all),不影响上面的 /api 路由 if WEB_DIR.exists(): app.mount("/", StaticFiles(directory=str(WEB_DIR), html=True), name="web")