feat: included 2FA and lots of feature from django-htmx-template
CI / test (push) Successful in 2m13s
CI / build (push) Successful in 55s

This commit is contained in:
2026-09-16 13:20:44 +02:00
parent db21129960
commit 4b0d404116
52 changed files with 3135 additions and 311 deletions
+52 -6
View File
@@ -16,11 +16,12 @@ cuentas de usuario para guardar y gestionar los propios patrones.
impresión del navegador.
- **Vista de solo lectura compartible** (`pattern/<uuid>/`), sin necesidad
de cuenta ni de cargar el editor.
- **Cuentas de usuario**: registro (con email, necesario para poder
recuperar la contraseña), login/logout, ajustes de cuenta (cambiar
email/contraseña, con HTMX) y recuperación de contraseña por email.
Cada patrón pertenece a quien lo creó; solo su propietario puede
editarlo o borrarlo.
- **Cuentas de usuario**: registro con verificación de email, login/logout
(con verificación en dos pasos opcional por TOTP + códigos de respaldo),
ajustes de cuenta (cambiar email/contraseña, con HTMX) y recuperación de
contraseña por email. Login, registro y recuperación de contraseña están
protegidos contra fuerza bruta (`django-ratelimit`). Cada patrón
pertenece a quien lo creó; solo su propietario puede editarlo o borrarlo.
- **"Mis patrones"**: listado de los patrones propios con portada, fecha de
actualización y accesos directos a editar/ver/eliminar.
- **i18n completo** (español/inglés): interfaz, URLs traducidas
@@ -123,6 +124,42 @@ WeasyPrint y ejecuta `scripts/build.sh` (CSS precompilado, `collectstatic`,
`compilemessages`) al construir la imagen, y `scripts/run.sh` (Uvicorn) al
arrancar el contenedor.
### Entorno local con Postgres + S3 (Garage)
`docker-compose.yml` levanta un Postgres y un
[Garage](https://garagehq.deuxfleurs.fr/) (S3 self-hosted) para poder
probar `S3_ENABLED` sin depender de un bucket real:
```bash
docker compose up -d
```
Con eso, en tu `.env`:
```env
DATABASE_URL=postgres://django:django@localhost:6543/django
S3_ENABLED=True
S3_ACCESS_KEY_ID=GKa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
S3_SECRET_ACCESS_KEY=f0e1d2c3b4a5968778695a4b3c2d1e0ff0e1d2c3b4a5968778695a4b3c2d1e0f
S3_STORAGE_BUCKET_NAME=default-bucket
S3_ENDPOINT_URL=http://localhost:3900
S3_REGION_NAME=garage
S3_USE_SSL=False
S3_ADDRESSING_STYLE=path
```
La web de administración de Garage queda en `http://localhost:3909/`
(usuario `admin`, contraseña `garage-dev`; cámbiala en `docker-compose.yml`
si vas a exponerla más allá de tu propia máquina).
### Herramientas de desarrollo
`make help` lista los atajos disponibles (`make test`, `make lint`,
`make format`, `make messages`...). El estilo del código se comprueba con
[ruff](https://docs.astral.sh/ruff/) (`make lint`); `make precommit-install`
instala un hook de pre-commit que lo ejecuta automáticamente en cada
commit.
## Variables de entorno
Todas se leen en `config/settings/env.py`; sin `.env`, o si falta alguna,
@@ -137,10 +174,15 @@ se usan los valores por defecto indicados (pensados para desarrollo local).
| `DATABASE_URL` | `sqlite:///db.sqlite3` | Formato `django-environ` (p. ej. `postgres://usuario:password@host:5432/nombre_bd`). |
| `S3_ENABLED` | `False` | Si es `True`, los archivos subidos (imágenes de portada/patrón) se guardan en S3 en vez de en el filesystem local. |
| `S3_ACCESS_KEY_ID` / `S3_SECRET_ACCESS_KEY` / `S3_STORAGE_BUCKET_NAME` / `S3_ENDPOINT_URL` | vacío | Credenciales del bucket, solo necesarias si `S3_ENABLED=True`. |
| `EMAIL_BACKEND` | backend de consola | Usa `django.core.mail.backends.smtp.EmailBackend` para enviar emails de verdad (recuperación de contraseña). |
| `S3_REGION_NAME` / `S3_USE_SSL` / `S3_ADDRESSING_STYLE` | vacío / `True` / `auto` | Para S3 compatibles que lo exigen (p. ej. Garage, ver [entorno local con Garage](#entorno-local-con-postgres--s3-garage)): región obligatoria y sin soporte de direccionamiento "virtual" por defecto. |
| `EMAIL_BACKEND` | backend de consola | Usa `django.core.mail.backends.smtp.EmailBackend` para enviar emails de verdad (verificación de cuenta, recuperación de contraseña...). |
| `EMAIL_HOST` / `EMAIL_HOST_USER` / `EMAIL_HOST_PASSWORD` / `EMAIL_PORT` | vacío / vacío / vacío / `587` | Credenciales SMTP, solo con el backend SMTP. |
| `DEFAULT_FROM_EMAIL` | `webmaster@localhost` | Remitente de los emails salientes. La mayoría de proveedores SMTP rechazan enviar si no coincide con la cuenta autenticada (o un alias verificado). |
| `SUPPORT_EMAIL` | `soporte@localhost` | Dirección de contacto que se muestra en el aviso que recibe el email anterior de una cuenta cuando alguien lo cambia (ver `EmailUpdateForm`). |
| `SITE_NAME` | `Crochet` | Nombre mostrado en los emails de aviso de la cuenta (registro, verificación en dos pasos...). |
| `RATELIMIT_ENABLE` | `True` | Desactiva la limitación de peticiones en login/registro/recuperación de contraseña/verificación en dos pasos (pensado para tests o depuración, no para producción). |
| `SECURE_SSL_REDIRECT` / `SESSION_COOKIE_SECURE` / `CSRF_COOKIE_SECURE` | `False` | Actívalas en producción una vez la app esté detrás de HTTPS de verdad; en desarrollo local (HTTP plano) dejarlas activas rompe la sesión/CSRF. |
| `SECURE_HSTS_SECONDS` / `SECURE_HSTS_INCLUDE_SUBDOMAINS` / `SECURE_HSTS_PRELOAD` | `0` / `False` / `False` | HSTS; empieza con un valor bajo (p. ej. `3600`) antes de subirlo a uno largo (p. ej. `31536000` = 1 año) — un valor largo mal configurado deja el dominio inaccesible por HTTPS durante ese tiempo. |
| `LOG_LEVEL` | `INFO` | Nivel de log de Django y de la app (`crochet`). |
Existen además `CORS_ORIGIN_WHITELIST`, `REDIS_HOST`, `REDIS_PORT`,
@@ -168,6 +210,10 @@ Como mínimo hay que ajustar, respecto al `.env` de desarrollo:
configura `S3_ENABLED=True` y las credenciales de `S3_*` — el
filesystem local (por defecto) no es compartido ni persistente entre
despliegues.
8. **HTTPS**: una vez la app esté detrás de HTTPS de verdad, activa
`SECURE_SSL_REDIRECT=True`, `SESSION_COOKIE_SECURE=True` y
`CSRF_COOKIE_SECURE=True` (y, si quieres, `SECURE_HSTS_SECONDS` — ver la
tabla de arriba antes de subirlo a un valor largo).
La imagen Docker ya ejecuta `collectstatic`, `compilemessages` y el
CSS precompilado al construirse, y sirve la app con Uvicorn (variables