63 lines
2.0 KiB
Python
63 lines
2.0 KiB
Python
import uuid
|
|
from mimetypes import guess_extension
|
|
|
|
from fastapi import UploadFile
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from starlette.concurrency import run_in_threadpool
|
|
|
|
from app.core.config import get_settings
|
|
from app.models.object_photo import ObjectPhoto
|
|
from app.schemas.photo import PhotoRead
|
|
|
|
ALLOWED_CONTENT_TYPES = {"image/jpeg", "image/png", "image/webp", "image/heic"}
|
|
|
|
|
|
def build_photo_read(photo: ObjectPhoto) -> PhotoRead:
|
|
settings = get_settings()
|
|
url = f"{settings.minio_public_url}/{settings.minio_bucket}/{photo.storage_key}"
|
|
return PhotoRead(id=photo.id, url=url, content_type=photo.content_type, position=photo.position)
|
|
|
|
|
|
async def upload_photo(
|
|
db: AsyncSession, minio_client, object_id: uuid.UUID, upload_file: UploadFile
|
|
) -> ObjectPhoto:
|
|
if upload_file.content_type not in ALLOWED_CONTENT_TYPES:
|
|
raise ValueError(f"Unsupported content type: {upload_file.content_type}")
|
|
|
|
settings = get_settings()
|
|
extension = guess_extension(upload_file.content_type) or ""
|
|
photo_id = uuid.uuid4()
|
|
storage_key = f"objects/{object_id}/{photo_id}{extension}"
|
|
|
|
body = await upload_file.read()
|
|
await run_in_threadpool(
|
|
minio_client.put_object,
|
|
Bucket=settings.minio_bucket,
|
|
Key=storage_key,
|
|
Body=body,
|
|
ContentType=upload_file.content_type,
|
|
)
|
|
|
|
max_position = await db.scalar(
|
|
select(func.coalesce(func.max(ObjectPhoto.position), -1)).where(ObjectPhoto.object_id == object_id)
|
|
)
|
|
photo = ObjectPhoto(
|
|
id=photo_id,
|
|
object_id=object_id,
|
|
storage_key=storage_key,
|
|
content_type=upload_file.content_type,
|
|
position=max_position + 1,
|
|
)
|
|
db.add(photo)
|
|
await db.commit()
|
|
await db.refresh(photo)
|
|
return photo
|
|
|
|
|
|
async def delete_photo(db: AsyncSession, minio_client, photo: ObjectPhoto) -> None:
|
|
settings = get_settings()
|
|
await run_in_threadpool(minio_client.delete_object, Bucket=settings.minio_bucket, Key=photo.storage_key)
|
|
await db.delete(photo)
|
|
await db.commit()
|