- Add api.upload() to client.ts that passes FormData without setting Content-Type, letting the browser set multipart/form-data with the correct boundary (was causing 422 on the upload endpoint) - Use api.upload() instead of api.post() for avatar file upload - Update DiceBear URLs from v6 to 9.x in both frontend and backend Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""Avatar generation service using DiceBear API."""
|
|
|
|
from typing import Optional
|
|
import httpx
|
|
from rehearsalhub.db.models import Member
|
|
|
|
|
|
class AvatarService:
|
|
"""Service for generating and managing user avatars."""
|
|
|
|
def __init__(self):
|
|
self.base_url = "https://api.dicebear.com/9.x"
|
|
|
|
async def generate_avatar_url(self, seed: str, style: str = "identicon") -> str:
|
|
"""Generate a DiceBear avatar URL for the given seed.
|
|
|
|
Args:
|
|
seed: Unique identifier (user ID, email, etc.)
|
|
style: Avatar style (default: identicon)
|
|
|
|
Returns:
|
|
URL to the generated avatar
|
|
"""
|
|
# Clean the seed for URL usage
|
|
clean_seed = seed.replace("-", "").replace("_", "")
|
|
|
|
# Construct DiceBear URL
|
|
return f"{self.base_url}/{style}/svg?seed={clean_seed}&backgroundType=gradientLinear&size=128"
|
|
|
|
async def generate_default_avatar(self, member: Member) -> str:
|
|
"""Generate a default avatar for a member using their ID as seed.
|
|
|
|
Args:
|
|
member: Member object
|
|
|
|
Returns:
|
|
URL to the generated avatar
|
|
"""
|
|
return await self.generate_avatar_url(str(member.id))
|
|
|
|
async def get_avatar_url(self, member: Member) -> Optional[str]:
|
|
"""Get the avatar URL for a member, generating default if none exists.
|
|
|
|
Args:
|
|
member: Member object
|
|
|
|
Returns:
|
|
Avatar URL or None
|
|
"""
|
|
if member.avatar_url:
|
|
return member.avatar_url
|
|
|
|
# Generate default avatar if none exists
|
|
return await self.generate_default_avatar(member)
|