Files
inventory/backend/app/services/photos.py
2026-07-21 16:11:09 +05:00

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()