89 lines
3.3 KiB
Python
89 lines
3.3 KiB
Python
import boto3
|
|
from botocore.exceptions import ClientError
|
|
from botocore.client import Config
|
|
from typing import BinaryIO
|
|
from app.storage.base import StorageBackend
|
|
from app.config import settings
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MinIOBackend(StorageBackend):
|
|
"""MinIO storage backend implementation (S3-compatible)"""
|
|
|
|
def __init__(self):
|
|
# MinIO uses S3-compatible API
|
|
self.s3_client = boto3.client(
|
|
's3',
|
|
endpoint_url=f"{'https' if settings.minio_secure else 'http'}://{settings.minio_endpoint}",
|
|
aws_access_key_id=settings.minio_access_key,
|
|
aws_secret_access_key=settings.minio_secret_key,
|
|
config=Config(signature_version='s3v4'),
|
|
region_name='us-east-1'
|
|
)
|
|
self.bucket_name = settings.minio_bucket_name
|
|
self._ensure_bucket_exists()
|
|
|
|
def _ensure_bucket_exists(self):
|
|
"""Create bucket if it doesn't exist"""
|
|
try:
|
|
self.s3_client.head_bucket(Bucket=self.bucket_name)
|
|
except ClientError as e:
|
|
error_code = e.response['Error']['Code']
|
|
if error_code == '404':
|
|
try:
|
|
self.s3_client.create_bucket(Bucket=self.bucket_name)
|
|
logger.info(f"Created MinIO bucket: {self.bucket_name}")
|
|
except ClientError as create_error:
|
|
logger.error(f"Failed to create bucket: {create_error}")
|
|
raise
|
|
|
|
async def upload_file(self, file_data: BinaryIO, object_name: str) -> str:
|
|
"""Upload file to MinIO"""
|
|
try:
|
|
self.s3_client.upload_fileobj(file_data, self.bucket_name, object_name)
|
|
return f"minio://{self.bucket_name}/{object_name}"
|
|
except ClientError as e:
|
|
logger.error(f"Failed to upload file to MinIO: {e}")
|
|
raise
|
|
|
|
async def download_file(self, object_name: str) -> bytes:
|
|
"""Download file from MinIO"""
|
|
try:
|
|
response = self.s3_client.get_object(Bucket=self.bucket_name, Key=object_name)
|
|
return response['Body'].read()
|
|
except ClientError as e:
|
|
logger.error(f"Failed to download file from MinIO: {e}")
|
|
raise
|
|
|
|
async def delete_file(self, object_name: str) -> bool:
|
|
"""Delete file from MinIO"""
|
|
try:
|
|
self.s3_client.delete_object(Bucket=self.bucket_name, Key=object_name)
|
|
return True
|
|
except ClientError as e:
|
|
logger.error(f"Failed to delete file from MinIO: {e}")
|
|
return False
|
|
|
|
async def file_exists(self, object_name: str) -> bool:
|
|
"""Check if file exists in MinIO"""
|
|
try:
|
|
self.s3_client.head_object(Bucket=self.bucket_name, Key=object_name)
|
|
return True
|
|
except ClientError:
|
|
return False
|
|
|
|
async def get_file_url(self, object_name: str, expiration: int = 3600) -> str:
|
|
"""Generate presigned URL for MinIO object"""
|
|
try:
|
|
url = self.s3_client.generate_presigned_url(
|
|
'get_object',
|
|
Params={'Bucket': self.bucket_name, 'Key': object_name},
|
|
ExpiresIn=expiration
|
|
)
|
|
return url
|
|
except ClientError as e:
|
|
logger.error(f"Failed to generate presigned URL: {e}")
|
|
raise
|