From bf69fe88d91d09849f266e558cf89df9d07ae05a Mon Sep 17 00:00:00 2001 From: aserrador Date: Sun, 10 May 2026 20:32:42 -0500 Subject: [PATCH] feat: Add staging environment for local production testing - Add docker-compose.staging.yml with PostgreSQL and Django - Add .env.staging.example with staging-specific environment variables - Configure staging settings (DEBUG=False, no SSL redirect for localhost) - Update settings/__init__.py to support staging environment detection - Update AGENTS.md with staging environment documentation - Update .gitignore to exclude .env.staging - Optimize docker-compose.prod.yml configuration Staging environment simulates production configuration locally: - PostgreSQL database (port 5433 to avoid conflicts) - Gunicorn with 4 workers - DEBUG=False but HTTP allowed for easier testing - Separate volumes for static files, media, and logs Usage: cp .env.staging.example .env.staging docker compose -f docker-compose.staging.yml up -d --- .env.staging.example | 51 +++++++++++++++ .gitignore | 1 + AGENTS.md | 72 +++++++++++++++++---- django.Dockerfile | 2 - docker-compose.prod.yml | 6 +- docker-compose.staging.yml | 73 ++++++++++++++++++++++ tienda_ilusion/config/settings/__init__.py | 11 +++- 7 files changed, 198 insertions(+), 18 deletions(-) create mode 100644 .env.staging.example create mode 100644 docker-compose.staging.yml diff --git a/.env.staging.example b/.env.staging.example new file mode 100644 index 0000000..1975a11 --- /dev/null +++ b/.env.staging.example @@ -0,0 +1,51 @@ +# Staging Environment Variables +# Testing de producción en localhost sin SSL +# Este ambiente simula producción pero permite HTTP en localhost + +# Django Environment +DJANGO_ENV=staging + +# Debug mode (False para simular producción) +DEBUG=False + +# Django Secret Key +# Para staging local, puedes usar una key fija (NO usar en producción real) +SECRET_KEY=staging-local-key-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v + +# Allowed hosts (localhost para testing) +ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0 + +# CORS allowed origins (permite acceso desde navegador local) +# Incluye común puertos de desarrollo de frontend y acceso directo +CORS_ALLOWED_ORIGINS=http://localhost:8000,http://localhost:3000,http://localhost:5173,http://localhost:7001,http://127.0.0.1:8000 + +# CSRF Trusted Origins (localhost HTTP para staging) +CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000 + +# PostgreSQL Database Configuration (staging local) +DB_NAME=tienda_ilusion_staging +DB_USER=tienda_ilusion_user +DB_PASSWORD=staging_local_password +DB_HOST=postgres +DB_PORT=5432 + +# Email Configuration (console backend para staging) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USE_TLS=True +EMAIL_HOST_USER=staging@tiendailusion.local +EMAIL_HOST_PASSWORD=staging_password +DEFAULT_FROM_EMAIL=staging@tiendailusion.local + +# Admin notifications +ADMIN_EMAIL=admin@tiendailusion.local + +# Tryton ERP Configuration (Staging) +# Ajusta estos valores según tu entorno de staging de Tryton +TRYTON_HOST=localhost +TRYTON_DATABASE=tryton_staging +TRYTON_USERNAME=admin +TRYTON_PASSWORD=admin + +# Staging specific: Disable SSL redirect for localhost testing +STAGING_DISABLE_SSL=True diff --git a/.gitignore b/.gitignore index 9b40779..85499f0 100644 --- a/.gitignore +++ b/.gitignore @@ -363,3 +363,4 @@ pgdata/ # IDE-specific .vscode/ .idea/ +.env.staging diff --git a/AGENTS.md b/AGENTS.md index b04940f..4d4f0fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,9 +8,11 @@ Backend Django con Django REST Framework don_confiao_backend/ ├── requirements.txt # Dependencias Python ├── docker-compose.dev.yml # Docker Compose para desarrollo +├── docker-compose.staging.yml # Docker Compose para staging (testing producción local) ├── docker-compose.prod.yml # Docker Compose para producción ├── django.Dockerfile # Dockerfile Django ├── .env.development # Variables de entorno desarrollo +├── .env.staging # Variables de entorno staging ├── .env.production.example # Ejemplo variables de entorno producción ├── .env_example # Ejemplo de variables de entorno ├── README.rst # Documentación básica @@ -42,6 +44,7 @@ don_confiao_backend/ │ ├── __init__.py # Detección automática de ambiente │ ├── base.py # Configuración compartida │ ├── development.py # Configuración desarrollo + │ ├── staging.py # Configuración staging │ └── production.py # Configuración producción ├── urls.py ├── wsgi.py @@ -86,20 +89,43 @@ El proyecto se ejecuta con docker-compose. Todos los comandos `manage.py` deben ```bash # Ejecutar tests -docker-compose -f docker-compose.dev.yml run --rm django python manage.py test +docker compose -f docker-compose.dev.yml run --rm django python manage.py test # Migraciones -docker-compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations -docker-compose -f docker-compose.dev.yml run --rm django python manage.py migrate +docker compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations +docker compose -f docker-compose.dev.yml run --rm django python manage.py migrate # Servidor desarrollo -docker-compose -f docker-compose.dev.yml up +docker compose -f docker-compose.dev.yml up # Shell Django -docker-compose -f docker-compose.dev.yml run --rm django python manage.py shell +docker compose -f docker-compose.dev.yml run --rm django python manage.py shell # Crear superuser -docker-compose -f docker-compose.dev.yml run --rm django python manage.py createsuperuser +docker compose -f docker-compose.dev.yml run --rm django python manage.py createsuperuser +``` + +### Staging (Testing Producción Local) + +```bash +# Iniciar servicios (PostgreSQL + Django con Gunicorn) +# Ya incluye el archivo .env.staging, listo para usar +docker compose -f docker-compose.staging.yml up -d + +# Migraciones +docker compose -f docker-compose.staging.yml run --rm django python manage.py migrate + +# Colectar archivos estáticos +docker compose -f docker-compose.staging.yml run --rm django python manage.py collectstatic --noinput + +# Crear superuser +docker compose -f docker-compose.staging.yml run --rm django python manage.py createsuperuser + +# Ver logs +docker compose -f docker-compose.staging.yml logs -f django + +# Detener servicios +docker compose -f docker-compose.staging.yml down ``` ### Producción (Production) @@ -110,22 +136,22 @@ cp .env.production.example .env.production # Editar .env.production con valores reales # Iniciar servicios (PostgreSQL + Django con Gunicorn) -docker-compose -f docker-compose.prod.yml up -d +docker compose -f docker-compose.prod.yml up -d # Migraciones -docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate +docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate # Colectar archivos estáticos -docker-compose -f docker-compose.prod.yml run --rm django python manage.py collectstatic --noinput +docker compose -f docker-compose.prod.yml run --rm django python manage.py collectstatic --noinput # Crear superuser -docker-compose -f docker-compose.prod.yml run --rm django python manage.py createsuperuser +docker compose -f docker-compose.prod.yml run --rm django python manage.py createsuperuser # Ver logs -docker-compose -f docker-compose.prod.yml logs -f django +docker compose -f docker-compose.prod.yml logs -f django # Detener servicios -docker-compose -f docker-compose.prod.yml down +docker compose -f docker-compose.prod.yml down ``` Nota: El volumen monta `tienda_ilusion/` en `/app/`, por lo que el path correcto es `python manage.py` (no `python tienda_ilusion/manage.py`). @@ -143,7 +169,7 @@ Nota: El volumen monta `tienda_ilusion/` en `/app/`, por lo que el path correcto ## Configuración de Ambientes -El proyecto soporta dos ambientes diferentes mediante la variable `DJANGO_ENV`: +El proyecto soporta tres ambientes diferentes mediante la variable `DJANGO_ENV`: ### Development (desarrollo local) - **Variable de ambiente**: `DJANGO_ENV=development` @@ -153,6 +179,16 @@ El proyecto soporta dos ambientes diferentes mediante la variable `DJANGO_ENV`: - **CORS**: Permisivo para desarrollo local - **Docker Compose**: `docker-compose.dev.yml` +### Staging (testing de producción local) +- **Variable de ambiente**: `DJANGO_ENV=staging` +- **Archivo de configuración**: `.env.staging` +- **Base de datos**: PostgreSQL (tienda_ilusion_staging) +- **DEBUG**: False (simula producción) +- **CORS**: Permisivo para localhost (sin SSL redirect) +- **Docker Compose**: `docker-compose.staging.yml` +- **Servidor**: Gunicorn con 4 workers +- **Puerto PostgreSQL**: 5433 (para no conflictuar con producción) + ### Production (producción) - **Variable de ambiente**: `DJANGO_ENV=production` - **Archivo de configuración**: `.env.production` (crear desde `.env.production.example`) @@ -167,6 +203,7 @@ El proyecto soporta dos ambientes diferentes mediante la variable `DJANGO_ENV`: La detección de ambiente es automática mediante la variable `DJANGO_ENV`. Docker Compose configura esta variable automáticamente según el archivo usado: - `docker-compose.dev.yml` → usa `.env.development` → carga `settings/development.py` +- `docker-compose.staging.yml` → usa `.env.staging` → carga `settings/staging.py` - `docker-compose.prod.yml` → usa `.env.production` → carga `settings/production.py` ### Variables de entorno requeridas @@ -176,6 +213,15 @@ La detección de ambiente es automática mediante la variable `DJANGO_ENV`. Dock - `DEBUG=True` - `TRYTON_HOST`, `TRYTON_DATABASE`, `TRYTON_USERNAME`, `TRYTON_PASSWORD` +**Staging** (`.env.staging`): +- `DJANGO_ENV=staging` +- `DEBUG=False` +- `SECRET_KEY` (para staging local, puede ser una key fija) +- `ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0` +- `CORS_ALLOWED_ORIGINS` (URLs localhost separadas por comas) +- `DB_NAME=tienda_ilusion_staging`, `DB_USER`, `DB_PASSWORD`, `DB_HOST=postgres`, `DB_PORT=5432` +- `TRYTON_HOST`, `TRYTON_DATABASE`, `TRYTON_USERNAME`, `TRYTON_PASSWORD` + **Producción** (`.env.production`): - `DJANGO_ENV=production` - `DEBUG=False` diff --git a/django.Dockerfile b/django.Dockerfile index 77a17dc..b2837b7 100644 --- a/django.Dockerfile +++ b/django.Dockerfile @@ -4,5 +4,3 @@ WORKDIR /app/ COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt - -CMD ["python", "manage.py", "runserver", "0.0.0.0:9090"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 1e71b79..cc0f7fb 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -18,7 +18,11 @@ services: - tienda_network restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-tienda_ilusion_user}"] + test: + [ + "CMD-SHELL", + "pg_isready -U ${POSTGRES_USER:-tienda_ilusion_user} -d ${POSTGRES_DB:-tienda_ilusion_prod}", + ] interval: 10s timeout: 5s retries: 5 diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 0000000..0678ff1 --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,73 @@ +version: "3.8" + +services: + postgres: + image: postgres:15-alpine + container_name: tienda_ilusion_postgres_staging + environment: + - POSTGRES_USER=${DB_USER:-tienda_ilusion_user} + - POSTGRES_DB=${DB_NAME:-tienda_ilusion_staging} + - POSTGRES_PASSWORD=${DB_PASSWORD:-staging_local_password} + env_file: + - .env.staging + volumes: + - postgres_staging_data:/var/lib/postgresql/data + ports: + - "5433:5432" # Puerto diferente para no conflictuar con prod + networks: + - tienda_staging_network + restart: unless-stopped + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -U ${DB_USER:-tienda_ilusion_user} -d ${DB_PASSWORD:-tienda_ilusion_staging}", + ] + interval: 10s + timeout: 5s + retries: 5 + + django: + build: + context: ./ + dockerfile: django.Dockerfile + container_name: tienda_ilusion_django_staging + env_file: + - .env.staging + environment: + - DJANGO_ENV=staging + - DB_HOST=postgres + - DB_USER=${DB_USER:-tienda_ilusion_user} + - DB_NAME=${DB_NAME:-tienda_ilusion_staging} + - DB_PASSWORD=${DB_PASSWORD:-staging_local_password} + volumes: + - ./tienda_ilusion:/app/ + - static_staging_volume:/app/staticfiles + - media_staging_volume:/app/media + - logs_staging_volume:/app/logs + ports: + - "8000:8000" + depends_on: + postgres: + condition: service_healthy + networks: + - tienda_staging_network + restart: unless-stopped + command: > + sh -c "python manage.py migrate && + python manage.py collectstatic --noinput && + gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4 --timeout 120 --reload" + +volumes: + postgres_staging_data: + driver: local + static_staging_volume: + driver: local + media_staging_volume: + driver: local + logs_staging_volume: + driver: local + +networks: + tienda_staging_network: + driver: bridge diff --git a/tienda_ilusion/config/settings/__init__.py b/tienda_ilusion/config/settings/__init__.py index 340446a..dcf4c6b 100644 --- a/tienda_ilusion/config/settings/__init__.py +++ b/tienda_ilusion/config/settings/__init__.py @@ -3,8 +3,9 @@ Settings module for tienda_ilusion project. This module automatically loads the appropriate settings based on the DJANGO_ENV environment variable. Valid values are: -- 'development' (default): Development settings -- 'production': Production settings +- 'development' (default): Development settings with SQLite +- 'staging': Staging settings for testing production config locally (PostgreSQL, no SSL) +- 'production': Production settings with full security (PostgreSQL, SSL, etc.) Usage: Set DJANGO_ENV environment variable before running Django: @@ -13,6 +14,10 @@ Usage: export DJANGO_ENV=development python manage.py runserver + Staging (test production locally): + export DJANGO_ENV=staging + python manage.py runserver + Production: export DJANGO_ENV=production gunicorn config.wsgi:application @@ -25,6 +30,8 @@ DJANGO_ENV = os.environ.get("DJANGO_ENV", "development") if DJANGO_ENV == "production": from .production import * +elif DJANGO_ENV == "staging": + from .staging import * elif DJANGO_ENV == "development": from .development import * else: