- resources/help → A05_EduDetail/guide (git mv · 이력 남김) · 참조 고침(서버 · 시험 · B03 주석 · ui_checks)
- GET /api/help = 모든 페이지 [{page, title, items:[{key, title, text, version}]}] + admin
· 페이지 = ^[BDE]\d{2}$ 폴더 · `_` 파일 · 그림 뺌 · version = sha256 앞 16자 · 페이지 제목 = 00_설명서 제목
- GET /api/help/{page} 는 538-2 까지 살림 · B01 폴더 허용
- 시험: 글 내용 검사(명사형 · 400 자) 걷음 — 시스템 관리자가 화면에서 바로 고친 글이
지킴 시험을 빨갛게 하지 않게(요구서 359 4장 6) · 차례 · 판 · `_` 파일 · 나쁜 폴더 · 경로 탈출 시험 더함
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
"""[?] 사용자 가이드(54-2 · 538) — `A05_EduDetail/guide/<페이지>/*.md` 를 부를 때마다 읽어 줌.
|
|
|
|
사용자가 메모장으로 파일을 고치면 화면 고침 없이 다음에 열 때 바뀐 글이 보임.
|
|
파일 이름 순서 = 모달 왼쪽 차례 순서 · 제목 = 첫 `# ` 줄(없으면 파일 이름).
|
|
`_` 로 시작하는 파일 · 그림은 항목 아님.
|
|
판(version) = 파일 sha256 앞 16자 · 페이지 제목 = `00_설명서` 의 제목.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from common_util.common_util_auth import verify_session
|
|
|
|
router = APIRouter(prefix="/api/help", tags=["help"])
|
|
|
|
HELP_ROOT = Path(__file__).resolve().parent.parent / "A05_EduDetail" / "guide"
|
|
PAGE_RE = re.compile(r"^[BDE]\d{2}$")
|
|
|
|
|
|
def file_version(blob: bytes) -> str:
|
|
return hashlib.sha256(blob).hexdigest()[:16]
|
|
|
|
|
|
def read_help(page: str, root: Path = HELP_ROOT) -> list[dict[str, str]]:
|
|
if not PAGE_RE.match(page):
|
|
raise HTTPException(status_code=404, detail="없는 페이지")
|
|
folder = root / page
|
|
items = []
|
|
for path in sorted(folder.glob("*.md")) if folder.is_dir() else []:
|
|
if path.name.startswith("_"):
|
|
continue
|
|
blob = path.read_bytes()
|
|
text = blob.decode("utf-8-sig")
|
|
head = next((line[2:].strip() for line in text.splitlines() if line.startswith("# ")), "")
|
|
items.append(
|
|
{
|
|
"key": path.stem,
|
|
"title": head or path.stem,
|
|
"text": text,
|
|
"version": file_version(blob),
|
|
}
|
|
)
|
|
return items
|
|
|
|
|
|
def read_guide(root: Path = HELP_ROOT) -> list[dict[str, Any]]:
|
|
"""모든 페이지 — 폴더 이름 순 · 글 없는 폴더는 뺌."""
|
|
pages = []
|
|
for folder in sorted(p for p in root.iterdir() if p.is_dir() and PAGE_RE.match(p.name)):
|
|
items = read_help(folder.name, root)
|
|
if not items:
|
|
continue
|
|
manual = next((i for i in items if i["key"] == "00_설명서"), None)
|
|
pages.append(
|
|
{
|
|
"page": folder.name,
|
|
"title": manual["title"] if manual else folder.name,
|
|
"items": items,
|
|
}
|
|
)
|
|
return pages
|
|
|
|
|
|
@router.get("")
|
|
async def get_guide(session: dict[str, Any] = Depends(verify_session)) -> dict[str, Any]:
|
|
return {"pages": read_guide(), "admin": session.get("role") == "SYSTEM_ADMIN"}
|
|
|
|
|
|
@router.get("/{page}")
|
|
async def get_help(page: str) -> dict[str, list[dict[str, str]]]:
|
|
return {"items": read_help(page)}
|