162 lines
5.1 KiB
Python
162 lines
5.1 KiB
Python
from django.core.files.storage import FileSystemStorage
|
|
|
|
from infrasynth.shared.settings_utils import get_setting
|
|
|
|
|
|
class _LocalStorage:
|
|
def __init__(self, config: dict):
|
|
self._storage = FileSystemStorage()
|
|
|
|
def save(self, name: str, content) -> str:
|
|
return self._storage.save(name, content)
|
|
|
|
def open(self, name: str, mode: str = "rb"):
|
|
return self._storage.open(name, mode)
|
|
|
|
def exists(self, name: str) -> bool:
|
|
return self._storage.exists(name)
|
|
|
|
def url(self, name: str) -> str:
|
|
return self._storage.url(name)
|
|
|
|
def delete(self, name: str) -> None:
|
|
self._storage.delete(name)
|
|
|
|
def generate_signed_url(self, name: str, expiry_seconds: int = 3600) -> str:
|
|
return self._storage.url(name)
|
|
|
|
|
|
class _S3Storage:
|
|
def __init__(self, config: dict):
|
|
from storages.backends.s3boto3 import S3Boto3Storage
|
|
|
|
options = {
|
|
"access_key": config.get("ACCESS_KEY"),
|
|
"secret_key": config.get("SECRET_KEY"),
|
|
"bucket_name": config.get("BUCKET_NAME"),
|
|
"region_name": config.get("REGION", "us-east-1"),
|
|
"endpoint_url": config.get("ENDPOINT_URL"),
|
|
"querystring_auth": True,
|
|
"default_acl": None,
|
|
"file_overwrite": False,
|
|
}
|
|
options = {k: v for k, v in options.items() if v is not None}
|
|
self._storage = S3Boto3Storage(**options)
|
|
|
|
def save(self, name: str, content) -> str:
|
|
return self._storage.save(name, content)
|
|
|
|
def open(self, name: str, mode: str = "rb"):
|
|
return self._storage.open(name, mode)
|
|
|
|
def exists(self, name: str) -> bool:
|
|
return self._storage.exists(name)
|
|
|
|
def url(self, name: str) -> str:
|
|
return self._storage.url(name)
|
|
|
|
def delete(self, name: str) -> None:
|
|
self._storage.delete(name)
|
|
|
|
def generate_signed_url(self, name: str, expiry_seconds: int = 3600) -> str:
|
|
return self._storage.url(name, expire=expiry_seconds)
|
|
|
|
|
|
class _GCSStorage:
|
|
def __init__(self, config: dict):
|
|
from storages.backends.gcloud import GoogleCloudStorage
|
|
|
|
self._storage = GoogleCloudStorage(
|
|
bucket_name=config.get("BUCKET_NAME"),
|
|
project_id=config.get("PROJECT_ID"),
|
|
credentials=config.get("CREDENTIALS_PATH"),
|
|
)
|
|
|
|
def save(self, name: str, content) -> str:
|
|
return self._storage.save(name, content)
|
|
|
|
def open(self, name: str, mode: str = "rb"):
|
|
return self._storage.open(name, mode)
|
|
|
|
def exists(self, name: str) -> bool:
|
|
return self._storage.exists(name)
|
|
|
|
def url(self, name: str) -> str:
|
|
return self._storage.url(name)
|
|
|
|
def delete(self, name: str) -> None:
|
|
self._storage.delete(name)
|
|
|
|
def generate_signed_url(self, name: str, expiry_seconds: int = 3600) -> str:
|
|
return self._storage.url(name, expire=expiry_seconds)
|
|
|
|
|
|
class _CloudinaryStorage:
|
|
def __init__(self, config: dict):
|
|
import cloudinary
|
|
from cloudinary.uploader import upload
|
|
|
|
cloudinary.config(
|
|
cloud_name=config.get("CLOUD_NAME"),
|
|
api_key=config.get("API_KEY"),
|
|
api_secret=config.get("API_SECRET"),
|
|
)
|
|
self._upload = upload
|
|
|
|
def save(self, name: str, content) -> str:
|
|
result = self._upload(content.read(), public_id=name, overwrite=False)
|
|
return result.get("public_id", name)
|
|
|
|
def open(self, name: str, mode: str = "rb"):
|
|
raise NotImplementedError("Cloudinary storage does not support direct file open.")
|
|
|
|
def exists(self, name: str) -> bool:
|
|
import cloudinary.api
|
|
|
|
try:
|
|
cloudinary.api.resource(name)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def url(self, name: str) -> str:
|
|
import cloudinary.utils
|
|
|
|
return cloudinary.utils.cloudinary_url(name)[0]
|
|
|
|
def delete(self, name: str) -> None:
|
|
from cloudinary.uploader import destroy
|
|
|
|
destroy(name)
|
|
|
|
def generate_signed_url(self, name: str, expiry_seconds: int = 3600) -> str:
|
|
import cloudinary.utils
|
|
|
|
return cloudinary.utils.cloudinary_url(name, sign_url=True, expires_at=expiry_seconds)[0]
|
|
|
|
|
|
_BACKEND_CLASSES = {
|
|
"local": _LocalStorage,
|
|
"S3": _S3Storage,
|
|
"s3": _S3Storage,
|
|
"gcs": _GCSStorage,
|
|
"cloudinary": _CloudinaryStorage,
|
|
}
|
|
|
|
|
|
def get_storage_backend(backend_name: str | None = None):
|
|
"""Returns a storage backend instance by name (defaults to settings config)."""
|
|
from django.conf import settings
|
|
|
|
config = getattr(settings, "INFRASYNTH_FILES", {})
|
|
if not backend_name:
|
|
backend_name = get_setting("INFRASYNTH_FILES", "DEFAULT_STORAGE_BACKEND", "local")
|
|
backend_config = config.get("STORAGE_BACKENDS", {}).get(backend_name, {})
|
|
backend_class = _BACKEND_CLASSES.get(str(backend_name), _LocalStorage)
|
|
return backend_class(backend_config or {})
|
|
|
|
|
|
def save_file(file_obj, storage_key: str, backend: str = "local"):
|
|
"""Saves a file to the given storage backend, returns the stored key."""
|
|
storage = get_storage_backend(backend)
|
|
return storage.save(storage_key, file_obj)
|