import uuid import boto3 from fastapi import FastAPI, HTTPException, Depends from sqlalchemy import create_engine, Column, String, ForeignKey from sqlalchemy.orm import sessionmaker, Session, declarative_base, relationship from app.config import settings # --- 数据库初始化 --- engine = create_engine(settings.database_url) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # --- S3 客户端初始化 --- s3_client = boto3.client( 's3', aws_access_key_id=settings.s3_access_key, aws_secret_access_key=settings.s3_secret_key, endpoint_url=settings.s3_endpoint, region_name=settings.s3_region ) # --- 数据模型 --- class Project(Base): __tablename__ = "projects" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) name = Column(String, nullable=False) # cascade="all, delete-orphan" 确保在 db.delete(project) 时自动删除关联的 Markdown 记录 markdowns = relationship("Markdown", back_populates="project", cascade="all, delete-orphan") class Markdown(Base): __tablename__ = "markdowns" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) title = Column(String, nullable=False) s3_key = Column(String, nullable=False) project_id = Column(String, ForeignKey("projects.id", ondelete="CASCADE")) project = relationship("Project", back_populates="markdowns") Base.metadata.create_all(bind=engine) app = FastAPI() def get_db(): db = SessionLocal() try: yield db finally: db.close() # --- API 接口 --- @app.get("/health") def health(): return {"status": "ok"} @app.post("/projects") def create_project(name: str, db: Session = Depends(get_db)): project = Project(name=name) db.add(project) db.commit() db.refresh(project) return project @app.post("/projects/{project_id}/markdowns") def upload_markdown(project_id: str, title: str, content: str, db: Session = Depends(get_db)): project = db.query(Project).filter(Project.id == project_id).first() if not project: raise HTTPException(status_code=404, detail="Project not found") # 1. 存入 S3 s3_key = f"projects/{project_id}/{uuid.uuid4()}.md" s3_client.put_object( Bucket=settings.s3_bucket_name, Key=s3_key, Body=content, ContentType="text/markdown" ) # 2. 存入数据库 md_record = Markdown(title=title, s3_key=s3_key, project_id=project_id) db.add(md_record) db.commit() return {"id": md_record.id, "s3_key": s3_key} @app.delete("/projects/{project_id}") def delete_project(project_id: str, db: Session = Depends(get_db)): project = db.query(Project).filter(Project.id == project_id).first() if not project: raise HTTPException(status_code=404, detail="Project not found") # 1. 清理 S3 中的文件 markdowns = db.query(Markdown).filter(Markdown.project_id == project_id).all() if markdowns: keys = [{'Key': md.s3_key} for md in markdowns] s3_client.delete_objects( Bucket=settings.s3_bucket_name, Delete={'Objects': keys} ) # 2. 从数据库删除项目 (SQLAlchemy 会自动处理 markdowns 表的级联删除) db.delete(project) db.commit() return {"message": f"Project {project_id} and its files have been purged."}