#!/bin/bash # ============================================================================= # API 文档生成脚本 # ============================================================================= # 用途: 从 FastAPI 后端自动生成 OpenAPI 规范 + 静态 HTML 文档 # 输出: # docs/api/openapi.json - OpenAPI 3.0 规范 # docs/api/index.html - Swagger UI 静态版 # docs/api/redoc.html - ReDoc 静态版 # # 用法: # bash scripts/generate-api-docs.sh # 跑后端拿 OpenAPI # bash scripts/generate-api-docs.sh --from-running # 从运行中后端拿 # bash scripts/generate-api-docs.sh --offline # 离线生成(无需后端) # ============================================================================= set -e # 颜色 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $1"; } ok() { echo -e "${GREEN}[OK]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$PROJECT_ROOT" API_DOCS_DIR="docs/api" mkdir -p "$API_DOCS_DIR" # 参数 MODE="auto" for arg in "$@"; do case $arg in --from-running) MODE="running" ;; --offline) MODE="offline" ;; esac done # ============================================================================= # 1. 拿 OpenAPI 规范 # ============================================================================= info "── 1/3 拿 OpenAPI 规范" case $MODE in running|auto) # 先看后端跑没 if curl -s -f http://localhost:8000/openapi.json > /tmp/openapi.json 2>/dev/null; then ok "从运行中后端拿 OpenAPI" cp /tmp/openapi.json "$API_DOCS_DIR/openapi.json" elif [ "$MODE" = "running" ]; then error "后端没跑,无法从 running 拿" else warn "后端没跑,改用离线生成" MODE="offline" fi ;; esac if [ "$MODE" = "offline" ]; then info "离线生成 OpenAPI(import FastAPI app)..." cd backend if [ ! -d "venv" ]; then warn "后端 venv 不存在,跑: python -m venv venv && pip install -r requirements.txt" fi cat > /tmp/gen_openapi.py <<'PYEOF' import json import sys try: from app.main import app spec = app.openapi() print(json.dumps(spec, ensure_ascii=False, indent=2)) except Exception as e: print(f"ERROR: {e}", file=sys.stderr) sys.exit(1) PYEOF if command -v python &> /dev/null; then if python /tmp/gen_openapi.py > "../$API_DOCS_DIR/openapi.json" 2>/dev/null; then ok "离线生成 OpenAPI 成功" else # 试 python3 if python3 /tmp/gen_openapi.py > "../$API_DOCS_DIR/openapi.json" 2>/dev/null; then ok "离线生成 OpenAPI 成功(python3)" else warn "离线生成失败,降级到 mock 模式" cat > "../$API_DOCS_DIR/openapi.json" <<'JSONEOF' { "openapi": "3.0.0", "info": { "title": "企微 IT 智能服务台 API", "version": "1.0.0", "description": "离线生成的 mock,实际跑后端再生成" }, "paths": {} } JSONEOF fi fi fi cd "$PROJECT_ROOT" fi # 验证 OpenAPI if [ ! -f "$API_DOCS_DIR/openapi.json" ]; then error "OpenAPI 规范生成失败" fi ENDPOINT_COUNT=$(python -c "import json; d=json.load(open('$API_DOCS_DIR/openapi.json')); print(len(d.get('paths', {})))" 2>/dev/null || echo "?") ok "OpenAPI 规范生成,端点数: $ENDPOINT_COUNT" # ============================================================================= # 2. 生成 Swagger UI 静态 HTML # ============================================================================= info "── 2/3 生成 Swagger UI 静态 HTML" cat > "$API_DOCS_DIR/index.html" <<'HTMLEOF' 企微 IT 智能服务台 API - Swagger UI

📡 企微 IT 智能服务台 API 文档

📖 ReDoc 版 📄 OpenAPI 规范
HTMLEOF ok "Swagger UI 生成: $API_DOCS_DIR/index.html" # ============================================================================= # 3. 生成 ReDoc 静态 HTML # ============================================================================= info "── 3/3 生成 ReDoc 静态 HTML" cat > "$API_DOCS_DIR/redoc.html" <<'HTMLEOF' 企微 IT 智能服务台 API - ReDoc HTMLEOF ok "ReDoc 生成: $API_DOCS_DIR/redoc.html" # ============================================================================= # 4. 生成 API 模块清单 # ============================================================================= info "── 4/4 生成模块清单" python3 -c " import json with open('$API_DOCS_DIR/openapi.json') as f: spec = json.load(f) paths = spec.get('paths', {}) modules = {} for path, methods in paths.items(): # 解析 /api/v1// parts = path.split('/') if len(parts) >= 4 and parts[1] == 'api' and parts[2] == 'v1': module = parts[3] if module not in modules: modules[module] = [] for method in methods.keys(): if method in ['get', 'post', 'put', 'delete', 'patch']: modules[module].append({ 'method': method.upper(), 'path': path, }) print('# API 模块清单') print() print('**生成日期**: $(date +%Y-%m-%d)') print('**端点总数**: ', len(paths)) print('**模块数**: ', len(modules)) print() print('| 模块 | 端点数 | 端点 |') print('|---|---|---|') for module, endpoints in sorted(modules.items()): eps = ', '.join(f\"{e['method']} {e['path']}\" for e in endpoints[:5]) if len(endpoints) > 5: eps += f' ... (+{len(endpoints)-5})' print(f\"| {module} | {len(endpoints)} | {eps} |\") " > "$API_DOCS_DIR/MODULES.md" 2>/dev/null || { warn "模块清单生成失败(Python 解析)" cat > "$API_DOCS_DIR/MODULES.md" <<'EOF' # API 模块清单 (生成失败,见 docs/api/openapi.json 自行查看) EOF } ok "模块清单生成: $API_DOCS_DIR/MODULES.md" # ============================================================================= # 总结 # ============================================================================= info "── 总结" echo "" echo "输出文件:" echo " $API_DOCS_DIR/openapi.json - OpenAPI 3.0 规范" echo " $API_DOCS_DIR/index.html - Swagger UI 静态版" echo " $API_DOCS_DIR/redoc.html - ReDoc 静态版" echo " $API_DOCS_DIR/MODULES.md - 模块清单" echo "" echo "查看方式:" echo " 1. 浏览器打开 file://\$(pwd)/$API_DOCS_DIR/index.html" echo " 2. 跑 python -m http.server -d $API_DOCS_DIR 8080 → 浏览器 http://localhost:8080" echo "" echo "CI 集成:" echo " 把 'bash scripts/generate-api-docs.sh' 加进 Gitea Actions" echo " 跑批频率:每次 main 推送后" ok "API 文档生成完成"