From 87b16b71441cf3acd2ee042cdd8e53a60006473e Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sat, 22 Aug 2026 12:12:58 +0000 Subject: [PATCH] Implement /robots.txt and /sitemap.xml. Update dev proxy to all-by-default. --- frontend/vite.config.js | 9 ++++----- pagerite/app.py | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/frontend/vite.config.js b/frontend/vite.config.js index f84798f..da9bd24 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -7,11 +7,10 @@ import vueDevTools from 'vite-plugin-vue-devtools' const backendUrl = process.env.PAGERITE_BACKEND_URL || 'http://localhost:3200' -// Proxy content pages (/slug, /path/to/slug) to the FastAPI backend in dev. -// Excludes Vite internals (/@..., /src, /node_modules, /__...) and the -// backend's /_ prefix. /_api, /_f, /_themes and the /_a analytics ping are -// handled by the fastapi-vue plugin. -const CONTENT_PROXY = '^\\/(?!_|@|src|node_modules|__)(?:[^./?]+(?:\\/[^./?]+)*)?(?:\\?.*)?$' +// Proxy everything except Vite's own dev-time paths and the backend machinery +// to the FastAPI backend in dev. /_api, /_f, /_themes and /_a are handled by +// the fastapi-vue plugin, and /@..., /src, /node_modules, /__... stay with Vite. +const CONTENT_PROXY = '^(?!/_|/@|/src|/node_modules|/__).*$' // https://vite.dev/config/ export default defineConfig({ diff --git a/pagerite/app.py b/pagerite/app.py index 99c680c..86c271d 100644 --- a/pagerite/app.py +++ b/pagerite/app.py @@ -1008,32 +1008,54 @@ async def front_page(request: Request) -> Response: async def sitemap(request: Request) -> Response: """Dynamically generate a sitemap of all published article pages.""" base = str(request.base_url).rstrip("/") - entries: list[tuple[str, datetime]] = [] + entries: list[tuple[str, datetime, int]] = [] - def walk(nodes: dict[str, Node], prefix: str) -> None: + def walk( + nodes: dict[str, Node], prefix: str, parent_has_content: bool = True + ) -> None: + first_content_slug = next( + ( + slug + for slug, node in sorted_nodes(nodes) + if node.published and node.content is not None + ), + None, + ) for slug, node in sorted_nodes(nodes): path = f"{prefix}/{slug}" if prefix else slug + depth = path.count("/") if path else 0 + if ( + not parent_has_content + and slug == first_content_slug + and node.published + and node.content is not None + and depth > 0 + ): + depth -= 1 if node.published and node.content is not None: - entries.append((path, node.modified)) - walk(node.children, path) + entries.append((path, node.modified, depth)) + if node.children: + walk(node.children, path, node.content is not None) walk(data.menu, "") - def priority(path: str) -> float: - return 1.0 if path == "" else max(0.1, 0.8 - path.count("/") * 0.2) + def priority(depth: int) -> float: + return max(0.1, 1.0 - depth * 0.2) lines = [ '', '', ] - for path, modified in entries: + for path, modified, depth in entries: loc = xml_escape(f"{base}/{path}" if path else base) - lastmod = modified.astimezone(UTC).isoformat() + lastmod = ( + modified.astimezone(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z") + ) lines.append( f" " f"{loc}" f"{lastmod}" - f"{priority(path):.1f}" + f"{priority(depth):.1f}" f"" ) lines.append("")