Add S3/MinIO (RustFS) support and compose updates

Introduce S3-compatible storage support and update local/dev configuration.

- .claude: add "WebSearch" to local settings
- backend/.env.example: add USE_S3 flag and detailed S3/MinIO (RustFS) example env vars + comments to explain dev vs production usage
- backend/vontor_cz/settings.py: refactor to use USE_S3 boolean; set STATIC_ROOT; configure STORAGES for local filesystem vs S3 backends; add handling for S3 endpoint (MinIO/RustFS) vs real AWS S3 including path-style addressing, URL construction, and sensible AWS defaults
- docker-compose.yml: add rustfs to service dependencies, include rustfs in networks, change frontend port mapping to 8001:80, and add rustfs-data volume binding

These changes enable using a local S3-compatible backend (RustFS/MinIO) in development while keeping the option to swap to real AWS S3 for production.
This commit is contained in:
2026-06-10 01:29:30 +02:00
parent 2592a69790
commit 270d850572
4 changed files with 78 additions and 54 deletions

View File

@@ -20,6 +20,8 @@ POSTGRES_USER=dockerDBuser
POSTGRES_PASSWORD=AWSJeMocDrahaZalezitost
# ------------------ MEDIA / STATIC ------------------
# USE_S3=True enables S3-compatible storage (MinIO or AWS).
# Leave USE_S3=False (default) to use local filesystem in development.
MEDIA_URL=/media/
# ------------------ REDIS / CACHING / CHANNELS ------------------
@@ -44,12 +46,29 @@ EMAIL_USER=
EMAIL_USER_PASSWORD=
DEFAULT_FROM_EMAIL=
# ------------------ AWS (disabled unless USE_AWS=True) ------------------
USE_AWS=False
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_STORAGE_BUCKET_NAME=
AWS_S3_REGION_NAME=eu-central-1
# ------------------ S3 STORAGE ------------------
# Set USE_S3=True to enable. AWS_S3_ENDPOINT_URL selects the backend:
# set → MinIO / S3-compatible
# empty → real AWS S3
USE_S3=False
# RustFS (docker-compose default — S3-compatible MinIO successor)
AWS_S3_ENDPOINT_URL=http://rustfs:9000
AWS_S3_CUSTOM_DOMAIN=localhost:9000/vontor
AWS_STORAGE_BUCKET_NAME=vontor
AWS_ACCESS_KEY_ID=rustfsadmin
AWS_SECRET_ACCESS_KEY=rustfsadmin
RUSTFS_ACCESS_KEY=rustfsadmin
RUSTFS_SECRET_KEY=rustfsadmin
RUSTFS_BUCKET_NAME=vontor
# AWS S3 (swap in for production — clear AWS_S3_ENDPOINT_URL)
# AWS_STORAGE_BUCKET_NAME=my-bucket
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# AWS_S3_REGION_NAME=eu-central-1
# AWS_S3_ENDPOINT_URL=
# ------------------ JWT / TOKENS (lifetimes defined in code) ------------------
# (No env vars needed; kept placeholder section)

View File

@@ -626,63 +626,59 @@ STATICFILES_DIRS = [
if os.getenv("USE_AWS", "") == "True":
USE_AWS = True
else:
USE_AWS = False
USE_S3 = _env_bool("USE_S3")
print(f"USE_S3: {USE_S3}")
print(f"\n-------------- USE_AWS: {USE_AWS} --------------")
STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
if USE_AWS is False:
# Development: Use local file system storage for static files
if not USE_S3:
# Local filesystem — development only
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}
# Media and Static URL for local dev
MEDIA_URL = os.getenv("MEDIA_URL", "/media/")
MEDIA_URL = os.getenv("MEDIA_URL", "/media/")
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
elif USE_AWS:
# PRODUCTION
AWS_LOCATION = "static"
else:
# S3-compatible storage — MinIO (AWS_S3_ENDPOINT_URL set) or real AWS S3 (endpoint empty)
S3_BUCKET = os.getenv('AWS_STORAGE_BUCKET_NAME', 'vontor')
S3_ENDPOINT = os.getenv('AWS_S3_ENDPOINT_URL', '')
S3_CUSTOM_DOMAIN = os.getenv('AWS_S3_CUSTOM_DOMAIN', '')
S3_SSL = not S3_ENDPOINT or S3_ENDPOINT.startswith('https')
S3_PROTO = 'https' if S3_SSL else 'http'
# Production: Use S3 storage
STORAGES = {
"default": {
"BACKEND" : "storages.backends.s3boto3.S3StaticStorage",
},
"staticfiles": {
"BACKEND" : "storages.backends.s3boto3.S3StaticStorage",
},
"default": {"BACKEND": "storages.backends.s3boto3.S3Boto3Storage"},
"staticfiles": {"BACKEND": "storages.backends.s3boto3.S3StaticStorage"},
}
# Media and Static URL for AWS S3
MEDIA_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/media/'
STATIC_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/static/'
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = S3_BUCKET
AWS_S3_SIGNATURE_VERSION = 's3v4'
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
AWS_QUERYSTRING_AUTH = False
AWS_S3_USE_SSL = S3_SSL
CSRF_TRUSTED_ORIGINS.append(STATIC_URL)
# Static files should be collected to a local directory and then uploaded to S3
STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME', 'us-east-1') # Default to 'us-east-1' if not set
AWS_S3_SIGNATURE_VERSION = 's3v4' # Use AWS Signature Version 4
AWS_S3_USE_SSL = True
AWS_S3_FILE_OVERWRITE = True
AWS_DEFAULT_ACL = None # Set to None to avoid setting a default ACL
if S3_ENDPOINT:
# MinIO / S3-compatible: needs path-style addressing
AWS_S3_ENDPOINT_URL = S3_ENDPOINT
AWS_S3_ADDRESSING_STYLE = 'path'
if S3_CUSTOM_DOMAIN:
AWS_S3_CUSTOM_DOMAIN = S3_CUSTOM_DOMAIN
AWS_S3_URL_PROTOCOL = f'{S3_PROTO}:'
base_host = S3_CUSTOM_DOMAIN or f'{S3_ENDPOINT.split("://")[1]}/{S3_BUCKET}'
MEDIA_URL = f'{S3_PROTO}://{base_host}/'
STATIC_URL = f'{S3_PROTO}://{base_host}/static/'
else:
# AWS S3
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME', 'us-east-1')
MEDIA_URL = f'https://{S3_BUCKET}.s3.amazonaws.com/media/'
STATIC_URL = f'https://{S3_BUCKET}.s3.amazonaws.com/static/'
CSRF_TRUSTED_ORIGINS.append(STATIC_URL)