Add default branding assets when missing
This commit is contained in:
@@ -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"}
|
||||||
|
|||||||
Reference in New Issue
Block a user