# Crochet Aplicación web para diseñar patrones de crochet con un editor visual, exportarlos a PDF y compartirlos con un enlace de solo lectura. Incluye cuentas de usuario para guardar y gestionar los propios patrones. ## Funcionalidades - **Editor visual de patrones**: secciones de título, subtítulo, texto, nota, materiales, imágenes y "patrón" (puntos con contador), todo reordenable por arrastre. El contenido se guarda bilingüe (es/en). - **Personalización de página**: título, autor, imagen de portada, color de texto/acento/fondo, tipografía, tamaño de letra, alineación, tamaño y orientación de página. - **Exportación a PDF** con WeasyPrint, usando el propio diálogo de impresión del navegador. - **Vista de solo lectura compartible** (`pattern//`), 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. - **"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 (`/es/patron/...` frente a `/en/pattern/...`) y contenido del propio patrón. ## Stack técnico - **Backend**: Django 6, servido en ASGI con Uvicorn. - **Base de datos**: PostgreSQL en producción, SQLite por defecto en desarrollo (configurable con `DATABASE_URL`). - **Frontend del editor**: JavaScript vanilla (sin framework ni bundler), daisyUI + Tailwind CSS vía CDN con compilador JIT. - **Vista de solo lectura / PDF**: Tailwind precompilado sin JS (WeasyPrint no ejecuta JavaScript), generado con el CLI standalone de Tailwind (ver `python manage.py build_pattern_detail_css`). - **Imágenes**: Pillow (variantes de la portada del patrón en varios tamaños), almacenamiento en filesystem o S3 (`django-storages`, opcional). - **Estáticos**: WhiteNoise con manifest comprimido. - **HTMX** para los formularios de ajustes de cuenta. - **Gestión de dependencias**: [uv](https://docs.astral.sh/uv/). ## Desarrollo ### Requisitos - Python 3.13 - [uv](https://docs.astral.sh/uv/) - Librerías nativas de WeasyPrint (Pango, Cairo, GDK-Pixbuf) si vas a generar/probar el PDF fuera de Docker — en Debian/Ubuntu: `apt-get install libpango-1.0-0 libpangocairo-1.0-0 fonts-dejavu-core`. - `gettext` si vas a regenerar/compilar traducciones (`msgfmt`/`msguniq`/etc.) — en Debian/Ubuntu: `apt-get install gettext`. ### Puesta en marcha ```bash git clone cd crochet uv sync --group dev ``` Crea un archivo `.env` en la raíz del proyecto (ver [Variables de entorno](#variables-de-entorno) más abajo). Para desarrollo local basta con: ```env DEBUG=True ``` Con eso, la app arranca con SQLite, `SECRET_KEY` de desarrollo, envío de email al backend de consola (se imprime en la terminal en vez de mandarse de verdad) y sin credenciales adicionales. ```bash uv run python manage.py migrate uv run python manage.py createsuperuser # opcional, para /admin/ uv run python manage.py runserver ``` La app queda disponible en `http://localhost:8000/`. ### Tests ```bash uv run pytest ``` `pytest-cov` está incluido; para ver cobertura: `uv run pytest --cov`. ### Traducciones Las cadenas ya traducidas están compiladas y listas para usar. Si añades o cambias texto traducible (`{% trans %}`/`{% blocktrans %}`, `gettext_lazy`): ```bash uv run python manage.py makemessages -l es -l en # revisa a mano cualquier entrada marcada como "#, fuzzy" antes de compilar uv run python manage.py compilemessages ``` ### CSS precompilado de la vista de solo lectura / PDF Solo hace falta volver a generarlo si cambian las clases de Tailwind usadas en `pattern_detail.html` o `pattern_render.py`: ```bash uv run python manage.py build_pattern_detail_css ``` ### Docker ```bash docker build -t crochet . docker run --rm -p 8000:8000 --env-file .env crochet ``` El `Dockerfile` instala las dependencias del sistema necesarias para WeasyPrint y ejecuta `scripts/build.sh` (CSS precompilado, `collectstatic`, `compilemessages`) al construir la imagen, y `scripts/run.sh` (Uvicorn) al arrancar el contenedor. ## Variables de entorno Todas se leen en `config/settings/env.py`; sin `.env`, o si falta alguna, se usan los valores por defecto indicados (pensados para desarrollo local). | Variable | Por defecto | Descripción | |---|---|---| | `SECRET_KEY` | clave insegura de desarrollo | **Cámbiala en producción.** | | `DEBUG` | `False` | Activa páginas de error detalladas, Django Debug Toolbar y Silk. No debe estar activo en producción. | | `ALLOWED_HOSTS` | `*` | Lista separada por comas. Debe restringirse a los dominios reales en producción. | | `CSRF_TRUSTED_ORIGINS` | `http://localhost:8000` | Lista separada por comas. Necesario si la app se sirve detrás de un dominio/HTTPS distinto. | | `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). | | `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`). | | `LOG_LEVEL` | `INFO` | Nivel de log de Django y de la app (`crochet`). | Existen además `CORS_ORIGIN_WHITELIST`, `REDIS_HOST`, `REDIS_PORT`, `CELERY_BROKER_URL`, `PAGE_SIZE` e `ITEMS_PER_PAGE` en `env.py`: quedaron de una plantilla de proyecto y actualmente no los usa ninguna parte de la aplicación (no hay Celery, cachés en Redis, CORS ni paginación configurados), así que no hace falta definirlos para desplegar. ## Despliegue a producción Como mínimo hay que ajustar, respecto al `.env` de desarrollo: 1. **`SECRET_KEY`**: un valor único y secreto (no el de desarrollo). 2. **`DEBUG=False`**. 3. **`ALLOWED_HOSTS`**: los dominios reales, no `*`. 4. **`CSRF_TRUSTED_ORIGINS`**: los orígenes reales (con esquema, p. ej. `https://patrones.ejemplo.com`). 5. **`DATABASE_URL`**: apuntando a PostgreSQL. 6. **`EMAIL_BACKEND`/`EMAIL_HOST`/`EMAIL_HOST_USER`/`EMAIL_HOST_PASSWORD`/`DEFAULT_FROM_EMAIL`**: sin esto, el registro de usuarios funciona pero la recuperación de contraseña no llega a enviarse (se queda en el log en vez de salir por SMTP). 7. **Almacenamiento de archivos**: si vas a correr más de una instancia o quieres que las imágenes sobrevivan a un redeploy del contenedor, configura `S3_ENABLED=True` y las credenciales de `S3_*` — el filesystem local (por defecto) no es compartido ni persistente entre despliegues. La imagen Docker ya ejecuta `collectstatic`, `compilemessages` y el CSS precompilado al construirse, y sirve la app con Uvicorn (variables `WSGI_HOST`/`WSGI_WORKERS` para ajustar host/nº de workers). WhiteNoise se encarga de servir los estáticos directamente desde la propia app, sin necesidad de un servidor/proxy de estáticos aparte. Al hacer push a `master` se dispara la integración continua (tests) y, si pasan, se construye y publica una imagen Docker (ver `.gitea/workflows/test-build.yaml`).