# Copyright (c) 2025-2026 马建军. All rights reserved.
# 专有软件 — 未经授权禁止复制、传播、转售。
# 详见 LICENSE.zh-CN.txt
"""将项目 docs 下的 Markdown 转为安全 HTML(无第三方依赖)。"""
from __future__ import annotations
import html
import re
from pathlib import Path
_DOCS_ROOT = Path(__file__).resolve().parent / "docs"
ALLOWED_DOCS: dict[str, str] = {
"risk-guide": "风控说明.md",
}
def docs_root() -> Path:
return _DOCS_ROOT
def read_doc(slug: str) -> tuple[str, str]:
"""返回 (title, raw_markdown)。"""
name = ALLOWED_DOCS.get(slug)
if not name:
raise FileNotFoundError(slug)
path = (_DOCS_ROOT / name).resolve()
if not path.is_file() or _DOCS_ROOT.resolve() not in path.parents:
raise FileNotFoundError(slug)
text = path.read_text(encoding="utf-8")
title = name
for line in text.splitlines():
s = line.strip()
if s.startswith("# "):
title = s[2:].strip()
break
return title, text
def _inline(text: str) -> str:
s = html.escape(text)
s = re.sub(r"\*\*(.+?)\*\*", r"\1", s)
s = re.sub(r"`([^`]+)`", r"\1", s)
s = re.sub(
r"\[([^\]]+)\]\(([^)]+)\)",
lambda m: _link_html(m.group(1), m.group(2)),
s,
)
return s
def _link_html(label: str, href: str) -> str:
h = html.escape(href)
lbl = _inline(label)
if href.startswith(("http://", "https://", "mailto:")):
return f'{lbl}'
if href.endswith(".md") or href.startswith("./"):
return f'{lbl}'
return f'{lbl}'
def render_markdown(text: str) -> str:
lines = text.splitlines()
out: list[str] = []
i = 0
in_ul = False
in_ol = False
def close_lists() -> None:
nonlocal in_ul, in_ol
if in_ul:
out.append("")
in_ul = False
if in_ol:
out.append("")
in_ol = False
while i < len(lines):
line = lines[i]
stripped = line.strip()
if not stripped:
close_lists()
i += 1
continue
if stripped == "---":
close_lists()
out.append("
{_inline(para)}
") close_lists() return "\n".join(out) def _render_table(rows: list[str]) -> str: if len(rows) < 2: return "" header = [c.strip() for c in rows[0].strip("|").split("|")] body_rows = rows[2:] if len(rows) > 2 and re.match(r"^[\|\s:-]+$", rows[1]) else rows[1:] parts = ["| {_inline(cell)} | ") parts.append("
|---|
| {_inline(cell)} | ") parts.append("