feat: Add deploy environment, Add pyprojectoml
This commit is contained in:
26
.env.development
Normal file
26
.env.development
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Development Environment Variables
|
||||||
|
# Este archivo contiene las variables de entorno para desarrollo local
|
||||||
|
|
||||||
|
# Django Environment
|
||||||
|
DJANGO_ENV=development
|
||||||
|
|
||||||
|
# Debug mode (True for development)
|
||||||
|
DEBUG=True
|
||||||
|
|
||||||
|
# Django Secret Key (insecure key for development only)
|
||||||
|
SECRET_KEY=django-insecure-development-key-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v
|
||||||
|
|
||||||
|
# Allowed hosts (comma-separated)
|
||||||
|
ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0
|
||||||
|
|
||||||
|
# CORS allowed origins (comma-separated)
|
||||||
|
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:7001,http://localhost:5173
|
||||||
|
|
||||||
|
# Database (SQLite by default in development)
|
||||||
|
# No additional DB configuration needed for SQLite
|
||||||
|
|
||||||
|
# Tryton ERP Configuration
|
||||||
|
TRYTON_HOST=localhost
|
||||||
|
TRYTON_DATABASE=tryton
|
||||||
|
TRYTON_USERNAME=admin
|
||||||
|
TRYTON_PASSWORD=admin
|
||||||
56
.env.production.example
Normal file
56
.env.production.example
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Production Environment Variables - EXAMPLE
|
||||||
|
# ¡IMPORTANTE! Copia este archivo a .env.production y completa con valores reales
|
||||||
|
# NO commitees .env.production con valores sensibles a git
|
||||||
|
|
||||||
|
# Django Environment
|
||||||
|
DJANGO_ENV=production
|
||||||
|
|
||||||
|
# Debug mode (MUST be False in production)
|
||||||
|
DEBUG=False
|
||||||
|
|
||||||
|
# Django Secret Key
|
||||||
|
# ¡IMPORTANTE! Genera una clave única y segura para producción
|
||||||
|
# Puedes generar una con: python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
|
||||||
|
SECRET_KEY=CHANGE-ME-TO-A-SECURE-RANDOM-SECRET-KEY-IN-PRODUCTION
|
||||||
|
|
||||||
|
# Allowed hosts (comma-separated domains)
|
||||||
|
# Ejemplo: ALLOWED_HOSTS=tiendailusion.com,www.tiendailusion.com,api.tiendailusion.com
|
||||||
|
ALLOWED_HOSTS=tiendailusion.com,www.tiendailusion.com
|
||||||
|
|
||||||
|
# CORS allowed origins (comma-separated URLs)
|
||||||
|
# Ejemplo: CORS_ALLOWED_ORIGINS=https://tiendailusion.com,https://www.tiendailusion.com
|
||||||
|
CORS_ALLOWED_ORIGINS=https://tiendailusion.com,https://www.tiendailusion.com
|
||||||
|
|
||||||
|
# CSRF Trusted Origins (comma-separated URLs)
|
||||||
|
# Debe incluir el protocolo (https://)
|
||||||
|
CSRF_TRUSTED_ORIGINS=https://tiendailusion.com,https://www.tiendailusion.com
|
||||||
|
|
||||||
|
# PostgreSQL Database Configuration
|
||||||
|
DB_NAME=tienda_ilusion_prod
|
||||||
|
DB_USER=tienda_ilusion_user
|
||||||
|
DB_PASSWORD=CHANGE-ME-TO-A-SECURE-DATABASE-PASSWORD
|
||||||
|
DB_HOST=postgres
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
# Email Configuration (para notificaciones y recuperación de contraseñas)
|
||||||
|
EMAIL_HOST=smtp.gmail.com
|
||||||
|
EMAIL_PORT=587
|
||||||
|
EMAIL_USE_TLS=True
|
||||||
|
EMAIL_HOST_USER=noreply@tiendailusion.com
|
||||||
|
EMAIL_HOST_PASSWORD=CHANGE-ME-TO-YOUR-EMAIL-PASSWORD
|
||||||
|
DEFAULT_FROM_EMAIL=noreply@tiendailusion.com
|
||||||
|
|
||||||
|
# Admin notifications
|
||||||
|
ADMIN_EMAIL=admin@tiendailusion.com
|
||||||
|
|
||||||
|
# Tryton ERP Configuration (Production)
|
||||||
|
TRYTON_HOST=tryton-production-server
|
||||||
|
TRYTON_DATABASE=tryton_production
|
||||||
|
TRYTON_USERNAME=tienda_ilusion_integration
|
||||||
|
TRYTON_PASSWORD=CHANGE-ME-TO-TRYTON-PASSWORD
|
||||||
|
|
||||||
|
# Optional: Redis URL for caching (if using Redis)
|
||||||
|
# REDIS_URL=redis://redis:6379/1
|
||||||
|
|
||||||
|
# Optional: Sentry DSN for error tracking
|
||||||
|
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
TRYTON_HOST=localhost
|
|
||||||
TRYTON_DATABASE=tryton
|
|
||||||
TRYTON_USERNAME=admin
|
|
||||||
TRYTON_PASSWORD=admin
|
|
||||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -327,9 +327,39 @@ pip-selfcheck.json
|
|||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/emacs,python,django,venv
|
# End of https://www.toptal.com/developers/gitignore/api/emacs,python,django,venv
|
||||||
|
|
||||||
|
# Project-specific ignores
|
||||||
/tienda_ilusion/don_confiao/static/frontend/
|
/tienda_ilusion/don_confiao/static/frontend/
|
||||||
/tienda_ilusion/don_confiao/frontend/don-confiao/.vite/
|
/tienda_ilusion/don_confiao/frontend/don-confiao/.vite/
|
||||||
/tienda_ilusion/don_confiao/frontend/don-confiao/.eslintrc.js
|
/tienda_ilusion/don_confiao/frontend/don-confiao/.eslintrc.js
|
||||||
/tienda_ilusion/don_confiao/frontend/don-confiao/.eslintrc-auto-import.json
|
/tienda_ilusion/don_confiao/frontend/don-confiao/.eslintrc-auto-import.json
|
||||||
/tienda_ilusion/don_confiao/frontend/don-confiao/.editorconfig
|
/tienda_ilusion/don_confiao/frontend/don-confiao/.editorconfig
|
||||||
/tienda_ilusion/don_confiao/frontend/don-confiao/.browserslistrc
|
/tienda_ilusion/don_confiao/frontend/don-confiao/.browserslistrc
|
||||||
|
|
||||||
|
# Environment files with sensitive data
|
||||||
|
.env.production
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Static files collected by Django
|
||||||
|
/tienda_ilusion/staticfiles/
|
||||||
|
staticfiles/
|
||||||
|
|
||||||
|
# Media files uploaded by users
|
||||||
|
/tienda_ilusion/media/
|
||||||
|
|
||||||
|
# Application logs
|
||||||
|
/tienda_ilusion/logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Database backups
|
||||||
|
backups/
|
||||||
|
*.sql
|
||||||
|
*.dump
|
||||||
|
|
||||||
|
# Docker volumes and data
|
||||||
|
postgres_data/
|
||||||
|
pgdata/
|
||||||
|
|
||||||
|
# IDE-specific
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|||||||
331
AGENTS.md
331
AGENTS.md
@@ -7,19 +7,21 @@ Backend Django con Django REST Framework
|
|||||||
```
|
```
|
||||||
don_confiao_backend/
|
don_confiao_backend/
|
||||||
├── requirements.txt # Dependencias Python
|
├── requirements.txt # Dependencias Python
|
||||||
├── docker-compose.yml # Configuración Docker
|
├── docker-compose.dev.yml # Docker Compose para desarrollo
|
||||||
├── django.Dockerfile # Dockerfile Django
|
├── docker-compose.prod.yml # Docker Compose para producción
|
||||||
├── .env # Variables de entorno
|
├── django.Dockerfile # Dockerfile Django
|
||||||
├── .env_example # Ejemplo de variables de entorno
|
├── .env.development # Variables de entorno desarrollo
|
||||||
├── README.rst # Documentación básica
|
├── .env.production.example # Ejemplo variables de entorno producción
|
||||||
├── Rakefile # Tareas rake
|
├── .env_example # Ejemplo de variables de entorno
|
||||||
├── doc/ # Documentación adicional
|
├── README.rst # Documentación básica
|
||||||
|
├── Rakefile # Tareas rake
|
||||||
|
├── doc/ # Documentación adicional
|
||||||
│ └── requests.org
|
│ └── requests.org
|
||||||
└── tienda_ilusion/ # Proyecto Django
|
└── tienda_ilusion/ # Proyecto Django
|
||||||
├── manage.py
|
├── manage.py
|
||||||
├── db.sqlite3 # Base de datos SQLite
|
├── db.sqlite3 # Base de datos SQLite (desarrollo)
|
||||||
├── don_confiao/ # App principal
|
├── don_confiao/ # App principal
|
||||||
│ ├── models.py # Modelos: Customer, Product, Sale, SaleLine, Payment, ReconciliationJar, AdminCode
|
│ ├── models.py # Modelos: Customer, Product, Sale, SaleLine, Payment, ReconciliationJar, AdminCode
|
||||||
│ ├── views.py
|
│ ├── views.py
|
||||||
│ ├── api_views.py
|
│ ├── api_views.py
|
||||||
│ ├── serializers.py
|
│ ├── serializers.py
|
||||||
@@ -27,16 +29,20 @@ don_confiao_backend/
|
|||||||
│ ├── admin.py
|
│ ├── admin.py
|
||||||
│ ├── urls.py
|
│ ├── urls.py
|
||||||
│ ├── export_csv.py
|
│ ├── export_csv.py
|
||||||
│ ├── tests/ # Tests
|
│ ├── tests/ # Tests
|
||||||
│ └── migrations/
|
│ └── migrations/
|
||||||
├── users/ # App de usuarios
|
├── users/ # App de usuarios
|
||||||
│ ├── models.py
|
│ ├── models.py
|
||||||
│ ├── views.py
|
│ ├── views.py
|
||||||
│ ├── serializers.py
|
│ ├── serializers.py
|
||||||
│ ├── urls.py
|
│ ├── urls.py
|
||||||
│ └── tests/
|
│ └── tests/
|
||||||
└── tienda_ilusion/ # Configuración Django
|
└── config/ # Configuración Django
|
||||||
├── settings.py
|
├── settings/ # Settings por ambiente
|
||||||
|
│ ├── __init__.py # Detección automática de ambiente
|
||||||
|
│ ├── base.py # Configuración compartida
|
||||||
|
│ ├── development.py # Configuración desarrollo
|
||||||
|
│ └── production.py # Configuración producción
|
||||||
├── urls.py
|
├── urls.py
|
||||||
├── wsgi.py
|
├── wsgi.py
|
||||||
└── asgi.py
|
└── asgi.py
|
||||||
@@ -48,6 +54,9 @@ don_confiao_backend/
|
|||||||
- django-cors-headers
|
- django-cors-headers
|
||||||
- djangorestframework-simplejwt
|
- djangorestframework-simplejwt
|
||||||
- sabatron-tryton-rpc-client==7.4.0 (integración con Tryton ERP)
|
- sabatron-tryton-rpc-client==7.4.0 (integración con Tryton ERP)
|
||||||
|
- psycopg2-binary (driver PostgreSQL para producción)
|
||||||
|
- gunicorn (servidor WSGI para producción)
|
||||||
|
- python-decouple (gestión de variables de entorno)
|
||||||
|
|
||||||
## Modelos Principales (don_confiao/models.py)
|
## Modelos Principales (don_confiao/models.py)
|
||||||
- **Customer**: Clientes (name, address, email, phone, external_id)
|
- **Customer**: Clientes (name, address, email, phone, external_id)
|
||||||
@@ -73,22 +82,50 @@ don_confiao_backend/
|
|||||||
|
|
||||||
El proyecto se ejecuta con docker-compose. Todos los comandos `manage.py` deben ejecutarse dentro del contenedor:
|
El proyecto se ejecuta con docker-compose. Todos los comandos `manage.py` deben ejecutarse dentro del contenedor:
|
||||||
|
|
||||||
|
### Desarrollo (Development)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Ejecutar tests
|
# Ejecutar tests
|
||||||
docker-compose run --rm django python manage.py test
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py test
|
||||||
|
|
||||||
# Migraciones
|
# Migraciones
|
||||||
docker-compose run --rm django python manage.py makemigrations
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations
|
||||||
docker-compose run --rm django python manage.py migrate
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py migrate
|
||||||
|
|
||||||
# Servidor desarrollo
|
# Servidor desarrollo
|
||||||
docker-compose up
|
docker-compose -f docker-compose.dev.yml up
|
||||||
|
|
||||||
# Shell Django
|
# Shell Django
|
||||||
docker-compose run --rm django python manage.py shell
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py shell
|
||||||
|
|
||||||
# Crear superuser
|
# Crear superuser
|
||||||
docker-compose run --rm django python manage.py createsuperuser
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Producción (Production)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Primero, copiar .env.production.example a .env.production y configurar variables
|
||||||
|
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
|
||||||
|
|
||||||
|
# Migraciones
|
||||||
|
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
|
||||||
|
|
||||||
|
# Crear superuser
|
||||||
|
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
|
||||||
|
|
||||||
|
# Detener servicios
|
||||||
|
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`).
|
Nota: El volumen monta `tienda_ilusion/` en `/app/`, por lo que el path correcto es `python manage.py` (no `python tienda_ilusion/manage.py`).
|
||||||
@@ -96,13 +133,255 @@ Nota: El volumen monta `tienda_ilusion/` en `/app/`, por lo que el path correcto
|
|||||||
## Tests
|
## Tests
|
||||||
- Framework: Django unittest
|
- Framework: Django unittest
|
||||||
- Directorio: don_confiao/tests/
|
- Directorio: don_confiao/tests/
|
||||||
- Ejecutar: `docker-compose run --rm django python manage.py test`
|
- Ejecutar: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py test`
|
||||||
|
|
||||||
## Comandos Útiles (dentro del contenedor)
|
## Comandos Útiles (dentro del contenedor)
|
||||||
- Migraciones: `docker-compose run --rm django python manage.py makemigrations && docker-compose run --rm django python manage.py migrate`
|
- 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`
|
||||||
- Servidor desarrollo: `docker-compose up`
|
- Servidor desarrollo: `docker-compose -f docker-compose.dev.yml up`
|
||||||
- Shell Django: `docker-compose run --rm django python manage.py shell`
|
- Shell Django: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py shell`
|
||||||
- Superuser: `docker-compose run --rm django python manage.py createsuperuser`
|
- Superuser: `docker-compose -f docker-compose.dev.yml run --rm django python manage.py createsuperuser`
|
||||||
|
|
||||||
|
## Configuración de Ambientes
|
||||||
|
|
||||||
|
El proyecto soporta dos ambientes diferentes mediante la variable `DJANGO_ENV`:
|
||||||
|
|
||||||
|
### Development (desarrollo local)
|
||||||
|
- **Variable de ambiente**: `DJANGO_ENV=development`
|
||||||
|
- **Archivo de configuración**: `.env.development`
|
||||||
|
- **Base de datos**: SQLite (db.sqlite3)
|
||||||
|
- **DEBUG**: True
|
||||||
|
- **CORS**: Permisivo para desarrollo local
|
||||||
|
- **Docker Compose**: `docker-compose.dev.yml`
|
||||||
|
|
||||||
|
### Production (producción)
|
||||||
|
- **Variable de ambiente**: `DJANGO_ENV=production`
|
||||||
|
- **Archivo de configuración**: `.env.production` (crear desde `.env.production.example`)
|
||||||
|
- **Base de datos**: PostgreSQL
|
||||||
|
- **DEBUG**: False
|
||||||
|
- **Seguridad**: HTTPS, HSTS, secure cookies, CSRF protections
|
||||||
|
- **Docker Compose**: `docker-compose.prod.yml`
|
||||||
|
- **Servidor**: Gunicorn con 4 workers
|
||||||
|
|
||||||
|
### Cambiar entre ambientes
|
||||||
|
|
||||||
|
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.prod.yml` → usa `.env.production` → carga `settings/production.py`
|
||||||
|
|
||||||
|
### Variables de entorno requeridas
|
||||||
|
|
||||||
|
**Desarrollo** (`.env.development`):
|
||||||
|
- `DJANGO_ENV=development`
|
||||||
|
- `DEBUG=True`
|
||||||
|
- `TRYTON_HOST`, `TRYTON_DATABASE`, `TRYTON_USERNAME`, `TRYTON_PASSWORD`
|
||||||
|
|
||||||
|
**Producción** (`.env.production`):
|
||||||
|
- `DJANGO_ENV=production`
|
||||||
|
- `DEBUG=False`
|
||||||
|
- `SECRET_KEY` (generar una nueva y segura)
|
||||||
|
- `ALLOWED_HOSTS` (dominios separados por comas)
|
||||||
|
- `CORS_ALLOWED_ORIGINS` (URLs separadas por comas)
|
||||||
|
- `CSRF_TRUSTED_ORIGINS` (URLs separadas por comas)
|
||||||
|
- `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_PORT`
|
||||||
|
- `EMAIL_HOST`, `EMAIL_PORT`, `EMAIL_HOST_USER`, `EMAIL_HOST_PASSWORD`
|
||||||
|
- `TRYTON_HOST`, `TRYTON_DATABASE`, `TRYTON_USERNAME`, `TRYTON_PASSWORD`
|
||||||
|
|
||||||
|
Ver `.env.production.example` para todos los detalles.
|
||||||
|
|
||||||
|
## Scripts Útiles
|
||||||
|
|
||||||
|
El proyecto incluye scripts para facilitar operaciones comunes:
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
Verifica el estado de todos los servicios:
|
||||||
|
```bash
|
||||||
|
# Verificar ambiente de desarrollo
|
||||||
|
./scripts/health-check.sh dev
|
||||||
|
|
||||||
|
# Verificar ambiente de producción
|
||||||
|
./scripts/health-check.sh prod
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup de Base de Datos (Producción)
|
||||||
|
Crea un backup comprimido de la base de datos PostgreSQL:
|
||||||
|
```bash
|
||||||
|
./scripts/backup-db.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Los backups se guardan en `backups/` y se mantienen por 7 días automáticamente.
|
||||||
|
|
||||||
|
### Restore de Base de Datos (Producción)
|
||||||
|
Restaura un backup de la base de datos:
|
||||||
|
```bash
|
||||||
|
./scripts/restore-backup.sh <archivo_backup.sql.gz>
|
||||||
|
```
|
||||||
|
|
||||||
|
**ADVERTENCIA**: Esto reemplazará la base de datos actual. Se crea un backup de seguridad antes de restaurar.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Errores Comunes
|
||||||
|
|
||||||
|
#### 1. Error: "database 'tienda_ilusion_user' does not exist"
|
||||||
|
**Síntoma**: Aparece en los logs de PostgreSQL
|
||||||
|
```
|
||||||
|
FATAL: database "tienda_ilusion_user" does not exist
|
||||||
|
```
|
||||||
|
|
||||||
|
**Causa**: Este es un comportamiento normal de PostgreSQL. Cuando te conectas sin especificar una base de datos, PostgreSQL intenta conectarse a una base con el mismo nombre del usuario.
|
||||||
|
|
||||||
|
**Solución**: Este error NO afecta el funcionamiento de Django. Para verificar que la base de datos correcta existe:
|
||||||
|
```bash
|
||||||
|
docker exec tienda_ilusion_postgres_prod psql -U tienda_ilusion_user -d tienda_ilusion_prod -c '\l'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Error: "required variable DB_PASSWORD is missing a value"
|
||||||
|
**Síntoma**: Docker Compose falla al iniciar con error de variable faltante
|
||||||
|
|
||||||
|
**Causa**: El archivo `.env.production` no existe o tiene valores placeholder
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
1. Copiar el archivo de ejemplo: `cp .env.production.example .env.production`
|
||||||
|
2. Editar `.env.production` y reemplazar todos los valores `CHANGE-ME-...` con valores reales
|
||||||
|
3. Generar SECRET_KEY segura:
|
||||||
|
```bash
|
||||||
|
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Error: "ModuleNotFoundError" o importación fallida
|
||||||
|
**Síntoma**: Django no puede importar módulos después de cambiar configuración
|
||||||
|
|
||||||
|
**Causa**: Dependencias no instaladas o contenedor con caché viejo
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Reconstruir contenedores
|
||||||
|
docker-compose -f docker-compose.dev.yml build --no-cache
|
||||||
|
docker-compose -f docker-compose.dev.yml up
|
||||||
|
|
||||||
|
# Para producción
|
||||||
|
docker-compose -f docker-compose.prod.yml build --no-cache
|
||||||
|
docker-compose -f docker-compose.prod.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Error: "django.db.utils.OperationalError: could not connect to server"
|
||||||
|
**Síntoma**: Django no puede conectarse a PostgreSQL
|
||||||
|
|
||||||
|
**Causa**: PostgreSQL aún no está listo cuando Django intenta conectar
|
||||||
|
|
||||||
|
**Solución**: El healthcheck en `docker-compose.prod.yml` maneja esto automáticamente. Si el problema persiste:
|
||||||
|
```bash
|
||||||
|
# Verificar que PostgreSQL esté corriendo
|
||||||
|
docker ps | grep postgres
|
||||||
|
|
||||||
|
# Ver logs de PostgreSQL
|
||||||
|
docker logs tienda_ilusion_postgres_prod
|
||||||
|
|
||||||
|
# Reiniciar servicios en orden
|
||||||
|
docker-compose -f docker-compose.prod.yml restart postgres
|
||||||
|
docker-compose -f docker-compose.prod.yml restart django
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. Error: "CSRF verification failed"
|
||||||
|
**Síntoma**: Errores CSRF en producción
|
||||||
|
|
||||||
|
**Causa**: `CSRF_TRUSTED_ORIGINS` no configurado correctamente
|
||||||
|
|
||||||
|
**Solución**: En `.env.production`, asegurar que `CSRF_TRUSTED_ORIGINS` incluya el protocolo:
|
||||||
|
```bash
|
||||||
|
CSRF_TRUSTED_ORIGINS=https://tudominio.com,https://www.tudominio.com
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. Static files no se sirven en producción
|
||||||
|
**Síntoma**: CSS/JS no cargan, errores 404 para archivos estáticos
|
||||||
|
|
||||||
|
**Causa**: `collectstatic` no ejecutado o configuración de nginx incorrecta
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Ejecutar collectstatic
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Verificar que los archivos existen
|
||||||
|
docker exec tienda_ilusion_django_prod ls -la /app/staticfiles
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. Migraciones pendientes después de deployment
|
||||||
|
**Síntoma**: Errores de base de datos o tablas faltantes
|
||||||
|
|
||||||
|
**Causa**: Migraciones no aplicadas en producción
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```bash
|
||||||
|
# Ver estado de migraciones
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py showmigrations
|
||||||
|
|
||||||
|
# Aplicar migraciones pendientes
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comandos de Diagnóstico
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ver todos los contenedores
|
||||||
|
docker ps -a
|
||||||
|
|
||||||
|
# Ver logs en tiempo real
|
||||||
|
docker-compose -f docker-compose.prod.yml logs -f
|
||||||
|
|
||||||
|
# Ver logs solo de Django
|
||||||
|
docker logs -f tienda_ilusion_django_prod
|
||||||
|
|
||||||
|
# Ver logs solo de PostgreSQL
|
||||||
|
docker logs -f tienda_ilusion_postgres_prod
|
||||||
|
|
||||||
|
# Ejecutar shell en contenedor Django
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django bash
|
||||||
|
|
||||||
|
# Ejecutar Django shell
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py shell
|
||||||
|
|
||||||
|
# Conectar a PostgreSQL
|
||||||
|
docker exec -it tienda_ilusion_postgres_prod psql -U tienda_ilusion_user -d tienda_ilusion_prod
|
||||||
|
|
||||||
|
# Verificar variables de entorno en contenedor
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django env | grep DJANGO
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance y Optimización
|
||||||
|
|
||||||
|
Si experimentas problemas de rendimiento:
|
||||||
|
|
||||||
|
1. **Verificar recursos del contenedor**:
|
||||||
|
```bash
|
||||||
|
docker stats
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Ajustar workers de Gunicorn** (editar `docker-compose.prod.yml`):
|
||||||
|
```yaml
|
||||||
|
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 8 --timeout 120
|
||||||
|
```
|
||||||
|
Regla general: `workers = (2 x CPU cores) + 1`
|
||||||
|
|
||||||
|
3. **Habilitar persistent connections**: Ya configurado en `settings/production.py` con `CONN_MAX_AGE = 600`
|
||||||
|
|
||||||
|
4. **Considerar agregar Redis para caché**: Descomentar sección de Redis en `settings/production.py`
|
||||||
|
|
||||||
|
### Seguridad
|
||||||
|
|
||||||
|
Antes de ir a producción, verificar:
|
||||||
|
|
||||||
|
- [ ] `DEBUG=False` en `.env.production`
|
||||||
|
- [ ] `SECRET_KEY` único y seguro generado
|
||||||
|
- [ ] `ALLOWED_HOSTS` configurado con dominios reales
|
||||||
|
- [ ] `CORS_ALLOWED_ORIGINS` limitado a orígenes confiables
|
||||||
|
- [ ] `CSRF_TRUSTED_ORIGINS` configurado correctamente
|
||||||
|
- [ ] Contraseñas de base de datos seguras
|
||||||
|
- [ ] `.env.production` NO commiteado a git
|
||||||
|
- [ ] HTTPS habilitado (certificados SSL/TLS configurados)
|
||||||
|
- [ ] Firewall configurado (solo puertos necesarios abiertos)
|
||||||
|
- [ ] Backups automáticos configurados
|
||||||
|
|
||||||
## Integraciones
|
## Integraciones
|
||||||
- **Tryton ERP**: Integración mediante sabatron-tryton-rpc-client para sincronización de clientes, productos y ventas
|
- **Tryton ERP**: Integración mediante sabatron-tryton-rpc-client para sincronización de clientes, productos y ventas
|
||||||
|
|||||||
269
README.md
Normal file
269
README.md
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
# Don Confiao Backend - Tienda Ilusion
|
||||||
|
|
||||||
|
Backend Django con Django REST Framework para el sistema de punto de venta Tienda Ilusion.
|
||||||
|
|
||||||
|
## Características
|
||||||
|
|
||||||
|
- 🔐 Autenticación JWT con djangorestframework-simplejwt
|
||||||
|
- 🗄️ Soporte multi-ambiente (desarrollo/producción)
|
||||||
|
- 🐘 PostgreSQL para producción, SQLite para desarrollo
|
||||||
|
- 🔄 Integración con Tryton ERP
|
||||||
|
- 📦 Docker Compose para fácil deployment
|
||||||
|
- 🛡️ Configuración de seguridad completa para producción
|
||||||
|
- 📊 API REST completa para gestión de ventas, productos y clientes
|
||||||
|
|
||||||
|
## Requisitos Previos
|
||||||
|
|
||||||
|
- Docker & Docker Compose
|
||||||
|
- Python 3.11+ (para desarrollo local sin Docker)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## Inicio Rápido
|
||||||
|
|
||||||
|
### Desarrollo Local
|
||||||
|
|
||||||
|
1. **Clonar el repositorio**
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd don_confiao_backend
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Iniciar servicios de desarrollo**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dev.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Aplicar migraciones**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Crear superusuario**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Acceder a la aplicación**
|
||||||
|
- API: http://localhost:7000
|
||||||
|
- Admin: http://localhost:7000/admin
|
||||||
|
|
||||||
|
### Producción
|
||||||
|
|
||||||
|
1. **Configurar variables de entorno**
|
||||||
|
```bash
|
||||||
|
cp .env.production.example .env.production
|
||||||
|
# Editar .env.production con valores reales
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Generar SECRET_KEY segura**
|
||||||
|
```bash
|
||||||
|
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Iniciar servicios**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.prod.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Las migraciones y collectstatic se ejecutan automáticamente**
|
||||||
|
- Si necesitas ejecutarlas manualmente:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py migrate
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py collectstatic --noinput
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Crear superusuario**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.prod.yml exec django python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
## Estructura del Proyecto
|
||||||
|
|
||||||
|
```
|
||||||
|
don_confiao_backend/
|
||||||
|
├── tienda_ilusion/ # Proyecto Django
|
||||||
|
│ ├── config/ # Configuración principal
|
||||||
|
│ │ └── settings/ # Settings por ambiente
|
||||||
|
│ │ ├── base.py # Configuración compartida
|
||||||
|
│ │ ├── development.py # Desarrollo
|
||||||
|
│ │ └── production.py # Producción
|
||||||
|
│ ├── don_confiao/ # App principal
|
||||||
|
│ └── users/ # App de usuarios
|
||||||
|
├── scripts/ # Scripts de utilidad
|
||||||
|
│ ├── health-check.sh # Verificación de salud
|
||||||
|
│ ├── backup-db.sh # Backup de base de datos
|
||||||
|
│ └── restore-backup.sh # Restore de backup
|
||||||
|
├── docker-compose.dev.yml # Docker Compose desarrollo
|
||||||
|
├── docker-compose.prod.yml # Docker Compose producción
|
||||||
|
└── requirements.txt # Dependencias Python
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ambientes
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- Base de datos: SQLite
|
||||||
|
- Debug: Habilitado
|
||||||
|
- CORS: Permisivo
|
||||||
|
- Server: Django development server
|
||||||
|
- Puerto: 7000
|
||||||
|
|
||||||
|
### Production
|
||||||
|
- Base de datos: PostgreSQL
|
||||||
|
- Debug: Deshabilitado
|
||||||
|
- CORS: Configurado por dominio
|
||||||
|
- Server: Gunicorn (4 workers)
|
||||||
|
- Puerto: 8000
|
||||||
|
- Seguridad: HTTPS, HSTS, secure cookies
|
||||||
|
|
||||||
|
## Comandos Útiles
|
||||||
|
|
||||||
|
### Desarrollo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ejecutar tests
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py test
|
||||||
|
|
||||||
|
# Shell de Django
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py shell
|
||||||
|
|
||||||
|
# Crear migraciones
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py makemigrations
|
||||||
|
|
||||||
|
# Ver logs
|
||||||
|
docker-compose -f docker-compose.dev.yml logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Producción
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ver logs
|
||||||
|
docker-compose -f docker-compose.prod.yml logs -f django
|
||||||
|
|
||||||
|
# Backup de base de datos
|
||||||
|
./scripts/backup-db.sh
|
||||||
|
|
||||||
|
# Restore de backup
|
||||||
|
./scripts/restore-backup.sh backups/tienda_ilusion_backup_YYYYMMDD_HHMMSS.sql.gz
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
./scripts/health-check.sh prod
|
||||||
|
|
||||||
|
# Reiniciar servicios
|
||||||
|
docker-compose -f docker-compose.prod.yml restart
|
||||||
|
|
||||||
|
# Detener servicios
|
||||||
|
docker-compose -f docker-compose.prod.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
La API REST está disponible en `/api/`. Principales endpoints:
|
||||||
|
|
||||||
|
- `/api/token/` - Obtener token JWT
|
||||||
|
- `/api/token/refresh/` - Refrescar token JWT
|
||||||
|
- `/api/customers/` - Gestión de clientes
|
||||||
|
- `/api/products/` - Gestión de productos
|
||||||
|
- `/api/sales/` - Gestión de ventas
|
||||||
|
- `/admin/` - Panel de administración Django
|
||||||
|
|
||||||
|
## Integración con Tryton ERP
|
||||||
|
|
||||||
|
El sistema se integra con Tryton ERP para sincronización de:
|
||||||
|
- Clientes
|
||||||
|
- Productos
|
||||||
|
- Ventas
|
||||||
|
|
||||||
|
Configurar las variables de entorno de Tryton en `.env.development` o `.env.production`:
|
||||||
|
```bash
|
||||||
|
TRYTON_HOST=your-tryton-server
|
||||||
|
TRYTON_DATABASE=your-database
|
||||||
|
TRYTON_USERNAME=your-username
|
||||||
|
TRYTON_PASSWORD=your-password
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup y Restore
|
||||||
|
|
||||||
|
### Crear Backup
|
||||||
|
```bash
|
||||||
|
./scripts/backup-db.sh
|
||||||
|
```
|
||||||
|
Los backups se guardan en `backups/` y se mantienen por 7 días.
|
||||||
|
|
||||||
|
### Restaurar Backup
|
||||||
|
```bash
|
||||||
|
./scripts/restore-backup.sh backups/backup_file.sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
Ver la sección de Troubleshooting en [AGENTS.md](AGENTS.md) para soluciones a problemas comunes.
|
||||||
|
|
||||||
|
### Problemas Comunes
|
||||||
|
|
||||||
|
1. **Error de conexión a base de datos**: Verificar que PostgreSQL esté corriendo
|
||||||
|
2. **CSRF errors**: Verificar `CSRF_TRUSTED_ORIGINS` en `.env.production`
|
||||||
|
3. **Static files no cargan**: Ejecutar `collectstatic`
|
||||||
|
4. **Errores de migración**: Verificar estado con `showmigrations`
|
||||||
|
|
||||||
|
## Seguridad
|
||||||
|
|
||||||
|
### Checklist de Producción
|
||||||
|
|
||||||
|
Antes de desplegar a producción:
|
||||||
|
|
||||||
|
- [ ] `DEBUG=False` en `.env.production`
|
||||||
|
- [ ] `SECRET_KEY` única y segura generada
|
||||||
|
- [ ] `ALLOWED_HOSTS` configurado correctamente
|
||||||
|
- [ ] `CORS_ALLOWED_ORIGINS` limitado a dominios confiables
|
||||||
|
- [ ] Contraseñas de base de datos seguras
|
||||||
|
- [ ] HTTPS habilitado con certificados SSL/TLS válidos
|
||||||
|
- [ ] Firewall configurado
|
||||||
|
- [ ] Backups automáticos configurados
|
||||||
|
- [ ] Monitoreo de logs configurado
|
||||||
|
|
||||||
|
## Desarrollo
|
||||||
|
|
||||||
|
### Agregar nuevas dependencias
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Agregar a requirements.txt
|
||||||
|
echo "nueva-dependencia==version" >> requirements.txt
|
||||||
|
|
||||||
|
# Reconstruir contenedor
|
||||||
|
docker-compose -f docker-compose.dev.yml build --no-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ejecutar todos los tests
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py test
|
||||||
|
|
||||||
|
# Ejecutar tests de una app específica
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django python manage.py test don_confiao
|
||||||
|
|
||||||
|
# Con coverage
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django coverage run --source='.' manage.py test
|
||||||
|
docker-compose -f docker-compose.dev.yml run --rm django coverage report
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contribuir
|
||||||
|
|
||||||
|
1. Fork el proyecto
|
||||||
|
2. Crear branch de feature (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. Commit cambios (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. Push al branch (`git push origin feature/AmazingFeature`)
|
||||||
|
5. Abrir Pull Request
|
||||||
|
|
||||||
|
## Licencia
|
||||||
|
|
||||||
|
[Especificar licencia]
|
||||||
|
|
||||||
|
## Contacto
|
||||||
|
|
||||||
|
[Información de contacto]
|
||||||
|
|
||||||
|
## Documentación Adicional
|
||||||
|
|
||||||
|
- [AGENTS.md](AGENTS.md) - Contexto completo del proyecto y troubleshooting
|
||||||
|
- [.env.production.example](.env.production.example) - Ejemplo de variables de producción
|
||||||
24
docker-compose.dev.yml
Normal file
24
docker-compose.dev.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
django:
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: django.Dockerfile
|
||||||
|
container_name: tienda_ilusion_django_dev
|
||||||
|
env_file:
|
||||||
|
- .env.development
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=development
|
||||||
|
volumes:
|
||||||
|
- ./tienda_ilusion:/app/
|
||||||
|
ports:
|
||||||
|
- "7000:9090"
|
||||||
|
networks:
|
||||||
|
- tienda_network_dev
|
||||||
|
command: python manage.py runserver 0.0.0.0:9090
|
||||||
|
|
||||||
|
networks:
|
||||||
|
tienda_network_dev:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
87
docker-compose.prod.yml
Normal file
87
docker-compose.prod.yml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: tienda_ilusion_postgres_prod
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${DB_USER:-tienda_ilusion_user}
|
||||||
|
- POSTGRES_DB=${DB_NAME:-tienda_ilusion_prod}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD:-tienda_ilusion_pass}
|
||||||
|
env_file:
|
||||||
|
- .env.production
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
networks:
|
||||||
|
- tienda_network
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-tienda_ilusion_user}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
django:
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: django.Dockerfile
|
||||||
|
container_name: tienda_ilusion_django_prod
|
||||||
|
env_file:
|
||||||
|
- .env.production
|
||||||
|
environment:
|
||||||
|
- DJANGO_ENV=production
|
||||||
|
- DB_HOST=postgres
|
||||||
|
- DB_USER=${DB_USER:-tienda_ilusion_user}
|
||||||
|
- DB_NAME=${DB_NAME:-tienda_ilusion_prod}
|
||||||
|
- DB_PASSWORD=${DB_PASSWORD:-tienda_ilusion_pass}
|
||||||
|
volumes:
|
||||||
|
- ./tienda_ilusion:/app/
|
||||||
|
- static_volume:/app/staticfiles
|
||||||
|
- media_volume:/app/media
|
||||||
|
- logs_volume:/app/logs
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- tienda_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"
|
||||||
|
|
||||||
|
# Optional: Nginx reverse proxy para servir archivos estáticos
|
||||||
|
# nginx:
|
||||||
|
# image: nginx:alpine
|
||||||
|
# container_name: tienda_ilusion_nginx
|
||||||
|
# ports:
|
||||||
|
# - "80:80"
|
||||||
|
# - "443:443"
|
||||||
|
# volumes:
|
||||||
|
# - ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
# - static_volume:/var/www/static:ro
|
||||||
|
# - media_volume:/var/www/media:ro
|
||||||
|
# - ./ssl:/etc/nginx/ssl:ro
|
||||||
|
# depends_on:
|
||||||
|
# - django
|
||||||
|
# networks:
|
||||||
|
# - tienda_network
|
||||||
|
# restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
static_volume:
|
||||||
|
driver: local
|
||||||
|
media_volume:
|
||||||
|
driver: local
|
||||||
|
logs_volume:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
tienda_network:
|
||||||
|
driver: bridge
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
services:
|
|
||||||
django:
|
|
||||||
build:
|
|
||||||
context: ./
|
|
||||||
dockerfile: django.Dockerfile
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
volumes:
|
|
||||||
- ./tienda_ilusion:/app/
|
|
||||||
ports:
|
|
||||||
- "7000:9090"
|
|
||||||
|
|
||||||
157
poetry.lock
generated
Normal file
157
poetry.lock
generated
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asgiref"
|
||||||
|
version = "3.11.1"
|
||||||
|
description = "ASGI specs, helper code, and adapters"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133"},
|
||||||
|
{file = "asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django"
|
||||||
|
version = "6.0.5"
|
||||||
|
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.12"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0"},
|
||||||
|
{file = "django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
asgiref = ">=3.9.1"
|
||||||
|
sqlparse = ">=0.5.0"
|
||||||
|
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
argon2 = ["argon2-cffi (>=23.1.0)"]
|
||||||
|
bcrypt = ["bcrypt (>=4.1.1)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-cors-headers"
|
||||||
|
version = "4.9.0"
|
||||||
|
description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "django_cors_headers-4.9.0-py3-none-any.whl", hash = "sha256:15c7f20727f90044dcee2216a9fd7303741a864865f0c3657e28b7056f61b449"},
|
||||||
|
{file = "django_cors_headers-4.9.0.tar.gz", hash = "sha256:fe5d7cb59fdc2c8c646ce84b727ac2bca8912a247e6e68e1fb507372178e59e8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
asgiref = ">=3.6"
|
||||||
|
django = ">=4.2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "djangorestframework"
|
||||||
|
version = "3.17.1"
|
||||||
|
description = "Web APIs for Django, made easy."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "djangorestframework-3.17.1-py3-none-any.whl", hash = "sha256:c3c74dd3e83a5a3efc37b3c18d92bd6f86a6791c7b7d4dff62bb068500e76457"},
|
||||||
|
{file = "djangorestframework-3.17.1.tar.gz", hash = "sha256:a6def5f447fe78ff853bff1d47a3c59bf38f5434b031780b351b0c73a62db1a5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
django = ">=4.2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "djangorestframework-simplejwt"
|
||||||
|
version = "5.5.1"
|
||||||
|
description = "A minimal JSON Web Token authentication plugin for Django REST Framework"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "djangorestframework_simplejwt-5.5.1-py3-none-any.whl", hash = "sha256:2c30f3707053d384e9f315d11c2daccfcb548d4faa453111ca19a542b732e469"},
|
||||||
|
{file = "djangorestframework_simplejwt-5.5.1.tar.gz", hash = "sha256:e72c5572f51d7803021288e2057afcbd03f17fe11d484096f40a460abc76e87f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
django = ">=4.2"
|
||||||
|
djangorestframework = ">=3.14"
|
||||||
|
pyjwt = ">=1.7.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
crypto = ["cryptography (>=3.3.1)"]
|
||||||
|
dev = ["Sphinx", "cryptography", "freezegun", "ipython", "pre-commit", "pytest", "pytest-cov", "pytest-django", "pytest-watch", "pytest-xdist", "python-jose (==3.3.0)", "pyupgrade", "ruff", "sphinx_rtd_theme (>=0.1.9)", "tox", "twine", "wheel", "yesqa"]
|
||||||
|
doc = ["Sphinx", "sphinx_rtd_theme (>=0.1.9)"]
|
||||||
|
lint = ["pre-commit", "pyupgrade", "ruff", "yesqa"]
|
||||||
|
python-jose = ["python-jose (==3.3.0)"]
|
||||||
|
test = ["cryptography", "freezegun", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "tox"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyjwt"
|
||||||
|
version = "2.12.1"
|
||||||
|
description = "JSON Web Token implementation in Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c"},
|
||||||
|
{file = "pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
crypto = ["cryptography (>=3.4.0)"]
|
||||||
|
dev = ["coverage[toml] (==7.10.7)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=8.4.2,<9.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
tests = ["coverage[toml] (==7.10.7)", "pytest (>=8.4.2,<9.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sabatron-tryton-rpc-client"
|
||||||
|
version = "7.4.0"
|
||||||
|
description = "Python RPC Client for Tryton"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "sabatron_tryton_rpc_client-7.4.0-py3-none-any.whl", hash = "sha256:131d8d9d6a1dc1d556d4806fa7750ca637247f021881dca5c2855d8a44e4bd45"},
|
||||||
|
{file = "sabatron_tryton_rpc_client-7.4.0.tar.gz", hash = "sha256:3d3ededd1a8488463d6338cd7d8b2a271834ba42fc220ebb8e15e983c0e5b19d"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlparse"
|
||||||
|
version = "0.5.5"
|
||||||
|
description = "A non-validating SQL parser."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba"},
|
||||||
|
{file = "sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["build"]
|
||||||
|
doc = ["sphinx"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tzdata"
|
||||||
|
version = "2026.2"
|
||||||
|
description = "Provider of IANA time zone data"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2"
|
||||||
|
groups = ["main"]
|
||||||
|
markers = "sys_platform == \"win32\""
|
||||||
|
files = [
|
||||||
|
{file = "tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7"},
|
||||||
|
{file = "tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.1"
|
||||||
|
python-versions = ">=3.14"
|
||||||
|
content-hash = "f477382ae6c0dca55653b5c1b6784e0988a757c44c8d4e5d08417295c666cbc0"
|
||||||
21
pyproject.toml
Normal file
21
pyproject.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[project]
|
||||||
|
name = "don-confiao-backend"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Shop for Recreo store"
|
||||||
|
authors = [
|
||||||
|
{name = "aserrador",email = "alejandro.ayala@onecluster.org"}
|
||||||
|
]
|
||||||
|
license = {text = "GPL-3.0-or-later"}
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
dependencies = [
|
||||||
|
"django (>=6.0.5,<7.0.0)",
|
||||||
|
"djangorestframework (>=3.17.1,<4.0.0)",
|
||||||
|
"django-cors-headers (>=4.9.0,<5.0.0)",
|
||||||
|
"djangorestframework-simplejwt (>=5.5.1,<6.0.0)",
|
||||||
|
"sabatron-tryton-rpc-client (>=7.4.0,<8.0.0)"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
@@ -3,3 +3,15 @@ djangorestframework
|
|||||||
django-cors-headers
|
django-cors-headers
|
||||||
djangorestframework-simplejwt
|
djangorestframework-simplejwt
|
||||||
sabatron-tryton-rpc-client==7.4.0
|
sabatron-tryton-rpc-client==7.4.0
|
||||||
|
|
||||||
|
# Database drivers
|
||||||
|
psycopg2-binary # PostgreSQL driver for production
|
||||||
|
|
||||||
|
# Production server
|
||||||
|
gunicorn # WSGI HTTP Server for production
|
||||||
|
|
||||||
|
# Environment variable management
|
||||||
|
python-decouple # Manage environment variables and settings
|
||||||
|
|
||||||
|
# Optional: Static files serving in production
|
||||||
|
# whitenoise # Serve static files efficiently
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ import os
|
|||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||||
|
|
||||||
application = get_asgi_application()
|
application = get_asgi_application()
|
||||||
36
tienda_ilusion/config/settings/__init__.py
Normal file
36
tienda_ilusion/config/settings/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
Set DJANGO_ENV environment variable before running Django:
|
||||||
|
|
||||||
|
Development:
|
||||||
|
export DJANGO_ENV=development
|
||||||
|
python manage.py runserver
|
||||||
|
|
||||||
|
Production:
|
||||||
|
export DJANGO_ENV=production
|
||||||
|
gunicorn config.wsgi:application
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Determine which environment settings to load
|
||||||
|
DJANGO_ENV = os.environ.get("DJANGO_ENV", "development")
|
||||||
|
|
||||||
|
if DJANGO_ENV == "production":
|
||||||
|
from .production import *
|
||||||
|
elif DJANGO_ENV == "development":
|
||||||
|
from .development import *
|
||||||
|
else:
|
||||||
|
# Fallback to development if unknown environment
|
||||||
|
from .development import *
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Warning: Unknown DJANGO_ENV '{DJANGO_ENV}', falling back to development settings"
|
||||||
|
)
|
||||||
175
tienda_ilusion/config/settings/base.py
Normal file
175
tienda_ilusion/config/settings/base.py
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
"""
|
||||||
|
Django settings for tienda_ilusion project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.0.6.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
# BASE_DIR apunta a tienda_ilusion/ (3 niveles arriba desde settings/base.py)
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
# This will be overridden in production settings
|
||||||
|
SECRET_KEY = os.environ.get(
|
||||||
|
"SECRET_KEY",
|
||||||
|
"django-insecure-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v",
|
||||||
|
)
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
# This will be overridden in each environment
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
# This will be overridden in each environment
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
# This will be overridden in each environment
|
||||||
|
CORS_ALLOWED_ORIGINS = []
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
"don_confiao.apps.DonConfiaoConfig",
|
||||||
|
"django.contrib.admin",
|
||||||
|
"django.contrib.auth",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.messages",
|
||||||
|
"django.contrib.staticfiles",
|
||||||
|
"rest_framework",
|
||||||
|
"rest_framework.authtoken",
|
||||||
|
"corsheaders",
|
||||||
|
"users",
|
||||||
|
# 'don_confiao'
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
"django.middleware.common.CommonMiddleware",
|
||||||
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"corsheaders.middleware.CorsMiddleware",
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = "config.urls"
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
|
"DIRS": [os.path.join(BASE_DIR, "tienda_ilusion/templates")],
|
||||||
|
"APP_DIRS": True,
|
||||||
|
"OPTIONS": {
|
||||||
|
"context_processors": [
|
||||||
|
"django.template.context_processors.debug",
|
||||||
|
"django.template.context_processors.request",
|
||||||
|
"django.contrib.auth.context_processors.auth",
|
||||||
|
"django.contrib.messages.context_processors.messages",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = "config.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||||
|
# This will be overridden in each environment
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
"NAME": BASE_DIR / "db.sqlite3",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = "en-us"
|
||||||
|
|
||||||
|
TIME_ZONE = "UTC"
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = "static/"
|
||||||
|
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
FIXTURE_DIRS = ["don_confiao/tests/Fixtures"]
|
||||||
|
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||||
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
||||||
|
],
|
||||||
|
"DEFAULT_PERMISSION_CLASSES": [
|
||||||
|
"rest_framework.permissions.IsAuthenticated",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
SIMPLE_JWT = {
|
||||||
|
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
|
||||||
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
|
||||||
|
"AUTH_HEADER_TYPES": ("Bearer",),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Logging configuration (can be overridden in environment settings)
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
}
|
||||||
92
tienda_ilusion/config/settings/development.py
Normal file
92
tienda_ilusion/config/settings/development.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
Development settings for tienda_ilusion project.
|
||||||
|
|
||||||
|
This file contains settings specific to local development environment.
|
||||||
|
Uses SQLite database and permissive CORS settings for easier development.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from .base import *
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't use this in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
# SECRET_KEY for development (insecure, but convenient for development)
|
||||||
|
SECRET_KEY = os.environ.get(
|
||||||
|
"SECRET_KEY",
|
||||||
|
"django-insecure-development-key-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Allow all hosts in development for easier testing
|
||||||
|
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "localhost,127.0.0.1,0.0.0.0").split(
|
||||||
|
","
|
||||||
|
)
|
||||||
|
|
||||||
|
# CORS settings for development
|
||||||
|
CORS_ALLOWED_ORIGINS = os.environ.get(
|
||||||
|
"CORS_ALLOWED_ORIGINS",
|
||||||
|
"http://localhost:3000,http://localhost:7001,http://localhost:5173",
|
||||||
|
).split(",")
|
||||||
|
|
||||||
|
# Allow credentials in CORS for development
|
||||||
|
CORS_ALLOW_CREDENTIALS = True
|
||||||
|
|
||||||
|
# Database - SQLite for development
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
"NAME": BASE_DIR / "db.sqlite3",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Email backend for development (prints to console)
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
|
||||||
|
# Enhanced logging for development
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "[{levelname}] {asctime} {module} {message}",
|
||||||
|
"style": "{",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"django.db.backends": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Development-only apps (optional)
|
||||||
|
# Uncomment to add django-debug-toolbar or other dev tools
|
||||||
|
# INSTALLED_APPS += [
|
||||||
|
# 'debug_toolbar',
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# MIDDLEWARE += [
|
||||||
|
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# INTERNAL_IPS for debug toolbar
|
||||||
|
# INTERNAL_IPS = [
|
||||||
|
# '127.0.0.1',
|
||||||
|
# ]
|
||||||
180
tienda_ilusion/config/settings/production.py
Normal file
180
tienda_ilusion/config/settings/production.py
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
"""
|
||||||
|
Production settings for tienda_ilusion project.
|
||||||
|
|
||||||
|
This file contains settings specific to production environment.
|
||||||
|
Uses PostgreSQL database and strict security settings.
|
||||||
|
|
||||||
|
IMPORTANT: All sensitive values MUST be provided via environment variables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from .base import *
|
||||||
|
|
||||||
|
# SECURITY WARNING: DEBUG must be False in production!
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
# SECRET_KEY must be provided via environment variable in production
|
||||||
|
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||||
|
if not SECRET_KEY:
|
||||||
|
raise ValueError("SECRET_KEY environment variable must be set in production!")
|
||||||
|
|
||||||
|
# ALLOWED_HOSTS must be provided via environment variable
|
||||||
|
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
|
||||||
|
if not ALLOWED_HOSTS or ALLOWED_HOSTS == [""]:
|
||||||
|
raise ValueError("ALLOWED_HOSTS environment variable must be set in production!")
|
||||||
|
|
||||||
|
# CORS settings for production
|
||||||
|
CORS_ALLOWED_ORIGINS = os.environ.get("CORS_ALLOWED_ORIGINS", "").split(",")
|
||||||
|
if not CORS_ALLOWED_ORIGINS or CORS_ALLOWED_ORIGINS == [""]:
|
||||||
|
raise ValueError(
|
||||||
|
"CORS_ALLOWED_ORIGINS environment variable must be set in production!"
|
||||||
|
)
|
||||||
|
|
||||||
|
CORS_ALLOW_CREDENTIALS = True
|
||||||
|
|
||||||
|
# Database - PostgreSQL for production
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
"NAME": os.environ.get("DB_NAME", "tienda_ilusion"),
|
||||||
|
"USER": os.environ.get("DB_USER", "postgres"),
|
||||||
|
"PASSWORD": os.environ.get("DB_PASSWORD"),
|
||||||
|
"HOST": os.environ.get("DB_HOST", "localhost"),
|
||||||
|
"PORT": os.environ.get("DB_PORT", "5432"),
|
||||||
|
"CONN_MAX_AGE": 600, # Persistent connections
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if not DATABASES["default"]["PASSWORD"]:
|
||||||
|
raise ValueError("DB_PASSWORD environment variable must be set in production!")
|
||||||
|
|
||||||
|
# Security settings for HTTPS
|
||||||
|
SECURE_SSL_REDIRECT = True
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
CSRF_COOKIE_SECURE = True
|
||||||
|
SECURE_BROWSER_XSS_FILTER = True
|
||||||
|
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||||
|
X_FRAME_OPTIONS = "DENY"
|
||||||
|
|
||||||
|
# HTTP Strict Transport Security (HSTS)
|
||||||
|
SECURE_HSTS_SECONDS = 31536000 # 1 year
|
||||||
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||||
|
SECURE_HSTS_PRELOAD = True
|
||||||
|
|
||||||
|
# Proxy headers for deployment behind reverse proxy (nginx, etc.)
|
||||||
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
|
# Email configuration for production
|
||||||
|
# Adjust based on your email service
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||||
|
EMAIL_HOST = os.environ.get("EMAIL_HOST", "smtp.gmail.com")
|
||||||
|
EMAIL_PORT = int(os.environ.get("EMAIL_PORT", "587"))
|
||||||
|
EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "True") == "True"
|
||||||
|
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", "")
|
||||||
|
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", "")
|
||||||
|
DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL", "noreply@tiendailusion.com")
|
||||||
|
|
||||||
|
# Static files configuration for production
|
||||||
|
# Serve static files using WhiteNoise or CDN
|
||||||
|
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||||
|
STATIC_URL = "/static/"
|
||||||
|
|
||||||
|
# Media files configuration
|
||||||
|
MEDIA_ROOT = BASE_DIR / "media"
|
||||||
|
MEDIA_URL = "/media/"
|
||||||
|
|
||||||
|
# Optional: WhiteNoise configuration for serving static files
|
||||||
|
# Uncomment if using WhiteNoise
|
||||||
|
# MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
|
||||||
|
# STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
|
|
||||||
|
# Production logging - log to file and console
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "[{levelname}] {asctime} {name} {message}",
|
||||||
|
"style": "{",
|
||||||
|
},
|
||||||
|
"simple": {
|
||||||
|
"format": "[{levelname}] {message}",
|
||||||
|
"style": "{",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "simple",
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"filename": BASE_DIR / "logs" / "django.log",
|
||||||
|
"maxBytes": 1024 * 1024 * 15, # 15MB
|
||||||
|
"backupCount": 10,
|
||||||
|
"formatter": "verbose",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"django.security": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "WARNING",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create logs directory if it doesn't exist
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
logs_dir = BASE_DIR / "logs"
|
||||||
|
pathlib.Path(logs_dir).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Admin email notifications for errors (optional)
|
||||||
|
ADMINS = [
|
||||||
|
("Admin", os.environ.get("ADMIN_EMAIL", "admin@tiendailusion.com")),
|
||||||
|
]
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
# Cache configuration (optional - adjust based on your setup)
|
||||||
|
# Using local memory cache by default
|
||||||
|
CACHES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||||
|
"LOCATION": "unique-snowflake",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# For Redis cache (recommended for production):
|
||||||
|
# CACHES = {
|
||||||
|
# "default": {
|
||||||
|
# "BACKEND": "django_redis.cache.RedisCache",
|
||||||
|
# "LOCATION": os.environ.get("REDIS_URL", "redis://127.0.0.1:6379/1"),
|
||||||
|
# "OPTIONS": {
|
||||||
|
# "CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Session configuration
|
||||||
|
SESSION_ENGINE = "django.contrib.sessions.backends.db"
|
||||||
|
SESSION_COOKIE_AGE = 86400 # 24 hours
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
SESSION_COOKIE_SAMESITE = "Lax"
|
||||||
|
|
||||||
|
# CSRF configuration
|
||||||
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
|
CSRF_COOKIE_SAMESITE = "Lax"
|
||||||
|
CSRF_TRUSTED_ORIGINS = os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",")
|
||||||
|
|
||||||
|
# Performance optimizations
|
||||||
|
CONN_MAX_AGE = 600 # Database connection pooling
|
||||||
@@ -11,6 +11,6 @@ import os
|
|||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""Django's command-line utility for administrative tasks."""
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run administrative tasks."""
|
"""Run administrative tasks."""
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tienda_ilusion.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
@@ -18,5 +19,5 @@ def main():
|
|||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
"""
|
|
||||||
Django settings for tienda_ilusion project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 5.0.6.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = os.environ.get(
|
|
||||||
"SECRET_KEY",
|
|
||||||
"django-insecure-zh6rinl@8y7g(cf781snisx2j%p^c#d&b2@@9cqe!v@4yv8x=v"
|
|
||||||
)
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True if os.environ.get("DEBUG", 'False') in ['True', '1'] else False
|
|
||||||
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost').split(',')
|
|
||||||
|
|
||||||
CORS_ALLOWED_ORIGINS = os.environ.get(
|
|
||||||
'CORS_ALLOWED_ORIGINS',
|
|
||||||
'http://localhost:3000,http://localhost:7001').split(',')
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'don_confiao.apps.DonConfiaoConfig',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'rest_framework',
|
|
||||||
'rest_framework.authtoken',
|
|
||||||
'corsheaders',
|
|
||||||
'users',
|
|
||||||
# 'don_confiao'
|
|
||||||
]
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
'corsheaders.middleware.CorsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'tienda_ilusion.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [os.path.join(BASE_DIR, 'tienda_ilusion/templates')],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'tienda_ilusion.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
|
|
||||||
# Default primary key field type
|
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
||||||
|
|
||||||
FIXTURE_DIRS = ['don_confiao/tests/Fixtures']
|
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
|
||||||
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
||||||
],
|
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
|
||||||
'rest_framework.permissions.IsAuthenticated',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
SIMPLE_JWT = {
|
|
||||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
|
|
||||||
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
|
|
||||||
"AUTH_HEADER_TYPES": ("Bearer",),
|
|
||||||
}
|
|
||||||
|
|
||||||
# CORS_ALLOWED_ORIGINS = [
|
|
||||||
# "http://localhost:5173",
|
|
||||||
# ]
|
|
||||||
Reference in New Issue
Block a user