Add S3 Storage...
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
SECRET_KEY=django-insecure-change-me
|
SECRET_KEY=django-insecure-change-me
|
||||||
DEBUG=True
|
DEBUG=True
|
||||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||||
|
S3_ENABLE=False
|
||||||
|
|
||||||
# PostgreSQL (por defecto habilitado; si POSTGRES_ENABLED=False se usa SQLite)
|
# PostgreSQL (por defecto habilitado; si POSTGRES_ENABLED=False se usa SQLite)
|
||||||
POSTGRES_ENABLED=True
|
POSTGRES_ENABLED=True
|
||||||
@@ -14,6 +15,17 @@ POSTGRES_PORT=5432
|
|||||||
# Redis
|
# Redis
|
||||||
REDIS_URL=redis://127.0.0.1:6379/1
|
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
|
||||||
STRIPE_PUBLISHABLE_KEY=
|
STRIPE_PUBLISHABLE_KEY=
|
||||||
STRIPE_SECRET_KEY=
|
STRIPE_SECRET_KEY=
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ Templates use Django's inheritance pattern:
|
|||||||
- **Image uploads**: Organized in `tienda/static/media/images/` via `upload_to='images/'` in ImageField
|
- **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
|
- **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/')`
|
- **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
|
## Shipping Restrictions
|
||||||
- **Zona de envío**: Solo se vende/envía dentro de la provincia de Almería
|
- **Zona de envío**: Solo se vende/envía dentro de la provincia de Almería
|
||||||
|
|||||||
+4
-2
@@ -34,7 +34,9 @@ http {
|
|||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
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/ {
|
location /static/ {
|
||||||
alias /static/;
|
alias /static/;
|
||||||
expires 30d;
|
expires 30d;
|
||||||
@@ -42,7 +44,7 @@ http {
|
|||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Archivos subidos por usuarios.
|
# Archivos subidos por usuarios en modo local.
|
||||||
location /media/ {
|
location /media/ {
|
||||||
alias /media/;
|
alias /media/;
|
||||||
expires 7d;
|
expires 7d;
|
||||||
|
|||||||
+43
-1
@@ -53,6 +53,21 @@ def env_int(name: str, default: int) -> int:
|
|||||||
return default
|
return default
|
||||||
return int(value)
|
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'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
load_dotenv(BASE_DIR / '.env')
|
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!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = env_bool('DEBUG', True)
|
DEBUG = env_bool('DEBUG', True)
|
||||||
|
S3_ENABLE = env_bool('S3_ENABLE', False)
|
||||||
|
|
||||||
ALLOWED_HOSTS = env_list('ALLOWED_HOSTS', [
|
ALLOWED_HOSTS = env_list('ALLOWED_HOSTS', [
|
||||||
'192.168.1.142',
|
'192.168.1.142',
|
||||||
@@ -87,9 +103,11 @@ INSTALLED_APPS = [
|
|||||||
'compressor',
|
'compressor',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if S3_ENABLE:
|
||||||
|
INSTALLED_APPS.append('storages')
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
@@ -98,6 +116,9 @@ MIDDLEWARE = [
|
|||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if not S3_ENABLE:
|
||||||
|
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
|
||||||
|
|
||||||
ROOT_URLCONF = 'proyecto.urls'
|
ROOT_URLCONF = 'proyecto.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
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 = [
|
STATICFILES_FINDERS = [
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
|||||||
+1
-1
@@ -28,5 +28,5 @@ urlpatterns = [
|
|||||||
path('api/', api.urls)
|
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)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Django==6.0.4
|
|||||||
django-appconf==1.2.0
|
django-appconf==1.2.0
|
||||||
django-redis==5.4.0
|
django-redis==5.4.0
|
||||||
django_compressor==4.6.0
|
django_compressor==4.6.0
|
||||||
|
django-storages[boto3]==1.14.6
|
||||||
gunicorn==25.1.0
|
gunicorn==25.1.0
|
||||||
idna==3.11
|
idna==3.11
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.6
|
||||||
@@ -22,6 +23,7 @@ MarkupSafe==3.0.3
|
|||||||
packaging==26.0
|
packaging==26.0
|
||||||
paypalrestsdk==1.13.3
|
paypalrestsdk==1.13.3
|
||||||
pillow==12.2.0
|
pillow==12.2.0
|
||||||
|
boto3==1.42.97
|
||||||
prompt_toolkit==3.0.52
|
prompt_toolkit==3.0.52
|
||||||
pycparser==3.0
|
pycparser==3.0
|
||||||
pyOpenSSL==26.0.0
|
pyOpenSSL==26.0.0
|
||||||
|
|||||||
@@ -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',
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user