Update admin theme/layout and refresh README details.
Align the project baseline with the latest admin interface styling and layout structure while documenting setup and usage updates in README.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from io import BytesIO
|
||||
from uuid import uuid4
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.storage import upload_object
|
||||
|
||||
ALLOWED_MIME = {
|
||||
"image/jpeg": ".jpg",
|
||||
"image/png": ".png",
|
||||
"image/webp": ".webp",
|
||||
}
|
||||
|
||||
FORMAT_TO_MIME = {
|
||||
"JPEG": "image/jpeg",
|
||||
"PNG": "image/png",
|
||||
"WEBP": "image/webp",
|
||||
}
|
||||
|
||||
|
||||
class AvatarValidationError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def validate_and_process_avatar(content: bytes) -> tuple[str, bytes]:
|
||||
if len(content) > settings.avatar_max_bytes:
|
||||
raise AvatarValidationError("FILE_TOO_LARGE")
|
||||
if not content:
|
||||
raise AvatarValidationError("INVALID_IMAGE")
|
||||
|
||||
try:
|
||||
with Image.open(BytesIO(content)) as image:
|
||||
image.verify()
|
||||
with Image.open(BytesIO(content)) as image:
|
||||
mime = FORMAT_TO_MIME.get(image.format or "")
|
||||
if mime not in ALLOWED_MIME:
|
||||
raise AvatarValidationError("INVALID_MIME")
|
||||
|
||||
buffer = BytesIO()
|
||||
if mime == "image/jpeg":
|
||||
rgb = image.convert("RGB")
|
||||
rgb.save(buffer, format="JPEG", quality=85, optimize=True)
|
||||
elif mime == "image/png":
|
||||
image.save(buffer, format="PNG", optimize=True)
|
||||
else:
|
||||
image.save(buffer, format="WEBP", quality=85, method=6)
|
||||
return mime, buffer.getvalue()
|
||||
except AvatarValidationError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise AvatarValidationError("INVALID_IMAGE") from exc
|
||||
|
||||
|
||||
def upload_user_avatar(user_id: str, content: bytes) -> str:
|
||||
mime, processed = validate_and_process_avatar(content)
|
||||
extension = ALLOWED_MIME[mime]
|
||||
key = f"avatars/{user_id}/{uuid4()}{extension}"
|
||||
upload_object(key, processed, mime)
|
||||
return f"/api/v1/media/files/{key}"
|
||||
Reference in New Issue
Block a user