fix: add favicon (SVG + PNG + ICO), fix static MIME types (#613)
Squash-merges PR #613. Adds favicon to the app (was missing entirely — blank tab icon). 1371 tests passing, QA harness green. Review by independent agent (see PR comments). Follow-up commit addresses all three reviewer notes: hoisted _STATIC_MIME to module scope, fixed charset=utf-8 being appended to binary MIME types, confirmed correct MIME types on all three favicon formats. Co-authored-by: tiansiyuan <tiansiyuan@users.noreply.github.com>
This commit is contained in:
@@ -420,8 +420,19 @@ def handle_get(handler, parsed) -> bool:
|
||||
return j(handler, {"auth_enabled": is_auth_enabled(), "logged_in": logged_in})
|
||||
|
||||
if parsed.path == "/favicon.ico":
|
||||
handler.send_response(204)
|
||||
handler.end_headers()
|
||||
static_root = Path(__file__).parent.parent / "static"
|
||||
ico_path = (static_root / "favicon.ico").resolve()
|
||||
if ico_path.exists() and ico_path.is_file():
|
||||
data = ico_path.read_bytes()
|
||||
handler.send_response(200)
|
||||
handler.send_header("Content-Type", "image/x-icon")
|
||||
handler.send_header("Content-Length", str(len(data)))
|
||||
handler.send_header("Cache-Control", "public, max-age=86400")
|
||||
handler.end_headers()
|
||||
handler.wfile.write(data)
|
||||
else:
|
||||
handler.send_response(204)
|
||||
handler.end_headers()
|
||||
return True
|
||||
|
||||
if parsed.path == "/health":
|
||||
@@ -1313,6 +1324,25 @@ def handle_post(handler, parsed) -> bool:
|
||||
|
||||
# ── GET route helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
# MIME types for static file serving. Hoisted to module scope to avoid
|
||||
# rebuilding the dict on every request.
|
||||
_STATIC_MIME = {
|
||||
"css": "text/css",
|
||||
"js": "application/javascript",
|
||||
"html": "text/html",
|
||||
"svg": "image/svg+xml",
|
||||
"png": "image/png",
|
||||
"jpg": "image/jpeg",
|
||||
"jpeg": "image/jpeg",
|
||||
"ico": "image/x-icon",
|
||||
"gif": "image/gif",
|
||||
"webp": "image/webp",
|
||||
"woff": "font/woff",
|
||||
"woff2": "font/woff2",
|
||||
}
|
||||
# MIME types that are text-based and should carry charset=utf-8
|
||||
_TEXT_MIME_TYPES = {"text/css", "application/javascript", "text/html", "image/svg+xml", "text/plain"}
|
||||
|
||||
|
||||
def _serve_static(handler, parsed):
|
||||
static_root = (Path(__file__).parent.parent / "static").resolve()
|
||||
@@ -1326,11 +1356,10 @@ def _serve_static(handler, parsed):
|
||||
if not static_file.exists() or not static_file.is_file():
|
||||
return j(handler, {"error": "not found"}, status=404)
|
||||
ext = static_file.suffix.lower()
|
||||
ct = {"css": "text/css", "js": "application/javascript", "html": "text/html"}.get(
|
||||
ext.lstrip("."), "text/plain"
|
||||
)
|
||||
ct = _STATIC_MIME.get(ext.lstrip("."), "text/plain")
|
||||
ct_header = f"{ct}; charset=utf-8" if ct in _TEXT_MIME_TYPES else ct
|
||||
handler.send_response(200)
|
||||
handler.send_header("Content-Type", f"{ct}; charset=utf-8")
|
||||
handler.send_header("Content-Type", ct_header)
|
||||
handler.send_header("Cache-Control", "no-store")
|
||||
raw = static_file.read_bytes()
|
||||
handler.send_header("Content-Length", str(len(raw)))
|
||||
|
||||
Reference in New Issue
Block a user