"""MinIO storage service for file uploads.""" from minio import Minio from minio.error import S3Error from typing import BinaryIO import os from app.core.config import get_settings class MinIOService: """MinIO storage service.""" def __init__(self): """Initialize MinIO client from settings.""" settings = get_settings() self.client = Minio( settings.minio_endpoint, access_key=settings.minio_access_key, secret_key=settings.minio_secret_key, secure=settings.minio_secure, ) self.bucket_name = settings.minio_bucket self.public_url = settings.minio_public_url self._ensure_bucket() def _ensure_bucket(self): """Ensure bucket exists.""" try: if not self.client.bucket_exists(self.bucket_name): self.client.make_bucket(self.bucket_name) # Set download policy for public access policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"AWS": "*"}, "Action": ["s3:GetObject"], "Resource": [f"arn:aws:s3:::{self.bucket_name}/*"] } ] } import json self.client.set_bucket_policy(self.bucket_name, json.dumps(policy)) except S3Error as e: print(f"Error ensuring bucket: {e}") def upload_file( self, file: BinaryIO, storage_key: str, content_type: str = "application/octet-stream", ) -> str: """ Upload file to MinIO. Args: file: File object to upload storage_key: Storage path/key (e.g., "uploads/abc123/video.mp4") content_type: MIME type of the file Returns: str: Public URL of uploaded file """ try: # Get file size file.seek(0, os.SEEK_END) file_size = file.tell() file.seek(0) # Upload file self.client.put_object( self.bucket_name, storage_key, file, file_size, content_type=content_type, ) # Return public URL return f"{self.public_url}/{self.bucket_name}/{storage_key}" except S3Error as e: raise Exception(f"Failed to upload file: {e}") def get_url(self, storage_key: str) -> str: """Get public URL for a storage key.""" return f"{self.public_url}/{self.bucket_name}/{storage_key}" def delete_file(self, storage_key: str): """Delete file from MinIO.""" try: self.client.remove_object(self.bucket_name, storage_key) except S3Error as e: print(f"Error deleting file: {e}") # Singleton instance _minio_service = None def get_minio_service() -> MinIOService: """Get or create MinIO service instance.""" global _minio_service if _minio_service is None: _minio_service = MinIOService() return _minio_service