diff --git a/django_minio_backend/apps.py b/django_minio_backend/apps.py index d08e539..b3bf318 100644 --- a/django_minio_backend/apps.py +++ b/django_minio_backend/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig from .utils import get_setting, ConfigurationError -from .models import MinioBackendStatic +from .models import MinioBackend, MinioBackendStatic __all__ = ['DjangoMinioBackendConfig', ] @@ -10,6 +10,9 @@ class DjangoMinioBackendConfig(AppConfig): name = 'django_minio_backend' def ready(self): + mb = MinioBackend() + mb.validate_settings() + consistency_check_on_start = get_setting('MINIO_CONSISTENCY_CHECK_ON_START', False) if consistency_check_on_start: from django.core.management import call_command diff --git a/django_minio_backend/models.py b/django_minio_backend/models.py index 05a9cfe..55f25cf 100644 --- a/django_minio_backend/models.py +++ b/django_minio_backend/models.py @@ -30,7 +30,7 @@ from .utils import MinioServerStatus, PrivatePublicMixedError, ConfigurationError, get_setting -__all__ = ['MinioBackend', 'get_iso_date', 'iso_date_prefix', ] +__all__ = ['MinioBackend', 'MinioBackendStatic', 'get_iso_date', 'iso_date_prefix', ] def get_iso_date() -> str: @@ -85,7 +85,7 @@ def __init__(self, self.__CLIENT: Union[minio.Minio, None] = None # This client is used for internal communication only. Communication this way should not leave the host network's perimeter self.__CLIENT_FAKE: Union[minio.Minio, None] = None # This fake client is used for pre-signed URL generation only; it does not execute HTTP requests - self.__MINIO_ENDPOINT: str = get_setting("MINIO_ENDPOINT") + self.__MINIO_ENDPOINT: str = get_setting("MINIO_ENDPOINT", "") self.__MINIO_EXTERNAL_ENDPOINT: str = get_setting("MINIO_EXTERNAL_ENDPOINT", self.__MINIO_ENDPOINT) self.__MINIO_ACCESS_KEY: str = get_setting("MINIO_ACCESS_KEY") self.__MINIO_SECRET_KEY: str = get_setting("MINIO_SECRET_KEY") @@ -146,11 +146,14 @@ def _save(self, file_path_name: str, content: InMemoryUploadedFile) -> str: # Upload object file_path: Path = Path(file_path_name) # app name + file.suffix + content_bytes: io.BytesIO = io.BytesIO(content.read()) + content_length: int = len(content_bytes.getvalue()) + self.client.put_object( bucket_name=self.bucket, object_name=file_path.as_posix(), - data=content, - length=content.size, + data=content_bytes, + length=content_length, content_type=self._guess_content_type(file_path_name, content), metadata=self._META_KWARGS.get('metadata', None), sse=self._META_KWARGS.get('sse', None), @@ -333,16 +336,12 @@ def is_minio_available(self) -> MinioServerStatus: @property def client(self) -> minio.Minio: """Get handle to an (already) instantiated minio.Minio instance""" - if not self.__CLIENT: - return self._create_new_client() - return self.__CLIENT + return self.__CLIENT or self._create_new_client() @property def client_fake(self) -> minio.Minio: """Get handle to an (already) instantiated FAKE minio.Minio instance for generating signed URLs for external access""" - if not self.__CLIENT_FAKE: - return self._create_new_client(fake=True) - return self.__CLIENT_FAKE + return self.__CLIENT_FAKE or self._create_new_client(fake=True) @property def base_url(self) -> str: @@ -358,14 +357,6 @@ def _create_new_client(self, fake: bool = False) -> minio.Minio: """ Instantiates a new Minio client and assigns it to their respective class variable """ - # Safety Guards - if not self.PRIVATE_BUCKETS or not self.PUBLIC_BUCKETS: - raise ConfigurationError( - 'MINIO_PRIVATE_BUCKETS or ' - 'MINIO_PUBLIC_BUCKETS ' - 'is not configured properly in your settings.py (or equivalent)' - ) - mc = minio.Minio( endpoint=self.__MINIO_EXTERNAL_ENDPOINT if fake else self.__MINIO_ENDPOINT, access_key=self.__MINIO_ACCESS_KEY, @@ -424,6 +415,28 @@ def set_bucket_to_public(self): ]} self.set_bucket_policy(self.bucket, policy_public_read_only) + def validate_settings(self): + """ + validate_settings raises a ConfigurationError exception when one of the following conditions is met: + * Neither MINIO_PRIVATE_BUCKETS nor MINIO_PUBLIC_BUCKETS have been declared and configured with at least 1 bucket + * A mandatory parameter (ENDPOINT, ACCESS_KEY, SECRET_KEY or USE_HTTP) hasn't been declared and configured properly + """ + # minimum 1 bucket has to be declared + if not (get_setting("MINIO_PRIVATE_BUCKETS") or get_setting("MINIO_PUBLIC_BUCKETS")): + raise ConfigurationError( + 'Either ' + 'MINIO_PRIVATE_BUCKETS' + ' or ' + 'MINIO_PUBLIC_BUCKETS ' + 'must be configured in your settings.py (can be both)' + ) + # mandatory parameters must be configured + mandatory_parameters = (self.__MINIO_ENDPOINT, self.__MINIO_ACCESS_KEY, self.__MINIO_SECRET_KEY, self.__MINIO_USE_HTTPS) + if any([bool(x) is False for x in mandatory_parameters]): + raise ConfigurationError( + "A mandatory parameter (ENDPOINT, ACCESS_KEY, SECRET_KEY or USE_HTTP) hasn't been configured properly" + ) + @deconstructible class MinioBackendStatic(MinioBackend):