Files
xiaoxia-saas/apps/api/app/core/storage.py
T
Xiaoxia AI 749ef7f55a
Tests / test (push) Failing after 30s
Tests / lint (push) Failing after 31s
chore: add production environment configuration system
2026-06-15 18:32:05 +08:00

108 lines
3.3 KiB
Python

"""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