guardia-itsm/routers/manual_updater.py

103 lines
3.6 KiB
Python

"""성장일지 — 매뉴얼 자동 업데이트 (라우터 변경 감지 → MD 재생성)."""
from __future__ import annotations
import logging
from datetime import datetime
from pathlib import Path
from fastapi import APIRouter, BackgroundTasks, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from core.auth import get_current_user, require_admin_role
from database import get_db
from models import User
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/manual-update", tags=["성장일지-매뉴얼"])
MANUAL_DIR = Path("/opt/guardia/workspace/guardia-docs")
ITSM_DIR = Path("/opt/guardia/workspace/guardia-itsm")
def _collect_router_endpoints() -> list[dict]:
"""routers/ 디렉토리에서 엔드포인트 목록 추출."""
endpoints = []
router_dir = ITSM_DIR / "routers"
if not router_dir.exists():
return endpoints
for py_file in sorted(router_dir.glob("*.py")):
if py_file.name.startswith("_"):
continue
try:
content = py_file.read_text(encoding="utf-8")
for line in content.splitlines():
stripped = line.strip()
for method in ("@router.get", "@router.post", "@router.put", "@router.delete", "@router.patch"):
if stripped.startswith(method):
path = stripped.split('"')[1] if '"' in stripped else stripped.split("'")[1] if "'" in stripped else "?"
endpoints.append({"file": py_file.name, "method": method.split(".")[1].upper(), "path": path})
except Exception:
pass
return endpoints
def _build_api_md(endpoints: list[dict]) -> str:
now = datetime.now().strftime("%Y-%m-%d %H:%M")
lines = [
f"# GUARDiA ITSM API 레퍼런스\n\n> 자동 생성: {now}\n",
f"{len(endpoints)}개 엔드포인트\n",
"| 파일 | Method | Path |",
"|------|--------|------|",
]
for ep in endpoints:
lines.append(f"| {ep['file']} | {ep['method']} | `{ep['path']}` |")
return "\n".join(lines)
async def _do_update():
endpoints = _collect_router_endpoints()
md = _build_api_md(endpoints)
out_file = MANUAL_DIR / "api-reference-auto.md"
try:
out_file.parent.mkdir(parents=True, exist_ok=True)
out_file.write_text(md, encoding="utf-8")
logger.info("매뉴얼 업데이트 완료: %s (%d개)", out_file, len(endpoints))
except Exception as e:
logger.error("매뉴얼 업데이트 실패: %s", e)
return len(endpoints)
@router.post("/run")
async def run_update(
background_tasks: BackgroundTasks,
user: User = Depends(require_admin_role),
):
"""라우터 분석 → api-reference-auto.md 재생성."""
background_tasks.add_task(_do_update)
return {"ok": True, "message": "매뉴얼 업데이트 시작됨 (백그라운드)"}
@router.get("/preview")
async def preview(user: User = Depends(get_current_user)):
"""현재 엔드포인트 목록 미리보기 (저장 없음)."""
endpoints = _collect_router_endpoints()
return {
"total": len(endpoints),
"endpoints": endpoints[:50],
"note": "최대 50개 미리보기",
}
@router.get("/status")
async def status(user: User = Depends(get_current_user)):
"""마지막 자동 업데이트 파일 정보."""
out_file = MANUAL_DIR / "api-reference-auto.md"
if out_file.exists():
stat = out_file.stat()
return {
"exists": True,
"path": str(out_file),
"size_kb": round(stat.st_size / 1024, 1),
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
}
return {"exists": False}