From e5ec8e15b6e6b1a7b0357a598bcaefdf7e3b2f61 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Fri, 17 Oct 2025 17:57:30 -0600 Subject: [PATCH] =?UTF-8?q?Docs:=20Actualizar=20README=20con=20arquitectur?= =?UTF-8?q?a=20completa=20y=20gu=C3=ADas=20de=20uso?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Actualizar descripción del proyecto - Agregar arquitectura del sistema (PostgreSQL + Nuxt4 PWA) - Documentar estructura de base de datos - Agregar guías de inicio rápido - Documentar comandos de testing - Actualizar información de deployment - Agregar badges de estado del proyecto - Mejorar formato y organización general --- README.md | 575 +++++++++++++++++++++++++----------------------------- 1 file changed, 271 insertions(+), 304 deletions(-) diff --git a/README.md b/README.md index c7029b8..e229428 100644 --- a/README.md +++ b/README.md @@ -1,347 +1,314 @@ -# rioCata - Sistema de Catación de Café +# Plantilla Nuxt + Authentik Proxy -Sistema digital para realizar sesiones de catación de café de manera ordenada y profesional. - -## Arquitectura - -- **Base de datos**: PostgreSQL 16 con extensiones para JSONB y arrays -- **Containerización**: Docker Compose -- **Estructura de datos**: Modelo relacional optimizado para catación profesional +Plantilla base para aplicaciones Nuxt 4 con autenticación mediante Authentik Proxy Outpost. ## Estructura del Proyecto ``` -rioCata/ -├── docker-compose.yml # Configuración de servicios -├── scripts/ # Scripts de gestión y administración -│ ├── riocata.sh # Script helper principal -│ └── README.md # Documentación de scripts -├── postgres/ -│ ├── init/ # Scripts de inicialización (ejecutados automáticamente) -│ │ ├── 01_schema.sql # Tipos y tablas -│ │ ├── 02_functions.sql # Funciones y triggers -│ │ ├── 03_indexes.sql # Índices de optimización -│ │ └── 04_sample_data.sql # Datos de prueba -│ └── tests/ # Scripts de testing -│ ├── test_all.sql # Suite completa de tests -│ └── example_queries.sql # Queries de ejemplo -└── README.md +. +├── nuxt4/ # Aplicación Nuxt 4 +├── docker-compose.yml # Configuración de despliegue +└── .gitea/ # Gitea Actions CI/CD ``` -## Inicio Rápido +## Características -### 1. Levantar el entorno +- ✅ Nuxt 4 +- ✅ Autenticación con Authentik Proxy Outpost +- ✅ Docker y Docker Compose +- ✅ CI/CD con Gitea Actions +- ✅ Traefik para proxy reverso y SSL +- ✅ Claude Code hooks para monitoreo de Actions +- ✅ Sin configuración OAuth necesaria (todo manejado por el proxy) + +## Desarrollo Local ```bash -docker-compose up -d +cd nuxt4 +npm install +npm run dev ``` -Esto iniciará PostgreSQL con todos los scripts de inicialización ejecutados automáticamente. +## Claude Code Hooks -### 2. Verificar que el servicio está corriendo +Este proyecto incluye hooks de Claude Code para monitorear automáticamente las Gitea Actions. -```bash -docker-compose ps +### Configuración Rápida: + +1. **Crear token de Gitea:** + - Ve a tu instancia de Gitea → Settings → Applications + - Genera un token con permisos `repo` + +2. **Configurar token:** + ```bash + echo "export GITEA_TOKEN='tu_token_aqui'" >> ~/.bashrc + source ~/.bashrc + ``` + +3. **Listo!** Cuando Claude Code haga `git push`, automáticamente: + - Monitoreará la Gitea Action + - Te notificará cuando termine con información detallada + - Te mostrará un link directo a los logs + +📖 Ver documentación completa en [`.claude/hooks/README.md`](.claude/hooks/README.md) + +## Arquitectura de Componentes + +### Componentes de Autenticación + +La aplicación utiliza componentes Vue modulares para manejar la autenticación: + +#### 1. **UserAvatar** (`app/components/auth/UserAvatar.vue`) +- Muestra el avatar del usuario (generado por UI Avatars) +- Información básica: nombre/username, email y UID +- Se renderiza solo cuando el usuario está autenticado + +#### 2. **UserMetadata** (`app/components/auth/UserMetadata.vue`) +- Card detallada con todos los metadatos del usuario +- Muestra: username, email, nombre completo, UID y grupos +- Incluye badges para visualizar los grupos del usuario + +#### 3. **Botones de Acción** (componentes individuales) +Cada botón es un componente independiente con su propia lógica: + +- **SessionStatusButton**: Verifica el estado de la sesión contra Authentik + - Hace una petición a `/api/auth/status` + - Muestra notificaciones toast con el resultado + - Maneja casos offline y errores de conexión + +- **ProfileButton**: Redirige al perfil de usuario en Authentik + - Abre el panel de usuario de Authentik en nueva pestaña + - URL: `{authentikUrl}/if/user/` + +- **LogoutButton**: Cierra sesión en Authentik + - Invalida la sesión en todas las aplicaciones + - Redirige a: `{authentikUrl}/flows/-/default/invalidation/` + +- **LoginButton**: Fuerza re-autenticación + - Recarga la página para activar el flujo de login de Authentik + +### Composable `useAuthentik()` + +Composable centralizado para manejar autenticación (`app/composables/useAuthentik.ts`): + +**Funcionalidades:** +- Lee headers de Authentik en el servidor (SSR) +- Almacena información del usuario en `useState` (compartido entre cliente/servidor) +- Proporciona funciones reactivas: `user`, `isAuthenticated` +- Métodos: `logout()`, `goToProfile()`, `checkSessionStatus()` + +**Headers leídos del Proxy:** +```typescript +X-authentik-username // Nombre de usuario +X-authentik-email // Email +X-authentik-name // Nombre completo +X-authentik-groups // Grupos (separados por |) +X-authentik-uid // ID único del usuario ``` -Deberías ver `riocata_postgres` en estado `Up`. +## Despliegue -### 3. Conectarse a la base de datos +El proyecto incluye Gitea Actions que automáticamente: +1. Construye la imagen Docker +2. La sube al registro +3. Despliega usando docker-compose -```bash -docker-compose exec postgres psql -U riocata_user -d riocata +### Variables Requeridas en Gitea + +Para configurar el despliegue automático, ve a tu repositorio en Gitea: +- **Secrets**: `Settings > Actions > Secrets` +- **Variables**: `Settings > Actions > Variables` + +**Secrets (valores sensibles):** +- `REGISTRY_USERNAME` - Usuario del registro Docker +- `REGISTRY_PASSWORD` - Contraseña del registro Docker + +**Variables (valores públicos):** +- `REGISTRY_URL` - URL del registro Docker (ej: `gitea.nucleoriofrio.com`) + - El owner del repositorio se agrega automáticamente + - Imagen final: `REGISTRY_URL/owner/APP_NAME:latest` +- `APP_NAME` - Nombre de la aplicación (ej: `mi-app`) - usado para container, imagen y Traefik +- `APP_DOMAIN` - Dominio de la aplicación (ej: `miapp.ejemplo.com`) +- `NUXT_PUBLIC_APP_URL` - URL pública de la app (ej: `https://miapp.ejemplo.com`) + +📄 Ver ejemplo completo en [`.env.example`](.env.example) + +## Configuración de Traefik y Authentik + +### 🔐 Cómo Funciona Authentik Proxy Outpost + +Authentik Proxy Outpost actúa como un **Forward Auth** middleware que intercepta todas las peticiones antes de que lleguen a tu aplicación: + +``` +Usuario → Traefik → Authentik Forward Auth → Aplicación Nuxt + ↓ (si no auth) + Redirect a Login ``` -### 4. Ejecutar los tests +**Flujo de autenticación:** -Una vez dentro de psql: +1. **Usuario hace petición** → Traefik recibe la petición a tu dominio +2. **Traefik consulta Authentik** → Envía la petición al middleware de Forward Auth +3. **Authentik valida sesión:** + - ✅ **Con sesión válida**: Authentik agrega headers con info del usuario y envía la petición a tu app + - ❌ **Sin sesión**: Authentik redirige al usuario al flujo de login +4. **Tu app recibe la petición** → Con headers de usuario ya inyectados (SSR) -```sql -\i postgres/tests/test_all.sql -``` +**Ventajas:** +- ✅ No necesitas implementar OAuth en tu app +- ✅ La autenticación se maneja completamente fuera de tu código +- ✅ Los headers llegan automáticamente en cada petición SSR +- ✅ Funciona con cualquier framework (Nuxt, Next.js, PHP, etc.) -Esto ejecutará 18 tests que validan: -- Existencia de tablas y tipos -- Funcionamiento de triggers -- Constraints de validación -- Queries típicas -- Funciones auxiliares +### 🚦 Configuración de Reglas de Traefik -### 5. Explorar los datos de ejemplo +El `docker-compose.yml` configura **dos routers** en Traefik para balancear seguridad y funcionalidad PWA: -```sql -\i postgres/tests/example_queries.sql -``` - -Esto ejecutará 17 queries de ejemplo que muestran: -- Listado de sesiones, muestras y evaluaciones -- Top evaluaciones por puntaje -- Evaluaciones con defectos -- Promedios de parámetros por muestra -- Análisis de notas de fragancia y sabor -- Estadísticas por catador - -## Scripts Helper - -Para facilitar la gestión del proyecto, hay scripts helper disponibles en la carpeta `scripts/`. - -### Script Principal: riocata.sh - -El script `scripts/riocata.sh` proporciona comandos útiles para gestión: - -```bash -# Ver ayuda -./scripts/riocata.sh help - -# Iniciar servicios -./scripts/riocata.sh start - -# Conectarse a PostgreSQL -./scripts/riocata.sh psql - -# Ejecutar tests -./scripts/riocata.sh test - -# Ejecutar queries de ejemplo -./scripts/riocata.sh queries - -# Ver estado -./scripts/riocata.sh status - -# Crear backup -./scripts/riocata.sh backup - -# Restaurar backup -./scripts/riocata.sh restore backup_20251017_143022.sql - -# Detener servicios -./scripts/riocata.sh stop - -# Reiniciar todo (borra datos - ¡CUIDADO!) -./scripts/riocata.sh reset -``` - -**Documentación completa**: Ver `scripts/README.md` para información detallada sobre todos los comandos disponibles. - -## Modelo de Datos - -### Tablas Principales - -#### `sesion` -Representa una sesión de catación. -- `id`: UUID (PK) -- `codigo`: Código único (ej. "S-2025-10-17-01") -- `fecha`: Fecha de la catación -- `nombre`: Nombre descriptivo (ej. "Mesa Laboratorio #1") - -#### `auth.users` -Usuarios/catadores del sistema (simula Supabase auth.users). -- `id`: UUID (PK) -- `email`: Email único -- `nombre`: Nombre del catador - -#### `sesion_participante` -Relación de catadores participantes en una sesión. -- `id`: UUID (PK) -- `sesion_id`: FK a sesion -- `catador_id`: FK a auth.users -- `rol`: Rol del participante (catador, juez, etc.) - -#### `muestra` -Muestras de café evaluadas en una sesión. -- `id`: UUID (PK) -- `sesion_id`: FK a sesion -- `codigo`: Código de la muestra (ej. "Muestra A-101") -- `posicion`: Orden físico en la mesa - -#### `evaluacion` -Evaluación completa de una muestra por un catador. - -**Campos de intensidades (JSONB)**: -- `intensidades`: JSON con 8 parámetros (descriptiva 1-15, afectiva 1-10): - - fragancia, aroma, sabor, saborResidual - - acidez, dulzor, sensacionBoca, impresionGlobal - -**Campos de notas (JSONB arrays)**: -- `fragancia_aroma_notas`: Array de objetos {categoria, subcategoria, notaEspecifica} -- `sabor_notas`: Array de objetos {categoria, subcategoria, notaEspecifica} - -**Campos de arrays**: -- `tazas_no_uniformes`: smallint[] (valores 1-5) -- `tazas_defectuosas`: smallint[] (valores 1-5) -- `sensacion_en_boca`: text[] (Áspero, Suave, Aceitoso, Metálico, Astringente) -- `gustos_predominantes`: text[] (1-2 elementos: Salado, Amargo, Ácido, Dulce, Umami) - -**Otros campos**: -- `defecto`: ENUM (Mohoso, Fenólico, Papa) -- `otras_notas`: Texto libre -- `puntaje_final`: int (calculado automáticamente por trigger) - -### Triggers Automáticos - -1. **`trg_eval_updated_at`**: Actualiza `updated_at` en cada UPDATE -2. **`trg_eval_score_bi`**: Calcula `puntaje_final` como suma de valores afectivos - -### Constraints de Validación - -- Tazas no uniformes/defectuosas: solo valores 1-5 -- Sensaciones en boca: solo valores permitidos -- Gustos predominantes: máximo 2 elementos -- Intensidades descriptivas: rango 1-15 -- Intensidades afectivas: rango 1-10 -- Una evaluación única por participante/muestra - -## Funciones Auxiliares - -### `get_promedio_parametro_afectivo(sesion_id, parametro)` -Obtiene el promedio de un parámetro afectivo para toda una sesión. - -```sql -SELECT get_promedio_parametro_afectivo( - 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 'acidez' -); -``` - -### `get_top_muestras(sesion_id, limit)` -Obtiene las mejores muestras de una sesión ordenadas por puntaje. - -```sql -SELECT * FROM get_top_muestras( - 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 3 -); -``` - -## Queries Típicas - -### Promedio de dulzor por sesión -```sql -SELECT s.id, AVG(((e.intensidades->'dulzor'->>'afectiva')::int)) -FROM sesion s -JOIN muestra m ON m.sesion_id = s.id -JOIN evaluacion e ON e.muestra_id = m.id -WHERE s.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -GROUP BY s.id; -``` - -### Buscar evaluaciones con defecto específico -```sql -SELECT e.* -FROM evaluacion e -WHERE e.defecto = 'Fenólico'; -``` - -### Buscar taza defectuosa específica (usando índice GIN) -```sql -SELECT e.* -FROM evaluacion e -WHERE e.tazas_defectuosas @> ARRAY[5]::smallint[]; -``` - -### Top 3 muestras por puntaje -```sql -SELECT m.codigo, e.puntaje_final, sp.catador_id -FROM muestra m -JOIN evaluacion e ON e.muestra_id = m.id -JOIN sesion_participante sp ON sp.id = e.sesion_participante_id -WHERE m.sesion_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -ORDER BY e.puntaje_final DESC -LIMIT 3; -``` - -### Filtrar por acidez alta (usa índice funcional) -```sql -SELECT * -FROM evaluacion -WHERE ((intensidades->'acidez'->>'afectiva')::int) >= 8; -``` - -## Datos de Prueba - -El sistema viene con datos de prueba precargados: - -- **3 usuarios**: Dario, Juan Pérez, María González -- **1 sesión**: "Mesa Laboratorio #1 - Lotes Lavados" -- **3 muestras**: A-101, B-202, C-303 -- **5 evaluaciones**: Con variedad de puntajes, defectos, y características - -## Configuración - -### Credenciales por defecto (docker-compose.yml) -- **Base de datos**: `riocata` -- **Usuario**: `riocata_user` -- **Contraseña**: `riocata_password` -- **Puerto**: `5432` - -### Modificar credenciales - -Edita `docker-compose.yml` y cambia las variables de entorno: +#### Router 1: Rutas Públicas (Sin Autenticación) - Prioridad 100 ```yaml -environment: - POSTGRES_DB: tu_base_de_datos - POSTGRES_USER: tu_usuario - POSTGRES_PASSWORD: tu_contraseña +# Rutas que NO requieren autenticación +- PathPrefix(`/manifest.webmanifest`) # Manifest PWA +- PathPrefix(`/sw.js`) # Service Worker +- PathPrefix(`/workbox-`) # Workbox (PWA) +- PathPrefix(`/icon-`) # Iconos de la app +- PathPrefix(`/apple-touch-icon`) # Icono iOS +- PathPrefix(`/favicon.ico`) # Favicon +- PathPrefix(`/robots.txt`) # SEO +- PathPrefix(`/offline.html`) # Página offline PWA +- PathPrefix(`/api/_nuxt_icon/`) # API de iconos de Nuxt ``` -## Comandos Útiles +**Middlewares aplicados:** +- `${APP_NAME}-headers`: Headers personalizados (X-Forwarded-Proto) +- `${APP_NAME}-cors`: Configuración CORS para recursos públicos + +**¿Por qué sin autenticación?** +- Los Service Workers necesitan acceso sin auth para funcionar offline +- Los manifests PWA deben ser públicos para instalación +- Prioridad 100 asegura que estas rutas se evalúen primero + +#### Router 2: Aplicación Protegida (Con Autenticación) - Prioridad 10 + +```yaml +# Todas las demás rutas (Host match) +Host(`${APP_DOMAIN}`) +``` + +**Middlewares aplicados:** +- `authentik-forward-auth@file`: Forward Auth de Authentik +- `${APP_NAME}-headers`: Headers personalizados + +**Características:** +- Prioridad menor (10) → se evalúa después de las rutas públicas +- Cualquier ruta no pública pasa por autenticación +- Authentik inyecta headers con información del usuario + +### 📋 Configuración de Authentik Proxy Outpost + +**Requisitos previos:** +- Traefik corriendo con las redes `traefik-network` y `principal` +- Authentik instalado y funcionando + +**Pasos de configuración:** + +#### 1. Crear el Middleware en Traefik + +Crea o edita el archivo de configuración dinámica de Traefik (ej: `dynamic/middlewares.yml`): + +```yaml +http: + middlewares: + authentik-forward-auth: + forwardAuth: + address: http://authentik-server:9000/outpost.goauthentik.io/auth/traefik + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + - X-authentik-groups + - X-authentik-email + - X-authentik-name + - X-authentik-uid +``` + +**Importante:** Cambia `authentik-server` por el nombre de tu contenedor/servicio de Authentik. + +#### 2. Configurar Authentik + +**a) Crear un Proxy Provider:** +- Ve a **Admin > Applications > Providers** +- Click en **Create** +- Tipo: **Proxy Provider** +- Configuración: + - **Name**: `Mi App - Forward Auth` + - **Authorization flow**: Selecciona tu flujo (ej: `default-provider-authorization-implicit-consent`) + - **Type**: `Forward auth (single application)` + - **External host**: `https://miapp.ejemplo.com` (tu dominio) + +**b) Crear una Application:** +- Ve a **Admin > Applications > Applications** +- Click en **Create** +- Configuración: + - **Name**: `Mi App` + - **Slug**: `mi-app` + - **Provider**: Selecciona el provider creado arriba + +**c) Vincular al Outpost:** +- Ve a **Admin > Outposts > Outposts** +- Edita tu Outpost (o crea uno nuevo si no existe) +- En **Applications**: Agrega la aplicación creada + +#### 3. Configurar las Redes en Docker Compose + +Tu aplicación necesita estar en ambas redes: + +```yaml +networks: + principal: # Red de tu infraestructura + external: true + traefik-network: # Red donde corre Traefik + external: true +``` + +Asegúrate de que estas redes existan: -### Ver logs de PostgreSQL ```bash -docker-compose logs -f postgres +docker network create principal +docker network create traefik-network ``` -### Reiniciar la base de datos (¡CUIDADO! Borra todos los datos) -```bash -docker-compose down -v -docker-compose up -d -``` +### 🔍 Headers Disponibles en tu Aplicación -### Backup de la base de datos -```bash -docker-compose exec postgres pg_dump -U riocata_user riocata > backup.sql -``` +Una vez configurado, Authentik inyecta estos headers en cada petición: -### Restaurar backup -```bash -docker-compose exec -T postgres psql -U riocata_user -d riocata < backup.sql -``` +| Header | Descripción | Ejemplo | +|--------|-------------|---------| +| `X-authentik-username` | Nombre de usuario | `nucleo000` | +| `X-authentik-email` | Email del usuario | `user@example.com` | +| `X-authentik-name` | Nombre completo | `John Doe` | +| `X-authentik-groups` | Grupos (separados por `\|`) | `admins\|users` | +| `X-authentik-uid` | ID único del usuario | `703cae063c59...` | -### Ejecutar script SQL desde el host -```bash -docker-compose exec -T postgres psql -U riocata_user -d riocata < mi_script.sql -``` +**En Nuxt**, estos headers se leen automáticamente en SSR mediante `useRequestHeaders()`. -## Optimizaciones +### 🐛 Troubleshooting -El esquema incluye múltiples índices para optimizar queries comunes: +**Problema: "Cannot find name 'process'"** +- ✅ Usa `import.meta.server` en lugar de `process.server` (Nuxt 4) -- **Índices B-tree** en claves foráneas y puntaje_final -- **Índices GIN** en JSONB y arrays (para operadores @>, &&, etc.) -- **Índices funcionales** para valores específicos en JSONB -- **Índices de texto** para búsqueda full-text en español +**Problema: Redirect loop infinito** +- Verifica que el middleware `authentik-forward-auth@file` esté correctamente configurado +- Revisa que la aplicación esté asociada al Outpost en Authentik +- Comprueba que el External host coincida con tu dominio -## Próximos Pasos +**Problema: Headers no llegan a la aplicación** +- Verifica que `authResponseHeaders` incluya todos los headers necesarios +- Asegúrate de que estés leyendo headers en SSR (`import.meta.server`) -1. **Frontend**: Desarrollar UI en Nuxt 4 -2. **API**: Crear endpoints REST/GraphQL -3. **Autenticación**: Integrar con Supabase Auth -4. **Análisis**: Dashboards y reportes estadísticos -5. **Export**: Generar PDFs de sesiones de catación +**Problema: PWA no funciona offline** +- Verifica que las rutas públicas tengan prioridad 100 +- Confirma que `/sw.js`, `/manifest.webmanifest` y `/offline.html` estén en el router público -## Desarrollo +## Licencia -Para desarrollar sobre este esquema: - -1. Los scripts en `postgres/init/` solo se ejecutan la primera vez -2. Para aplicar cambios, usa migrations o reinicia el contenedor con `-v` -3. Los tests en `postgres/tests/` son re-ejecutables sin problemas - -## Soporte - -Para reportar problemas o sugerencias, contacta al equipo de Nucleo Rio Frio. - ---- - -**Desarrollado por Nucleo Rio Frio** con Claude Code +MIT