Docs: Actualizar README con arquitectura completa y guías de uso
- 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
This commit is contained in:
565
README.md
565
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.
|
Plantilla base para aplicaciones Nuxt 4 con autenticación mediante Authentik Proxy Outpost.
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
## Estructura del Proyecto
|
## Estructura del Proyecto
|
||||||
|
|
||||||
```
|
```
|
||||||
rioCata/
|
.
|
||||||
├── docker-compose.yml # Configuración de servicios
|
├── nuxt4/ # Aplicación Nuxt 4
|
||||||
├── scripts/ # Scripts de gestión y administración
|
├── docker-compose.yml # Configuración de despliegue
|
||||||
│ ├── riocata.sh # Script helper principal
|
└── .gitea/ # Gitea Actions CI/CD
|
||||||
│ └── 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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
|
```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.
|
||||||
|
|
||||||
|
### 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
|
```bash
|
||||||
docker-compose ps
|
echo "export GITEA_TOKEN='tu_token_aqui'" >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
```
|
```
|
||||||
|
|
||||||
Deberías ver `riocata_postgres` en estado `Up`.
|
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
|
||||||
|
|
||||||
### 3. Conectarse a la base de datos
|
📖 Ver documentación completa en [`.claude/hooks/README.md`](.claude/hooks/README.md)
|
||||||
|
|
||||||
```bash
|
## Arquitectura de Componentes
|
||||||
docker-compose exec postgres psql -U riocata_user -d riocata
|
|
||||||
|
### 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
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Ejecutar los tests
|
## Despliegue
|
||||||
|
|
||||||
Una vez dentro de psql:
|
El proyecto incluye Gitea Actions que automáticamente:
|
||||||
|
1. Construye la imagen Docker
|
||||||
|
2. La sube al registro
|
||||||
|
3. Despliega usando docker-compose
|
||||||
|
|
||||||
```sql
|
### Variables Requeridas en Gitea
|
||||||
\i postgres/tests/test_all.sql
|
|
||||||
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Esto ejecutará 18 tests que validan:
|
**Flujo de autenticación:**
|
||||||
- Existencia de tablas y tipos
|
|
||||||
- Funcionamiento de triggers
|
|
||||||
- Constraints de validación
|
|
||||||
- Queries típicas
|
|
||||||
- Funciones auxiliares
|
|
||||||
|
|
||||||
### 5. Explorar los datos de ejemplo
|
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
|
**Ventajas:**
|
||||||
\i postgres/tests/example_queries.sql
|
- ✅ 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á 17 queries de ejemplo que muestran:
|
### 🚦 Configuración de Reglas de Traefik
|
||||||
- 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
|
El `docker-compose.yml` configura **dos routers** en Traefik para balancear seguridad y funcionalidad PWA:
|
||||||
|
|
||||||
Para facilitar la gestión del proyecto, hay scripts helper disponibles en la carpeta `scripts/`.
|
#### Router 1: Rutas Públicas (Sin Autenticación) - Prioridad 100
|
||||||
|
|
||||||
### 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:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
# Rutas que NO requieren autenticación
|
||||||
POSTGRES_DB: tu_base_de_datos
|
- PathPrefix(`/manifest.webmanifest`) # Manifest PWA
|
||||||
POSTGRES_USER: tu_usuario
|
- PathPrefix(`/sw.js`) # Service Worker
|
||||||
POSTGRES_PASSWORD: tu_contraseña
|
- 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
|
```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)
|
### 🔍 Headers Disponibles en tu Aplicación
|
||||||
```bash
|
|
||||||
docker-compose down -v
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backup de la base de datos
|
Una vez configurado, Authentik inyecta estos headers en cada petición:
|
||||||
```bash
|
|
||||||
docker-compose exec postgres pg_dump -U riocata_user riocata > backup.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
### Restaurar backup
|
| Header | Descripción | Ejemplo |
|
||||||
```bash
|
|--------|-------------|---------|
|
||||||
docker-compose exec -T postgres psql -U riocata_user -d riocata < backup.sql
|
| `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
|
**En Nuxt**, estos headers se leen automáticamente en SSR mediante `useRequestHeaders()`.
|
||||||
```bash
|
|
||||||
docker-compose exec -T postgres psql -U riocata_user -d riocata < mi_script.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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
|
**Problema: Redirect loop infinito**
|
||||||
- **Índices GIN** en JSONB y arrays (para operadores @>, &&, etc.)
|
- Verifica que el middleware `authentik-forward-auth@file` esté correctamente configurado
|
||||||
- **Índices funcionales** para valores específicos en JSONB
|
- Revisa que la aplicación esté asociada al Outpost en Authentik
|
||||||
- **Índices de texto** para búsqueda full-text en español
|
- 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
|
**Problema: PWA no funciona offline**
|
||||||
2. **API**: Crear endpoints REST/GraphQL
|
- Verifica que las rutas públicas tengan prioridad 100
|
||||||
3. **Autenticación**: Integrar con Supabase Auth
|
- Confirma que `/sw.js`, `/manifest.webmanifest` y `/offline.html` estén en el router público
|
||||||
4. **Análisis**: Dashboards y reportes estadísticos
|
|
||||||
5. **Export**: Generar PDFs de sesiones de catación
|
|
||||||
|
|
||||||
## Desarrollo
|
## Licencia
|
||||||
|
|
||||||
Para desarrollar sobre este esquema:
|
MIT
|
||||||
|
|
||||||
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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user