Add S3 Storage...

This commit is contained in:
2026-04-28 21:19:32 +02:00
parent d8f6838f0c
commit 84d8a0e3b6
7 changed files with 86 additions and 4 deletions
+12
View File
@@ -2,6 +2,7 @@
SECRET_KEY=django-insecure-change-me
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
S3_ENABLE=False
# PostgreSQL (por defecto habilitado; si POSTGRES_ENABLED=False se usa SQLite)
POSTGRES_ENABLED=True
@@ -14,6 +15,17 @@ POSTGRES_PORT=5432
# Redis
REDIS_URL=redis://127.0.0.1:6379/1
# S3 (activar con S3_ENABLE=True)
AWS_STORAGE_BUCKET_NAME=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_S3_REGION_NAME=
AWS_S3_ENDPOINT_URL=
AWS_S3_CUSTOM_DOMAIN=
AWS_S3_USE_SSL=True
AWS_QUERYSTRING_AUTH=False
AWS_DEFAULT_ACL=public-read
# Stripe
STRIPE_PUBLISHABLE_KEY=
STRIPE_SECRET_KEY=
+1
View File
@@ -35,6 +35,7 @@ Templates use Django's inheritance pattern:
- **Image uploads**: Organized in `tienda/static/media/images/` via `upload_to='images/'` in ImageField
- **Access**: Media files served automatically in development via Django's static file handler
- **Image model**: Located in [tienda/models.py](tienda/models.py) with `ImageField(upload_to='images/')`
- **S3 mode**: if `S3_ENABLE=True` (case-insensitive), static and media switch to S3 storages instead of the local filesystem; Nginx should proxy the app only and the browser should load asset URLs from the bucket or CDN
## Shipping Restrictions
- **Zona de envío**: Solo se vende/envía dentro de la provincia de Almería
+4 -2
View File
@@ -34,7 +34,9 @@ http {
listen 80;
server_name _;
# Archivos estáticos generados por collectstatic.
# Modo local: sirve static/media desde volúmenes montados.
# Si S3_ENABLE=True, estos bloques no se usan y el navegador debe
# cargar los assets directamente desde el bucket o CDN.
location /static/ {
alias /static/;
expires 30d;
@@ -42,7 +44,7 @@ http {
access_log off;
}
# Archivos subidos por usuarios.
# Archivos subidos por usuarios en modo local.
location /media/ {
alias /media/;
expires 7d;
+43 -1
View File
@@ -53,6 +53,21 @@ def env_int(name: str, default: int) -> int:
return default
return int(value)
def env_str(name: str, default: str = '') -> str:
value = os.getenv(name)
if value is None:
return default
return value.strip()
def env_optional_str(name: str) -> str | None:
value = os.getenv(name)
if value is None:
return None
value = value.strip()
return value or None
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(BASE_DIR / '.env')
@@ -66,6 +81,7 @@ SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-#g((q@lvnkt(j6)2(gvtn0px)r
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env_bool('DEBUG', True)
S3_ENABLE = env_bool('S3_ENABLE', False)
ALLOWED_HOSTS = env_list('ALLOWED_HOSTS', [
'192.168.1.142',
@@ -87,9 +103,11 @@ INSTALLED_APPS = [
'compressor',
]
if S3_ENABLE:
INSTALLED_APPS.append('storages')
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@@ -98,6 +116,9 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
if not S3_ENABLE:
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
ROOT_URLCONF = 'proyecto.urls'
TEMPLATES = [
@@ -211,6 +232,27 @@ STORAGES = {
},
}
if S3_ENABLE:
AWS_STORAGE_BUCKET_NAME = env_str('AWS_STORAGE_BUCKET_NAME') or None
AWS_ACCESS_KEY_ID = env_optional_str('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = env_optional_str('AWS_SECRET_ACCESS_KEY')
AWS_S3_REGION_NAME = env_optional_str('AWS_S3_REGION_NAME')
AWS_S3_ENDPOINT_URL = env_optional_str('AWS_S3_ENDPOINT_URL')
AWS_S3_CUSTOM_DOMAIN = env_optional_str('AWS_S3_CUSTOM_DOMAIN')
AWS_S3_USE_SSL = env_bool('AWS_S3_USE_SSL', True)
AWS_QUERYSTRING_AUTH = env_bool('AWS_QUERYSTRING_AUTH', False)
AWS_DEFAULT_ACL = env_str('AWS_DEFAULT_ACL', 'public-read') or None
AWS_S3_OBJECT_PARAMETERS = {}
STORAGES = {
'default': {
'BACKEND': 'tienda.storage_backends.MediaStorage',
},
'staticfiles': {
'BACKEND': 'tienda.storage_backends.StaticStorage',
},
}
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+1 -1
View File
@@ -28,5 +28,5 @@ urlpatterns = [
path('api/', api.urls)
]
if settings.DEBUG:
if settings.DEBUG and not settings.S3_ENABLE:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+2
View File
@@ -14,6 +14,7 @@ Django==6.0.4
django-appconf==1.2.0
django-redis==5.4.0
django_compressor==4.6.0
django-storages[boto3]==1.14.6
gunicorn==25.1.0
idna==3.11
Jinja2==3.1.6
@@ -22,6 +23,7 @@ MarkupSafe==3.0.3
packaging==26.0
paypalrestsdk==1.13.3
pillow==12.2.0
boto3==1.42.97
prompt_toolkit==3.0.52
pycparser==3.0
pyOpenSSL==26.0.0
+23
View File
@@ -0,0 +1,23 @@
from __future__ import annotations
from storages.backends.s3 import S3ManifestStaticStorage, S3Storage
class StaticStorage(S3ManifestStaticStorage):
location = 'static'
default_acl = 'public-read'
querystring_auth = False
file_overwrite = True
object_parameters = {
'CacheControl': 'public, max-age=31536000, immutable',
}
class MediaStorage(S3Storage):
location = 'media'
default_acl = 'public-read'
querystring_auth = False
file_overwrite = False
object_parameters = {
'CacheControl': 'public, max-age=604800',
}