""" Client MinIO/S3 asincrono per il worker. Percorso EML raw: pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml Percorso allegati: pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/attachments/{msg_id}/{filename} """ import io import logging from functools import lru_cache from miniopy_async import Minio from app.config import get_settings logger = logging.getLogger(__name__) settings = get_settings() @lru_cache(maxsize=1) def get_minio_client() -> Minio: """Restituisce l'istanza singleton del client MinIO.""" return Minio( endpoint=settings.minio_endpoint, access_key=settings.minio_access_key, secret_key=settings.minio_secret_key, secure=settings.minio_use_ssl, ) async def upload_eml( tenant_id: str, mailbox_id: str, uid: int, eml_bytes: bytes, ) -> str: """ Carica un raw EML su MinIO e restituisce il percorso oggetto. Percorso: tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml """ client = get_minio_client() bucket = settings.minio_bucket object_path = f"tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml" try: data_stream = io.BytesIO(eml_bytes) await client.put_object( bucket_name=bucket, object_name=object_path, data=data_stream, length=len(eml_bytes), content_type="message/rfc822", ) logger.debug(f"EML caricato: s3://{bucket}/{object_path} ({len(eml_bytes)} bytes)") return object_path except Exception as e: logger.error(f"Errore upload EML {object_path}: {e}") raise async def ensure_bucket_exists() -> None: """Verifica che il bucket MinIO esista, altrimenti lo crea.""" client = get_minio_client() bucket = settings.minio_bucket try: found = await client.bucket_exists(bucket) if not found: await client.make_bucket(bucket) logger.info(f"Bucket MinIO creato: {bucket}") else: logger.debug(f"Bucket MinIO esistente: {bucket}") except Exception as e: logger.warning(f"Impossibile verificare/creare bucket MinIO: {e}")