From d50093e893126d0f5f4b658b845fc558b1a83ce4 Mon Sep 17 00:00:00 2001 From: JiriUhlir <149317995+JiriUhlir@users.noreply.github.com> Date: Mon, 3 Aug 2026 12:04:13 +0200 Subject: [PATCH] docs read fix --- app/repo_docs.py | 56 +++++++++++++++++++++++++++++++---------- app/static/portal.js | 4 +++ app/templates/layout.py | 24 ++++++++++++++++-- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/app/repo_docs.py b/app/repo_docs.py index 57adef0..0aed431 100644 --- a/app/repo_docs.py +++ b/app/repo_docs.py @@ -32,6 +32,13 @@ MAX_DOC_FILES = 100 MAX_DOC_BYTES = 300_000 REQUEST_TIMEOUT = 8 +# Gitea si u stromu repozitáře vynucuje vlastní stránkování (api.DEFAULT_PAGING_NUM, ve +# výchozím nastavení 30 položek) a náš per_page si klidně sníží. Strom vrací VŠECHNY soubory, +# ne jen *.md, takže bez dotažení dalších stránek by se dokumentace ve větším repu vůbec +# nenašla. Proto čteme stránku po stránce, dokud Gitea hlásí truncated. +TREE_PAGE_SIZE = 100 +MAX_TREE_PAGES = 50 + class RepoDocsError(RuntimeError): """Dokumentaci se nepodařilo načíst (chybí token, Gitea je nedostupná, repo neexistuje).""" @@ -65,16 +72,31 @@ def list_markdown_files(repo: str, branch: str = "") -> list[dict]: raise RepoDocsError("Služba nemá evidovaný název repozitáře.") ref = (branch or "").strip() or _default_branch(repo) - payload = _request_json(f"/api/v1/repos/{quote(gitea_org())}/{quote(repo)}/git/trees/{quote(ref, safe='')}?recursive=true&per_page=1000") + tree_url = f"/api/v1/repos/{quote(gitea_org())}/{quote(repo)}/git/trees/{quote(ref, safe='')}" files = [] - for entry in payload.get("tree") or []: - if entry.get("type") != "blob": - continue - path = entry.get("path") or "" - if not is_markdown_path(path): - continue - files.append({"path": path, "size": int(entry.get("size") or 0)}) + seen_entries = 0 + for page in range(1, MAX_TREE_PAGES + 1): + payload = _request_json(f"{tree_url}?recursive=true&per_page={TREE_PAGE_SIZE}&page={page}") + entries = payload.get("tree") or [] + if not entries: + break + + seen_entries += len(entries) + for entry in entries: + if entry.get("type") != "blob": + continue + path = entry.get("path") or "" + if not is_markdown_path(path): + continue + files.append({"path": path, "size": int(entry.get("size") or 0)}) + + total_count = int(payload.get("total_count") or 0) + if not payload.get("truncated") or (total_count and seen_entries >= total_count): + break + if len(files) >= MAX_DOC_FILES: + logger.info("Repozitář %s má více než %s souborů *.md, seznam je zkrácený.", repo, MAX_DOC_FILES) + break files.sort(key=lambda item: (item["path"].count("/"), item["path"].lower())) return files[:MAX_DOC_FILES] @@ -105,19 +127,23 @@ def read_markdown(repo: str, path: str, branch: str = "") -> str: def render_markdown(text: str, repo: str) -> str: """Vyrenderuje markdown do HTML přes Gitea API. - Portál nemá markdown knihovnu a Gitea render umí a zároveň si výstupní HTML sanitizuje. - Když render selže, vrátíme obsah jako neformátovaný (escapovaný) text - náhled tak - funguje i při výpadku render endpointu. + Portál nemá markdown knihovnu, Gitea render umí a zároveň si výstupní HTML sanitizuje. + Náhled ale nesmí skončit prázdný: když render selže NEBO vrátí prázdné tělo (Gitea to + dělá i bez chybové hlášky, např. když si nepřečte pole text), zobrazíme obsah souboru + jako neformátovaný escapovaný text. """ + if not text.strip(): + return '
Soubor je prázdný.
' + payload = json.dumps( { "text": text, "mode": "gfm", "context": f"/{gitea_org()}/{repo}", - "wiki": False, } ).encode("utf-8") + rendered = "" try: rendered = _request_bytes( "/api/v1/markdown", @@ -126,11 +152,15 @@ def render_markdown(text: str, repo: str) -> str: accept="text/html", content_type="application/json", ).decode("utf-8", errors="replace") - return f'{html.escape(text)}'
+ return f'