import os from contextlib import asynccontextmanager from typing import AsyncIterator import aioboto3 from botocore.exceptions import ClientError MINIO_ENDPOINT: str = os.getenv("MINIO_ENDPOINT", "http://minio:9000") MINIO_ACCESS_KEY: str = os.getenv("MINIO_ACCESS_KEY", "minioadmin") MINIO_SECRET_KEY: str = os.getenv("MINIO_SECRET_KEY", "minioadminpassword") MINIO_BUCKET: str = os.getenv("MINIO_BUCKET", "tickets-media") _session = aioboto3.Session() @asynccontextmanager async def _s3_client() -> AsyncIterator: async with _session.client( "s3", endpoint_url=MINIO_ENDPOINT, aws_access_key_id=MINIO_ACCESS_KEY, aws_secret_access_key=MINIO_SECRET_KEY, region_name="us-east-1", # MinIO не требует региона, но boto3 обязывает передать ) as client: yield client async def ensure_bucket_exists() -> None: """Создаёт бакет при старте воркера, если он ещё не существует.""" async with _s3_client() as client: try: await client.head_bucket(Bucket=MINIO_BUCKET) except ClientError as exc: error_code = exc.response["Error"]["Code"] if error_code in ("404", "NoSuchBucket"): await client.create_bucket(Bucket=MINIO_BUCKET) else: raise async def upload_pdf(object_name: str, pdf_bytes: bytes) -> str: """Загружает PDF в MinIO и возвращает публичный URL объекта.""" async with _s3_client() as client: await client.put_object( Bucket=MINIO_BUCKET, Key=object_name, Body=pdf_bytes, ContentType="application/pdf", ) return f"{MINIO_ENDPOINT}/{MINIO_BUCKET}/{object_name}"