Files
GMG-Smart-Quote/backend/app/services/storage.py
T

110 lines
3.1 KiB
Python

import io
import uuid
from datetime import timedelta
from urllib.parse import urlparse
import structlog
from minio import Minio
from minio.error import S3Error
from fastapi import UploadFile, HTTPException, status
from app.core.config import settings
logger = structlog.get_logger()
_client: Minio | None = None
_public_client: Minio | None = None
def get_client() -> Minio:
global _client
if _client is None:
_client = Minio(
settings.minio_endpoint,
access_key=settings.minio_access_key,
secret_key=settings.minio_secret_key,
secure=settings.minio_secure,
)
return _client
def _get_public_client() -> Minio:
global _public_client
if _public_client is None:
parsed = urlparse(settings.minio_public_url)
endpoint = parsed.netloc
secure = parsed.scheme == "https"
_public_client = Minio(
endpoint,
access_key=settings.minio_access_key,
secret_key=settings.minio_secret_key,
secure=secure,
region="us-east-1",
)
return _public_client
def _ensure_bucket(bucket: str) -> None:
client = get_client()
try:
if not client.bucket_exists(bucket):
client.make_bucket(bucket)
logger.info("minio_bucket_created", bucket=bucket)
except S3Error as exc:
logger.error("minio_bucket_error", bucket=bucket, error=str(exc))
raise
async def upload_photo(file: UploadFile, valuation_id: int) -> str:
bucket = settings.minio_bucket_photos
try:
_ensure_bucket(bucket)
except S3Error as exc:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Storage non disponibile",
) from exc
ext = ""
if file.filename and "." in file.filename:
ext = "." + file.filename.rsplit(".", 1)[-1].lower()
object_name = f"valuations/{valuation_id}/{uuid.uuid4().hex}{ext}"
content = await file.read()
content_length = len(content)
content_type = file.content_type or "application/octet-stream"
client = get_client()
try:
client.put_object(
bucket,
object_name,
io.BytesIO(content),
length=content_length,
content_type=content_type,
)
logger.info("minio_upload_ok", object_name=object_name)
return object_name
except S3Error as exc:
logger.error("minio_upload_error", object_name=object_name, error=str(exc))
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Errore durante il caricamento del file",
) from exc
def get_presigned_url(storage_path: str, expires_hours: int = 4) -> str | None:
bucket = settings.minio_bucket_photos
client = _get_public_client()
try:
url = client.presigned_get_object(
bucket,
storage_path,
expires=timedelta(hours=expires_hours),
)
return url
except S3Error as exc:
logger.warning("minio_presigned_error", path=storage_path, error=str(exc))
return None