From 6322e54ba1c63704040a7365a0a05b9961eb1e93 Mon Sep 17 00:00:00 2001 From: Zaine Date: Thu, 7 May 2026 16:32:09 +0100 Subject: [PATCH] changes 3 --- authoring_server.py | 56 +++++++++++++++++++++++++--------- posts/posts-list.org | 2 +- tests/test_authoring_server.py | 17 +++++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/authoring_server.py b/authoring_server.py index 50d8932..c1e2191 100755 --- a/authoring_server.py +++ b/authoring_server.py @@ -399,20 +399,30 @@ def list_pages() -> list[dict[str, Any]]: pages = [] org_paths = [] if ROOT.exists(): - for path in ROOT.rglob("*.org"): - rel_parts = path.relative_to(ROOT).parts - if any(part in EXCLUDED_CONTENT_DIR_NAMES for part in rel_parts): - continue - org_paths.append(path) - md_paths = list(LIMA_DIR.rglob("*.md")) if LIMA_DIR.exists() else [] + try: + for path in ROOT.rglob("*.org"): + try: + rel_parts = path.relative_to(ROOT).parts + except ValueError: + continue + if any(part in EXCLUDED_CONTENT_DIR_NAMES for part in rel_parts): + continue + org_paths.append(path) + except OSError: + org_paths = [] + try: + md_paths = list(LIMA_DIR.rglob("*.md")) if LIMA_DIR.exists() else [] + except OSError: + md_paths = [] for path in sorted(org_paths + md_paths): if path.name in GENERATED_CONTENT_NAMES or "sync-conflict" in path.name: continue try: page = read_page(path) - except UnicodeDecodeError: + parsed = parse_org_datetime(page.date) + timestamp = parsed.timestamp() if parsed else path.stat().st_mtime + except (OSError, UnicodeDecodeError, ValueError): continue - parsed = parse_org_datetime(page.date) pages.append( { "path": page.path, @@ -422,7 +432,7 @@ def list_pages() -> list[dict[str, Any]]: "tags": page.tags, "date": page.date, "format": page.format, - "timestamp": parsed.timestamp() if parsed else path.stat().st_mtime, + "timestamp": timestamp, } ) return sorted(pages, key=lambda item: item["timestamp"], reverse=True) @@ -850,7 +860,12 @@ APP_HTML = r""" async function api(path, options = {}) { const res = await fetch(path, { headers: { "Content-Type": "application/json" }, ...options }); - const data = await res.json(); + let data = null; + try { + data = await res.clone().json(); + } catch (_err) { + data = { error: await res.text().catch(() => "") }; + } if (!res.ok) throw new Error(data.error || "Request failed"); return data; } @@ -1014,13 +1029,23 @@ APP_HTML = r""" state.pages = await api("/api/pages"); renderPages(); } catch (err) { - pagesBox.innerHTML = `
${html(err.message)}
`; + if (state.pages.length) { + renderPages(); + saveMessage.textContent = `Could not refresh file list: ${err.message}`; + } else { + pagesBox.innerHTML = `
${html(err.message)}
`; + } } } async function refreshBuild() { - state.build = await api("/api/build"); - renderStatus(); + try { + state.build = await api("/api/build"); + renderStatus(); + } catch (err) { + statusBox.className = "status fail"; + statusBox.textContent = `Could not refresh build status: ${err.message}`; + } await refreshPages(); } @@ -1323,7 +1348,10 @@ class Handler(BaseHTTPRequestHandler): self.wfile.write(body) return if parsed.path == "/api/pages": - self.send_json(list_pages()) + try: + self.send_json(list_pages()) + except Exception as exc: + self.send_json({"error": str(exc)}, HTTPStatus.INTERNAL_SERVER_ERROR) return if parsed.path == "/api/page": query = parse_qs(parsed.query) diff --git a/posts/posts-list.org b/posts/posts-list.org index 15e74bd..025867a 100755 --- a/posts/posts-list.org +++ b/posts/posts-list.org @@ -4,7 +4,7 @@ See the categories: @@html:Categories@@ * Posts: -- [[file:career/career-list.org][Career List]] @@html:07-05-2026 16:28@@ +- [[file:career/career-list.org][Career List]] @@html:07-05-2026 16:31@@ - [[file:posts-list.sync-conflict-20260417-233432-VT6366A.org][Posts List]] @@html:14-04-2026 16:36@@ - [[file:career/ha-dr.org][High Availability, Disaster Recovery and Business Continuity]] @@html:11-03-2026 17:18@@ @@html:@@ @@html:@@ - [[file:career/javascript.org][Understands the Javascript language]] @@html:11-03-2026 16:52@@ @@html:@@ @@html:@@ diff --git a/tests/test_authoring_server.py b/tests/test_authoring_server.py index a42c719..7ec39e8 100755 --- a/tests/test_authoring_server.py +++ b/tests/test_authoring_server.py @@ -172,6 +172,23 @@ class PageRenderingTests(AuthoringServerTestCase): self.assertEqual(set(paths), {"blogs/keep.org", "home/notes.org", "lima/index.md"}) + def test_list_pages_skips_files_that_fail_to_read(self): + good = self.blogs / "keep.org" + bad = self.blogs / "bad.org" + good.write_text("#+TITLE: Keep\n#+DATE: <2026-05-07 Thu 12:00>\n", encoding="utf-8") + bad.write_text("#+TITLE: Bad\n", encoding="utf-8") + original_read_page = server.read_page + + def read_page(path): + if path == bad: + raise OSError("file disappeared") + return original_read_page(path) + + with mock.patch.object(server, "read_page", side_effect=read_page): + paths = [page["path"] for page in server.list_pages()] + + self.assertEqual(paths, ["blogs/keep.org"]) + def test_relative_asset_path_is_calculated_from_lima_page_directory(self): self.assertEqual( server.relative_asset_path("lima/family/update.md", "assets/images/hzone/pic.png"),