feat: add avatars to members

This commit is contained in:
sam 2024-03-30 04:33:23 +01:00
parent 437c93463d
commit 66a5dfb433
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
3 changed files with 60 additions and 3 deletions

View file

@ -2,6 +2,7 @@ from pydantic import Field
from quart import Blueprint, g from quart import Blueprint, g
from quart_schema import validate_request, validate_response from quart_schema import validate_request, validate_response
from foxnouns import tasks
from foxnouns.auth import require_auth from foxnouns.auth import require_auth
from foxnouns.db import Member from foxnouns.db import Member
from foxnouns.db.aio import async_session from foxnouns.db.aio import async_session
@ -54,4 +55,7 @@ async def create_member(data: MemberCreateModel):
# we have to do it manually. # we have to do it manually.
await member.awaitable_attrs.user await member.awaitable_attrs.user
if data.avatar:
tasks.process_member_avatar.delay(member.id, data.avatar)
return FullMemberModel.model_validate(member) return FullMemberModel.model_validate(member)

View file

@ -18,6 +18,8 @@ class MemberPatchModel(BasePatchModel):
) )
bio: str | None = Field(max_length=1024, default=None) bio: str | None = Field(max_length=1024, default=None)
avatar: str | None = Field(max_length=1_000_000, default=None)
names: list[FieldEntry] = Field(default=[]) names: list[FieldEntry] = Field(default=[])
pronouns: list[PronounEntry] = Field(default=[]) pronouns: list[PronounEntry] = Field(default=[])
fields: list[ProfileField] = Field(default=[]) fields: list[ProfileField] = Field(default=[])

View file

@ -8,7 +8,7 @@ from celery.utils.log import get_task_logger
from minio import Minio from minio import Minio
from sqlalchemy import select, update from sqlalchemy import select, update
from foxnouns.db import User from foxnouns.db import Member, User
from foxnouns.db.sync import session from foxnouns.db.sync import session
from foxnouns.settings import MINIO, REDIS_URL from foxnouns.settings import MINIO, REDIS_URL
@ -44,7 +44,7 @@ def convert_avatar(uri: str) -> bytes:
@app.task @app.task
def process_user_avatar(user_id: int, avatar: str): def process_user_avatar(user_id: int, avatar: str) -> None:
with session() as conn: with session() as conn:
user = conn.scalar(select(User).where(User.id == user_id)) user = conn.scalar(select(User).where(User.id == user_id))
if not user: if not user:
@ -71,7 +71,7 @@ def process_user_avatar(user_id: int, avatar: str):
@app.task @app.task
def delete_user_avatar(user_id: int): def delete_user_avatar(user_id: int) -> None:
with session() as conn: with session() as conn:
user = conn.scalar(select(User).where(User.id == user_id)) user = conn.scalar(select(User).where(User.id == user_id))
if not user: if not user:
@ -87,3 +87,54 @@ def delete_user_avatar(user_id: int):
with session() as conn: with session() as conn:
conn.execute(update(User).values(avatar=None).where(User.id == user_id)) conn.execute(update(User).values(avatar=None).where(User.id == user_id))
conn.commit() conn.commit()
@app.task
def process_member_avatar(member_id: int, avatar: str) -> None:
with session() as conn:
member = conn.scalar(select(Member).where(Member.id == member_id))
if not member:
raise ValueError(
"process_member_avatar was passed the ID of a nonexistent member"
)
img = convert_avatar(avatar)
hash = hashlib.new("sha256", data=img).hexdigest()
old_hash = member.avatar
minio.put_object(
bucket,
f"members/{member_id}/avatars/{hash}.webp",
BytesIO(img),
len(img),
"image/webp",
)
with session() as conn:
conn.execute(update(Member).values(avatar=hash).where(Member.id == member_id))
conn.commit()
if old_hash:
minio.remove_object(bucket, f"members/{member_id}/avatars/{old_hash}.webp")
@app.task
def delete_member_avatar(member_id: int) -> None:
with session() as conn:
member = conn.scalar(select(Member).where(Member.id == member_id))
if not member:
raise ValueError(
"delete_member_avatar was passed the ID of a nonexistent member"
)
if not member.avatar:
logger.info(
"delete_member_avatar was called for a member with a null avatar (%d)",
member_id,
)
return
minio.remove_object(bucket, f"members/{member_id}/avatars/{member.avatar}.webp")
with session() as conn:
conn.execute(update(Member).values(avatar=None).where(Member.id == member_id))
conn.commit()