#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ FastAPI接口应用 提供现代化的API接口和自动生成的文档 """ import os import sys from datetime import datetime, timedelta from typing import List, Optional from fastapi import FastAPI, HTTPException, Depends, status from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, Boolean, ForeignKey from sqlalchemy.orm import declarative_base, sessionmaker, Session # 添加项目根目录到Python路径 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # 导入配置 from config import Config # 数据库配置 DATABASE_URL = Config.SQLALCHEMY_DATABASE_URI engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # 创建FastAPI应用 app = FastAPI( title="KaMiXiTong API", description="软件授权管理系统的FastAPI接口", version="1.0.0", docs_url="/docs", redoc_url="/redoc" ) # 添加CORS中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 数据模型定义 class APIBase(BaseModel): api_name: str description: Optional[str] = None status: Optional[int] = 1 class Config: from_attributes = True # Pydantic V2中orm_mode已重命名为from_attributes class APICreate(APIBase): api_id: Optional[str] = None class APIUpdate(APIBase): pass class APIInDB(APIBase): api_id: str create_time: datetime update_time: datetime class APIKeyBase(BaseModel): name: str description: Optional[str] = None status: Optional[int] = 1 expire_time: Optional[datetime] = None class Config: from_attributes = True # Pydantic V2中orm_mode已重命名为from_attributes class APIKeyCreate(APIKeyBase): api_id: str class APIKeyUpdate(APIKeyBase): api_id: Optional[str] = None class APIKeyInDB(APIKeyBase): id: int key: str api_id: str create_time: datetime update_time: datetime class APIVersionBase(BaseModel): version_num: str description: Optional[str] = None publish_status: Optional[int] = 0 class Config: from_attributes = True # Pydantic V2中orm_mode已重命名为from_attributes class APIVersionCreate(APIVersionBase): api_id: str class APIVersionUpdate(APIVersionBase): api_id: Optional[str] = None class APIVersionInDB(APIVersionBase): id: int api_id: str create_time: datetime update_time: datetime # 数据库模型 class DBAPI(Base): __tablename__ = "api" api_id = Column(String(32), primary_key=True) api_name = Column(String(64), nullable=False) description = Column(Text, nullable=True) status = Column(Integer, nullable=False, default=1) create_time = Column(DateTime, default=datetime.utcnow) update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class DBAPIKey(Base): __tablename__ = "api_key" id = Column(Integer, primary_key=True) key = Column(String(64), nullable=False, unique=True) api_id = Column(String(32), ForeignKey('api.api_id'), nullable=False) name = Column(String(64), nullable=False) description = Column(Text, nullable=True) status = Column(Integer, nullable=False, default=1) create_time = Column(DateTime, default=datetime.utcnow) update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) expire_time = Column(DateTime, nullable=True) class DBAPIVersion(Base): __tablename__ = "api_version" id = Column(Integer, primary_key=True) version_num = Column(String(32), nullable=False) api_id = Column(String(32), ForeignKey('api.api_id'), nullable=False) description = Column(Text, nullable=True) publish_status = Column(Integer, nullable=False, default=0) create_time = Column(DateTime, default=datetime.utcnow) update_time = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 依赖项 def get_db(): db = SessionLocal() try: yield db finally: db.close() # API路由 @app.get("/") async def root(): return {"message": "欢迎使用KaMiXiTong FastAPI接口", "version": "1.0.0"} @app.get("/apis", response_model=List[APIInDB]) async def get_apis(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): """获取API列表""" apis = db.query(DBAPI).offset(skip).limit(limit).all() return apis @app.post("/apis", response_model=APIInDB) async def create_api(api: APICreate, db: Session = Depends(get_db)): """创建API""" # 检查自定义ID是否重复 if api.api_id: existing = db.query(DBAPI).filter(DBAPI.api_id == api.api_id).first() if existing: raise HTTPException(status_code=400, detail="API ID已存在") # 准备API数据 api_data = api.model_dump() # 处理API ID生成 if api.api_id is None or api.api_id == "": # 自动生成API ID import uuid api_data['api_id'] = f"API_{uuid.uuid4().hex[:8]}".upper() # 创建API db_api = DBAPI(**api_data) db.add(db_api) db.commit() db.refresh(db_api) return db_api @app.get("/apis/{api_id}", response_model=APIInDB) async def get_api(api_id: str, db: Session = Depends(get_db)): """获取API详情""" api = db.query(DBAPI).filter(DBAPI.api_id == api_id).first() if not api: raise HTTPException(status_code=404, detail="API不存在") return api @app.put("/apis/{api_id}", response_model=APIInDB) async def update_api(api_id: str, api: APIUpdate, db: Session = Depends(get_db)): """更新API""" db_api = db.query(DBAPI).filter(DBAPI.api_id == api_id).first() if not db_api: raise HTTPException(status_code=404, detail="API不存在") for key, value in api.model_dump().items(): setattr(db_api, key, value) db.commit() db.refresh(db_api) return db_api @app.delete("/apis/{api_id}") async def delete_api(api_id: str, db: Session = Depends(get_db)): """删除API""" db_api = db.query(DBAPI).filter(DBAPI.api_id == api_id).first() if not db_api: raise HTTPException(status_code=404, detail="API不存在") # 检查是否有关联的密钥 key_count = db.query(DBAPIKey).filter(DBAPIKey.api_id == api_id).count() if key_count > 0: raise HTTPException(status_code=400, detail=f"API下还有 {key_count} 个密钥,无法删除") db.delete(db_api) db.commit() return {"message": "API删除成功"} # API密钥路由 @app.get("/api_keys", response_model=List[APIKeyInDB]) async def get_api_keys(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): """获取API密钥列表""" keys = db.query(DBAPIKey).offset(skip).limit(limit).all() return keys @app.post("/api_keys", response_model=APIKeyInDB) async def create_api_key(key: APIKeyCreate, db: Session = Depends(get_db)): """生成API密钥""" # 检查API是否存在 api = db.query(DBAPI).filter(DBAPI.api_id == key.api_id).first() if not api: raise HTTPException(status_code=404, detail="指定的API不存在") # 生成唯一的API密钥 import secrets import string characters = string.ascii_letters + string.digits api_key_value = ''.join(secrets.choice(characters) for _ in range(32)) # 确保密钥唯一 max_attempts = 10 for _ in range(max_attempts): existing = db.query(DBAPIKey).filter(DBAPIKey.key == api_key_value).first() if not existing: break api_key_value = ''.join(secrets.choice(characters) for _ in range(32)) else: raise HTTPException(status_code=500, detail="无法生成唯一的API密钥,请稍后重试") # 准备密钥数据 key_data = key.model_dump() key_data['key'] = api_key_value # 创建API密钥 db_key = DBAPIKey(**key_data) db.add(db_key) db.commit() db.refresh(db_key) return db_key @app.get("/api_keys/{key_id}", response_model=APIKeyInDB) async def get_api_key(key_id: int, db: Session = Depends(get_db)): """获取API密钥详情""" key = db.query(DBAPIKey).filter(DBAPIKey.id == key_id).first() if not key: raise HTTPException(status_code=404, detail="API密钥不存在") return key @app.put("/api_keys/{key_id}", response_model=APIKeyInDB) async def update_api_key(key_id: int, key: APIKeyUpdate, db: Session = Depends(get_db)): """更新API密钥""" db_key = db.query(DBAPIKey).filter(DBAPIKey.id == key_id).first() if not db_key: raise HTTPException(status_code=404, detail="API密钥不存在") # 如果更新了api_id,检查API是否存在 if key.api_id and key.api_id != db_key.api_id: api = db.query(DBAPI).filter(DBAPI.api_id == key.api_id).first() if not api: raise HTTPException(status_code=404, detail="指定的API不存在") for field, value in key.model_dump().items(): if value is not None: setattr(db_key, field, value) db.commit() db.refresh(db_key) return db_key @app.delete("/api_keys/{key_id}") async def delete_api_key(key_id: int, db: Session = Depends(get_db)): """删除API密钥""" db_key = db.query(DBAPIKey).filter(DBAPIKey.id == key_id).first() if not db_key: raise HTTPException(status_code=404, detail="API密钥不存在") db.delete(db_key) db.commit() return {"message": "API密钥删除成功"} # API版本路由 @app.get("/api_versions", response_model=List[APIVersionInDB]) async def get_api_versions(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): """获取API版本列表""" versions = db.query(DBAPIVersion).offset(skip).limit(limit).all() return versions @app.post("/api_versions", response_model=APIVersionInDB) async def create_api_version(version: APIVersionCreate, db: Session = Depends(get_db)): """创建API版本""" # 检查API是否存在 api = db.query(DBAPI).filter(DBAPI.api_id == version.api_id).first() if not api: raise HTTPException(status_code=404, detail="指定的API不存在") # 检查版本号是否重复 existing = db.query(DBAPIVersion).filter( DBAPIVersion.api_id == version.api_id, DBAPIVersion.version_num == version.version_num ).first() if existing: raise HTTPException(status_code=400, detail="该API下已存在相同版本号") # 创建API版本 db_version = DBAPIVersion(**version.model_dump()) db.add(db_version) db.commit() db.refresh(db_version) return db_version @app.get("/api_versions/{version_id}", response_model=APIVersionInDB) async def get_api_version(version_id: int, db: Session = Depends(get_db)): """获取API版本详情""" version = db.query(DBAPIVersion).filter(DBAPIVersion.id == version_id).first() if not version: raise HTTPException(status_code=404, detail="API版本不存在") return version @app.put("/api_versions/{version_id}", response_model=APIVersionInDB) async def update_api_version(version_id: int, version: APIVersionUpdate, db: Session = Depends(get_db)): """更新API版本""" db_version = db.query(DBAPIVersion).filter(DBAPIVersion.id == version_id).first() if not db_version: raise HTTPException(status_code=404, detail="API版本不存在") # 如果更新了api_id,检查API是否存在 if version.api_id and version.api_id != db_version.api_id: api = db.query(DBAPI).filter(DBAPI.api_id == version.api_id).first() if not api: raise HTTPException(status_code=404, detail="指定的API不存在") # 如果更新了version_num,检查版本号是否重复(排除自己) if version.version_num and version.version_num != db_version.version_num: existing = db.query(DBAPIVersion).filter( DBAPIVersion.api_id == db_version.api_id, DBAPIVersion.version_num == version.version_num, DBAPIVersion.id != version_id ).first() if existing: raise HTTPException(status_code=400, detail="该API下已存在相同版本号") for field, value in version.model_dump().items(): if value is not None: setattr(db_version, field, value) db.commit() db.refresh(db_version) return db_version @app.delete("/api_versions/{version_id}") async def delete_api_version(version_id: int, db: Session = Depends(get_db)): """删除API版本""" db_version = db.query(DBAPIVersion).filter(DBAPIVersion.id == version_id).first() if not db_version: raise HTTPException(status_code=404, detail="API版本不存在") db.delete(db_version) db.commit() return {"message": "API版本删除成功"} if __name__ == "__main__": import uvicorn # 使用127.0.0.1而不是0.0.0.0来避免权限问题 uvicorn.run(app, host="127.0.0.1", port=9002, log_level="info")