2 Commits

Author SHA1 Message Date
22f90b7e07 Serve bundled branding assets by default 2026-01-25 18:20:30 +13:00
57a4883931 Seed branding logo from bundled assets 2026-01-25 18:01:54 +13:00
4 changed files with 35 additions and 11 deletions

View File

@@ -1 +1 @@
251260452 251261817

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -11,6 +11,10 @@ router = APIRouter(prefix="/branding", tags=["branding"])
_BRANDING_DIR = os.path.join(os.getcwd(), "data", "branding") _BRANDING_DIR = os.path.join(os.getcwd(), "data", "branding")
_LOGO_PATH = os.path.join(_BRANDING_DIR, "logo.png") _LOGO_PATH = os.path.join(_BRANDING_DIR, "logo.png")
_FAVICON_PATH = os.path.join(_BRANDING_DIR, "favicon.ico") _FAVICON_PATH = os.path.join(_BRANDING_DIR, "favicon.ico")
_BUNDLED_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "assets", "branding"))
_BUNDLED_LOGO_PATH = os.path.join(_BUNDLED_DIR, "logo.png")
_BUNDLED_FAVICON_PATH = os.path.join(_BUNDLED_DIR, "favicon.ico")
_BRANDING_SOURCE = os.getenv("BRANDING_SOURCE", "bundled").lower()
def _ensure_branding_dir() -> None: def _ensure_branding_dir() -> None:
@@ -41,6 +45,18 @@ def _ensure_default_branding() -> None:
if os.path.exists(_LOGO_PATH) and os.path.exists(_FAVICON_PATH): if os.path.exists(_LOGO_PATH) and os.path.exists(_FAVICON_PATH):
return return
_ensure_branding_dir() _ensure_branding_dir()
if not os.path.exists(_LOGO_PATH) and os.path.exists(_BUNDLED_LOGO_PATH):
try:
with open(_BUNDLED_LOGO_PATH, "rb") as source, open(_LOGO_PATH, "wb") as target:
target.write(source.read())
except OSError:
pass
if not os.path.exists(_FAVICON_PATH) and os.path.exists(_BUNDLED_FAVICON_PATH):
try:
with open(_BUNDLED_FAVICON_PATH, "rb") as source, open(_FAVICON_PATH, "wb") as target:
target.write(source.read())
except OSError:
pass
if not os.path.exists(_LOGO_PATH): if not os.path.exists(_LOGO_PATH):
image = Image.new("RGBA", (300, 300), (12, 18, 28, 255)) image = Image.new("RGBA", (300, 300), (12, 18, 28, 255))
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
@@ -65,24 +81,32 @@ def _ensure_default_branding() -> None:
favicon.save(_FAVICON_PATH, format="ICO") favicon.save(_FAVICON_PATH, format="ICO")
def _resolve_branding_paths() -> tuple[str, str]:
if _BRANDING_SOURCE == "data":
_ensure_default_branding()
return _LOGO_PATH, _FAVICON_PATH
if os.path.exists(_BUNDLED_LOGO_PATH) and os.path.exists(_BUNDLED_FAVICON_PATH):
return _BUNDLED_LOGO_PATH, _BUNDLED_FAVICON_PATH
_ensure_default_branding()
return _LOGO_PATH, _FAVICON_PATH
@router.get("/logo.png") @router.get("/logo.png")
async def branding_logo() -> FileResponse: async def branding_logo() -> FileResponse:
if not os.path.exists(_LOGO_PATH): logo_path, _ = _resolve_branding_paths()
_ensure_default_branding() if not os.path.exists(logo_path):
if not os.path.exists(_LOGO_PATH):
raise HTTPException(status_code=404, detail="Logo not found") raise HTTPException(status_code=404, detail="Logo not found")
headers = {"Cache-Control": "public, max-age=300"} headers = {"Cache-Control": "no-store"}
return FileResponse(_LOGO_PATH, media_type="image/png", headers=headers) return FileResponse(logo_path, media_type="image/png", headers=headers)
@router.get("/favicon.ico") @router.get("/favicon.ico")
async def branding_favicon() -> FileResponse: async def branding_favicon() -> FileResponse:
if not os.path.exists(_FAVICON_PATH): _, favicon_path = _resolve_branding_paths()
_ensure_default_branding() if not os.path.exists(favicon_path):
if not os.path.exists(_FAVICON_PATH):
raise HTTPException(status_code=404, detail="Favicon not found") raise HTTPException(status_code=404, detail="Favicon not found")
headers = {"Cache-Control": "public, max-age=300"} headers = {"Cache-Control": "no-store"}
return FileResponse(_FAVICON_PATH, media_type="image/x-icon", headers=headers) return FileResponse(favicon_path, media_type="image/x-icon", headers=headers)
async def save_branding_image(file: UploadFile) -> Dict[str, Any]: async def save_branding_image(file: UploadFile) -> Dict[str, Any]: