Enable dynamic docs discovery

This commit is contained in:
2025-12-25 23:15:27 +01:00
parent 5dc2e3a88c
commit 6a9b42850a

View File

@@ -32,34 +32,67 @@ from config import get_settings
@router.get("/docs", response_class=HTMLResponse)
async def list_docs(req: Request):
"""
List available documentation files.
List available documentation files dynamically.
"""
available = []
base_path = _get_docs_base_path()
for filename, title in ALLOWED_DOCS.items():
# Check root and subdirectories (1 level deep)
full_path = _find_file(base_path, filename)
if full_path:
available.append({"name": title, "file": filename})
# 1. Pinned Docs (from whitelist)
pinned = []
# 2. All other docs (dynamic scan)
library = []
# helper to check if already in pinned
pinned_filenames = set()
for filename, title in ALLOWED_DOCS.items():
full_path = _find_file(base_path, filename) # Helper now needs to look deeper?
if full_path:
pinned.append({"name": title, "file": filename})
pinned_filenames.add(filename)
# Scan for all .md files recursively
if os.path.exists(base_path):
for root, dirs, files in os.walk(base_path):
for file in files:
if file.endswith(".md"):
# Calculate relative path or just filename.
# If we use just filename, distinct files with same name in diff folders conflict.
# Current view_doc takes {filename}. Let's assume filenames are unique enough or we handle paths.
# For now, let's just show them if they aren't pinned.
if file not in pinned_filenames:
# Try to get a nice name (Title from first line?) or filename
name = file.replace(".md", "").replace("-", " ").title()
library.append({"name": name, "file": file})
# Combine or separate? Let's just return all in 'docs' for now to fit the template expectation.
# The template iterates 'docs'.
all_docs = pinned + sorted(library, key=lambda x: x["name"])
return req.app.state.render(
req,
"pages/docs_index.html",
docs=available,
docs=all_docs,
page_title="Dokumentation"
)
@router.get("/docs/{filename}", response_class=HTMLResponse)
async def view_doc(req: Request, filename: str):
"""
Render a specific markdown file.
Render a specific markdown file. Now supports any MD file found.
"""
if filename not in ALLOWED_DOCS:
raise HTTPException(404, "File not found or not allowed.")
# Security: basic check to prevent directory traversal outside allowable scope
if ".." in filename or "/" in filename: # filename param is usually just the last part if caught by path param?
# Actually FastAPI path param "{filename}" stops at slashes unless defined as "{path:path}".
# So 'filename' here is just the leaf name.
pass
base_path = _get_docs_base_path()
file_path = _find_file(base_path, filename)
# Use recursive find
file_path = _find_file_recursive(base_path, filename)
if not file_path:
raise HTTPException(404, "File not on server.")
@@ -68,16 +101,18 @@ async def view_doc(req: Request, filename: str):
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Convert Markdown to HTML
html_content = markdown.markdown(
content,
extensions=['tables', 'fenced_code', 'nl2br']
)
# Determine Title
title = ALLOWED_DOCS.get(filename, filename.replace(".md", "").replace("-", " ").title())
return req.app.state.render(
req,
"pages/doc_viewer.html",
doc_title=ALLOWED_DOCS[filename],
doc_title=title,
doc_content=html_content,
filename=filename
)
@@ -100,17 +135,19 @@ def _get_docs_base_path():
return base_path
def _find_file(base_path, filename):
"""Find file in base_path or immediate subdirectories."""
# 1. Direct match
"""Legacy helper: find in base or level 1 subdirs."""
return _find_file_recursive(base_path, filename)
def _find_file_recursive(base_path, filename):
"""Find file recursively in base_path."""
# 1. Direct check
direct = os.path.join(base_path, filename)
if os.path.exists(direct):
if os.path.exists(direct) and os.path.isfile(direct):
return direct
# 2. Check subdirectories (max depth 1)
if os.path.isdir(base_path):
for entry in os.scandir(base_path):
if entry.is_dir():
sub_path = os.path.join(entry.path, filename)
if os.path.exists(sub_path):
return sub_path
# 2. Walk
for root, dirs, files in os.walk(base_path):
if filename in files:
return os.path.join(root, filename)
return None