[fastapi_vue] Further cleanup, wildcards for favicon.

This commit is contained in:
2026-02-06 18:09:22 +00:00
parent 008b3593db
commit fbed7873e5
+11 -12
View File
@@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import fnmatch
import logging import logging
import mimetypes import mimetypes
import time import time
@@ -44,9 +45,9 @@ class Frontend:
Features: Features:
- Automatic zstd compression for compressible files - Automatic zstd compression for compressible files
- ETag-based caching with configurable cache headers - ETag-based caching of immutable assets
- SPA (Single Page Application) support - SPA (Single Page Application) support
- Favicon handling from hashed assets - /favicon.ico with correct MIME type (image/png etc)
- Dev mode: indexes files but returns error directing to Vite server - Dev mode: indexes files but returns error directing to Vite server
Args: Args:
@@ -54,7 +55,7 @@ class Frontend:
index: Name of the index file (default: "index.html") index: Name of the index file (default: "index.html")
spa: Enable SPA mode - serve index.html for unknown routes (default: False) spa: Enable SPA mode - serve index.html for unknown routes (default: False)
cached: Path prefixes that are immutable (default: "/assets/") cached: Path prefixes that are immutable (default: "/assets/")
favicon: E.g. /assets/logo.png will serve /assets/logo*.png as /favicon.ico favicon: May use wildcards of full path. E.g. /assets/logo*.png matches logo.hash.png created by Vite
zstdlevel: Zstd compression level (default: 18) zstdlevel: Zstd compression level (default: 18)
""" """
@@ -142,15 +143,11 @@ class Frontend:
zstd = None zstd = None
www[name] = data, zstd, headers www[name] = data, zstd, headers
if self.favicon: if self.favicon:
p = PurePosixPath(self.favicon) if m := fnmatch.filter(www, self.favicon):
base = str(p.with_suffix("")) data, zstd, headers = www[m[0]]
ext = p.suffix if "immutable" in headers.get("cache-control", ""):
hashed_path = next( headers = {**headers, "cache-control": "max-age=86400"}
(path for path in www if path.startswith(base) and path.endswith(ext)), www["/favicon.ico"] = data, zstd, headers
None,
)
if hashed_path:
www["/favicon.ico"] = www[hashed_path]
if not www: if not www:
msg = "Frontend files missing, check your installation.\n" msg = "Frontend files missing, check your installation.\n"
www["/"] = ( www["/"] = (
@@ -193,6 +190,8 @@ class Frontend:
f"{self.base.name}: {len(self.www)} files in {1000 * duration:.1f} ms | " f"{self.base.name}: {len(self.www)} files in {1000 * duration:.1f} ms | "
f"zstd {len(compfiles)} files {1e-6 * raw:.2f}->{1e-6 * comp:.2f} MB ({ratio:.0f} %)" f"zstd {len(compfiles)} files {1e-6 * raw:.2f}->{1e-6 * comp:.2f} MB ({ratio:.0f} %)"
) )
if self.favicon and "/favicon.ico" not in self.www:
logger.warning("Favicon not found: %s", self.favicon)
def route(self, app: FastAPI, mount_path="/"): def route(self, app: FastAPI, mount_path="/"):
"""Register frontend routes with a FastAPI app. """Register frontend routes with a FastAPI app.