mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2026-04-05 20:57:16 +02:00
feat: web documentation portal with contextual help links and consistent page headers (#67)
This commit is contained in:
parent
46637cf27f
commit
13f6fa5dbf
44 changed files with 2232 additions and 140 deletions
115
code/web/routes/docs.py
Normal file
115
code/web/routes/docs.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
"""Routes for user documentation viewer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Request, HTTPException
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from code.web.services.docs_service import DocsService, NotFoundError, ServiceError
|
||||
from ..app import templates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/help", tags=["help"])
|
||||
|
||||
# Initialize service
|
||||
_docs_service = DocsService()
|
||||
|
||||
|
||||
def _is_docs_enabled() -> bool:
|
||||
"""Check if docs feature is enabled.
|
||||
|
||||
Returns:
|
||||
True if ENABLE_WEB_DOCS=1
|
||||
"""
|
||||
return os.getenv("ENABLE_WEB_DOCS", "1") == "1"
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse, name="docs_index")
|
||||
async def docs_index(request: Request):
|
||||
"""Display documentation index page.
|
||||
|
||||
Lists all available user guides with titles and descriptions.
|
||||
"""
|
||||
if not _is_docs_enabled():
|
||||
raise HTTPException(status_code=404, detail="Documentation not available")
|
||||
|
||||
try:
|
||||
guides = _docs_service.list_guides()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"docs/index.html",
|
||||
{
|
||||
"guides": guides,
|
||||
"page_title": "Documentation"
|
||||
}
|
||||
)
|
||||
|
||||
except ServiceError as e:
|
||||
logger.error(f"Failed to load docs index: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to load documentation")
|
||||
|
||||
|
||||
@router.get("/{guide_name}", response_class=HTMLResponse, name="docs_guide")
|
||||
async def docs_guide(request: Request, guide_name: str, reload: Optional[bool] = False):
|
||||
"""Display a specific documentation guide.
|
||||
|
||||
Args:
|
||||
guide_name: Name of guide (without .md extension)
|
||||
reload: Force reload from disk (admin/debug)
|
||||
"""
|
||||
if not _is_docs_enabled():
|
||||
raise HTTPException(status_code=404, detail="Documentation not available")
|
||||
|
||||
try:
|
||||
# Get metadata
|
||||
metadata = _docs_service.get_metadata(guide_name)
|
||||
|
||||
# Get rendered content (HTML + TOC)
|
||||
content = _docs_service.get_guide(guide_name, force_reload=bool(reload))
|
||||
|
||||
# Get all guides for sidebar navigation
|
||||
all_guides = _docs_service.list_guides()
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"docs/guide.html",
|
||||
{
|
||||
"guide_name": guide_name,
|
||||
"guide_title": metadata.title,
|
||||
"guide_description": metadata.description,
|
||||
"html_content": content.html,
|
||||
"toc_html": content.toc_html,
|
||||
"all_guides": all_guides,
|
||||
"page_title": metadata.title
|
||||
}
|
||||
)
|
||||
|
||||
except NotFoundError:
|
||||
raise HTTPException(status_code=404, detail=f"Guide not found: {guide_name}")
|
||||
except ServiceError as e:
|
||||
logger.error(f"Failed to load guide {guide_name}: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to load guide")
|
||||
|
||||
|
||||
@router.post("/invalidate", name="docs_invalidate")
|
||||
async def invalidate_cache(guide_name: Optional[str] = None):
|
||||
"""Invalidate documentation cache (admin/debug).
|
||||
|
||||
Args:
|
||||
guide_name: Specific guide to invalidate (None = all)
|
||||
"""
|
||||
if not _is_docs_enabled():
|
||||
raise HTTPException(status_code=404, detail="Documentation not available")
|
||||
|
||||
try:
|
||||
_docs_service.invalidate_guide(guide_name)
|
||||
return {"status": "ok", "invalidated": guide_name or "all"}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to invalidate cache: {e}")
|
||||
raise HTTPException(status_code=500, detail="Cache invalidation failed")
|
||||
Loading…
Add table
Add a link
Reference in a new issue