From 132e02e06e12992f16bcc64d12bf384e5cbf5fca Mon Sep 17 00:00:00 2001 From: Rephl3x Date: Fri, 23 Jan 2026 20:02:36 +1300 Subject: [PATCH] Add default branding assets when missing --- backend/app/routers/branding.py | 48 ++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/backend/app/routers/branding.py b/backend/app/routers/branding.py index d0c57e7..7034810 100644 --- a/backend/app/routers/branding.py +++ b/backend/app/routers/branding.py @@ -4,7 +4,7 @@ from typing import Any, Dict from fastapi import APIRouter, HTTPException, UploadFile, File from fastapi.responses import FileResponse -from PIL import Image +from PIL import Image, ImageDraw, ImageFont router = APIRouter(prefix="/branding", tags=["branding"]) @@ -23,8 +23,52 @@ def _resize_image(image: Image.Image, max_size: int = 300) -> Image.Image: return image +def _load_font(size: int) -> ImageFont.ImageFont: + candidates = [ + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + ] + for path in candidates: + if os.path.exists(path): + try: + return ImageFont.truetype(path, size) + except OSError: + continue + return ImageFont.load_default() + + +def _ensure_default_branding() -> None: + if os.path.exists(_LOGO_PATH) and os.path.exists(_FAVICON_PATH): + return + _ensure_branding_dir() + if not os.path.exists(_LOGO_PATH): + image = Image.new("RGBA", (300, 300), (12, 18, 28, 255)) + draw = ImageDraw.Draw(image) + font = _load_font(160) + text = "M" + box = draw.textbbox((0, 0), text, font=font) + text_w = box[2] - box[0] + text_h = box[3] - box[1] + draw.text( + ((300 - text_w) / 2, (300 - text_h) / 2 - 6), + text, + font=font, + fill=(255, 255, 255, 255), + ) + image.save(_LOGO_PATH, format="PNG") + if not os.path.exists(_FAVICON_PATH): + favicon = Image.open(_LOGO_PATH).copy() + favicon.thumbnail((64, 64)) + try: + favicon.save(_FAVICON_PATH, format="ICO", sizes=[(32, 32), (64, 64)]) + except OSError: + favicon.save(_FAVICON_PATH, format="ICO") + + @router.get("/logo.png") async def branding_logo() -> FileResponse: + if not os.path.exists(_LOGO_PATH): + _ensure_default_branding() if not os.path.exists(_LOGO_PATH): raise HTTPException(status_code=404, detail="Logo not found") headers = {"Cache-Control": "public, max-age=300"} @@ -33,6 +77,8 @@ async def branding_logo() -> FileResponse: @router.get("/favicon.ico") async def branding_favicon() -> FileResponse: + if not os.path.exists(_FAVICON_PATH): + _ensure_default_branding() if not os.path.exists(_FAVICON_PATH): raise HTTPException(status_code=404, detail="Favicon not found") headers = {"Cache-Control": "public, max-age=300"}