from minio import Minio from django.conf import settings import uuid import os import json import logging logger = logging.getLogger(__name__) class MinioClient: def __init__(self): self._client = None self.bucket_name = getattr(settings, 'MINIO_BUCKET_NAME', 'opc-assets') self._initialized = False @property def client(self): if self._client is None: self._client = Minio( endpoint=getattr(settings, 'MINIO_ENDPOINT', 'localhost:9000'), access_key=getattr(settings, 'MINIO_ACCESS_KEY', 'minioadmin'), secret_key=getattr(settings, 'MINIO_SECRET_KEY', 'minioadmin'), secure=getattr(settings, 'MINIO_SECURE', False) ) if not self._initialized: try: self._ensure_bucket() self._initialized = True except Exception as e: logger.warning(f'MinIO bucket init failed (will retry on next call): {e}') return self._client def _ensure_bucket(self): if not self._client.bucket_exists(self.bucket_name): self._client.make_bucket(self.bucket_name) # Always update policy to ensure public access policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"AWS": ["*"]}, "Action": ["s3:GetBucketLocation", "s3:ListBucket"], "Resource": [f"arn:aws:s3:::{self.bucket_name}"], }, { "Effect": "Allow", "Principal": {"AWS": ["*"]}, "Action": ["s3:GetObject"], "Resource": [f"arn:aws:s3:::{self.bucket_name}/*"], }, ], } self._client.set_bucket_policy(self.bucket_name, json.dumps(policy)) def upload_file(self, file_obj, folder='general'): # Generate a unique name ext = os.path.splitext(file_obj.name)[1] file_name = f"{folder}/{uuid.uuid4()}{ext}" # Upload (this will trigger lazy init of the client) self.client.put_object( self.bucket_name, file_name, file_obj, length=file_obj.size, content_type=file_obj.content_type ) # Return the public URL base_url = getattr(settings, 'MINIO_PUBLIC_URL', 'http://127.0.0.1:9000') return f"{base_url}/{self.bucket_name}/{file_name}" # Lazy singleton — does NOT connect at import time minio_client = MinioClient()