fix: add fallback for root-level static files in site-content handler

When an HTML page references static files (images, CSS, JS) via
relative URLs, the browser resolves them as root-level paths
(e.g. /image.jpg from page /site-name). Without a trailing slash
on the page URL, these resolve to /image.jpg instead of
/site-name/image.jpg.

The API's /site-content/{path} handler interprets the first path
segment as the site name, so /dule_yuan_15k.jpg was treated as a
site name lookup rather than a file within the dule-yuan site.

Added _find_static_file() fallback: when no site matches the first
path segment, search all site directories for a matching filename.
Only triggered for common static file extensions to avoid
ambiguous lookups.
This commit is contained in:
Junv (via Hermes)
2026-07-13 11:19:50 +10:00
parent a75e4d6637
commit 8aabf71f79
+34 -1
View File
@@ -127,6 +127,29 @@ def error_resp(status_code: int = 404) -> HTMLResponse:
return resp
# ── Static file fallback ─────────────────────────────
def _find_static_file(path: str):
"""Try to locate a static file (image, css, js, etc.) referenced
from a site's HTML as a root-relative URL (e.g. /image.jpg from page /site-name).
Searches all site directories for a matching filename.
Returns (site_name, sub_path) or None."""
filename = path.rsplit("/", 1)[-1]
if not filename or "." not in filename:
return None
ext = filename.rsplit(".", 1)[-1].lower()
if ext not in ("jpg", "jpeg", "png", "gif", "webp", "css", "js", "svg", "ico", "woff2", "pdf"):
return None
meta = _db.load_all()
for name in sorted(meta.get("sites", {}).keys()):
site_dir = DEMO_ROOT / name
if not site_dir.is_dir():
continue
candidate = site_dir / filename
if candidate.exists() and candidate.is_file():
return (name, filename)
return None
# ─── File Helpers (NFS safe — no rename) ─────────────
def sync_visibility(name: str):
"""Sync the on-disk index.html based on visibility mode."""
@@ -186,7 +209,17 @@ async def site_content(path: str = "", request: Request = None):
info = _db.get(site_name)
if not info:
return error_resp(404)
# ── Fallback: if the path doesn't match a site name, it might be
# a static file (image, css, js) referenced from a site's HTML.
# Try to find it in any site directory.
found = _find_static_file(path)
if found:
site_name, sub = found
info = _db.get(site_name)
if not info:
return error_resp(404)
else:
return error_resp(404)
visibility = info["visibility"]
user = check_auth(request)