"""
API文档生成器 - 离线生成Swagger文档
"""
import hashlib
import json
import os
from pathlib import Path
from typing import Optional
from ..utils import get_logger
logger = get_logger(__name__)
# 文档缓存目录
STATIC_DIR = Path("static")
CACHE_FILE = STATIC_DIR / ".openapi_cache"
DOC_FILE = STATIC_DIR / "doc.html"
def get_openapi_hash(openapi_spec: dict) -> str:
"""计算OpenAPI规范的哈希值"""
spec_str = json.dumps(openapi_spec, sort_keys=True, ensure_ascii=False)
return hashlib.md5(spec_str.encode()).hexdigest()
def get_cached_hash() -> Optional[str]:
"""获取缓存的哈希值"""
try:
if CACHE_FILE.exists():
return CACHE_FILE.read_text().strip()
except Exception:
pass
return None
def save_cache_hash(hash_value: str):
"""保存哈希值到缓存"""
try:
CACHE_FILE.write_text(hash_value)
except Exception as e:
logger.warning(f"无法保存文档缓存: {e}")
def generate_doc_html(openapi_spec: dict) -> str:
"""生成文档HTML内容"""
# 将OpenAPI规范内嵌到HTML中
spec_json = json.dumps(openapi_spec, ensure_ascii=False, indent=2)
api_version = openapi_spec.get("info", {}).get("version", "1.0.0")
api_title = openapi_spec.get("info", {}).get("title", "API文档")
html_content = f'''
{api_title} - 接口文档
'''
return html_content
def generate_loading_html() -> str:
"""生成加载中的HTML页面"""
return '''
生成文档中...
'''
def should_regenerate(openapi_spec: dict) -> bool:
"""检查是否需要重新生成文档"""
# 检查文档文件是否存在
if not DOC_FILE.exists():
return True
# 检查本地资源是否存在
vendor_dir = STATIC_DIR / "vendor"
if not (vendor_dir / "swagger-ui.css").exists() or not (vendor_dir / "swagger-ui-bundle.js").exists():
return True
# 比较哈希值
current_hash = get_openapi_hash(openapi_spec)
cached_hash = get_cached_hash()
return current_hash != cached_hash
def generate_docs(app) -> bool:
"""
生成API文档
Args:
app: FastAPI应用实例
Returns:
bool: 是否重新生成了文档
"""
try:
# 获取OpenAPI规范
openapi_spec = app.openapi()
# 检查是否需要重新生成
if not should_regenerate(openapi_spec):
logger.info("API文档未变化,使用缓存")
return False
logger.info("检测到API变化,正在生成文档...")
# 先写入加载页面
DOC_FILE.write_text(generate_loading_html(), encoding='utf-8')
# 生成文档HTML
doc_html = generate_doc_html(openapi_spec)
# 写入文档文件
DOC_FILE.write_text(doc_html, encoding='utf-8')
# 保存哈希值
current_hash = get_openapi_hash(openapi_spec)
save_cache_hash(current_hash)
logger.info("API文档生成完成")
return True
except Exception as e:
logger.error(f"生成API文档失败: {e}")
return False