Add default branding assets when missing

This commit is contained in:
2026-01-23 20:02:36 +13:00
parent cc79685eaf
commit 132e02e06e

View File

@@ -4,7 +4,7 @@ from typing import Any, Dict
from fastapi import APIRouter, HTTPException, UploadFile, File from fastapi import APIRouter, HTTPException, UploadFile, File
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from PIL import Image from PIL import Image, ImageDraw, ImageFont
router = APIRouter(prefix="/branding", tags=["branding"]) router = APIRouter(prefix="/branding", tags=["branding"])
@@ -23,8 +23,52 @@ def _resize_image(image: Image.Image, max_size: int = 300) -> Image.Image:
return 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") @router.get("/logo.png")
async def branding_logo() -> FileResponse: async def branding_logo() -> FileResponse:
if not os.path.exists(_LOGO_PATH):
_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": "public, max-age=300"}
@@ -33,6 +77,8 @@ async def branding_logo() -> FileResponse:
@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):
_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": "public, max-age=300"}