103 lines
3.6 KiB
Python
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}
|