You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Operación y configuración
Variables de entorno (principales)
- EVOLUTION_API_URL: base URL de Evolution API.
- EVOLUTION_API_INSTANCE: nombre/ID de instancia en Evolution.
- EVOLUTION_API_KEY: API key para peticiones salientes (contacts, etc.).
- WEBHOOK_URL: URL pública del webhook (puede usarse para auto-registro/config).
- WHATSAPP_COMMUNITY_ID: comunidad cuyos grupos se sincronizan.
- PORT: puerto HTTP del proxy interno (por defecto 3000).
- BOT_PORT: puerto interno del bot (por defecto 3007).
- WEB_PORT: puerto interno de la web SvelteKit (por defecto 3008).
- NODE_ENV: 'development' | 'test' | 'production'.
- METRICS_ENABLED: 'true'|'false'|'1'|'0' (por defecto habilitado salvo en test). Ej.: METRICS_ENABLED='true'
- RATE_LIMIT_PER_MIN: tokens por minuto por usuario (default 15).
- RATE_LIMIT_BURST: capacidad del bucket (default = RATE_LIMIT_PER_MIN).
- GROUP_SYNC_INTERVAL_MS: intervalo de sync de grupos (default 24h; min 10s en dev).
- GROUP_MEMBERS_SYNC_INTERVAL_MS: intervalo de sync de miembros (default 6h; min 10s en dev).
- GROUP_MEMBERS_INACTIVE_RETENTION_DAYS: días para borrar miembros inactivos (default 180).
- TZ: zona horaria para recordatorios (default Europe/Madrid).
- REMINDERS_GRACE_MINUTES: minutos de gracia tras la hora programada para enviar recordatorios atrasados (por defecto 60).
- GROUP_GATING_MODE: 'off' | 'discover' | 'enforce' (control de acceso por grupos; por defecto 'off'). Ej.: GROUP_GATING_MODE='discover'
- REACTIONS_ENABLED: 'true'|'false' para activar reacciones (por defecto 'false'). Ej.: REACTIONS_ENABLED='true'
- REACTIONS_SCOPE: 'groups'|'all' para limitar reacciones a grupos o permitir en DMs (por defecto 'groups'). Ej.: REACTIONS_SCOPE='groups'
- REACTIONS_TTL_DAYS: días para permitir la reacción ✅ al completar respecto al mensaje origen (por defecto 14). Ej.: REACTIONS_TTL_DAYS='14'
- RQ_REACTIONS_MAX_ATTEMPTS: reintentos máximos para jobs de reacción (si no se define, aplica el global). Ej.: RQ_REACTIONS_MAX_ATTEMPTS='3'
- ADMIN_USERS: lista separada por comas de IDs/JIDs autorizados para /admin (se normalizan a dígitos). Ej.: ADMIN_USERS='34600123456, 5554443333, +34 600 111 222'
- ALLOWED_GROUPS: lista separada por comas de group_id@g.us para sembrado inicial en arranque. Ej.: ALLOWED_GROUPS='12345-67890@g.us, 11111-22222@g.us'
- NOTIFY_ADMINS_ON_DISCOVERY: 'true'/'false' para avisar por DM a ADMIN_USERS al descubrir un grupo (modo 'discover'). Ej.: NOTIFY_ADMINS_ON_DISCOVERY='true'
- DATA_DIR: directorio base para la base de datos SQLite (por defecto ./data).
- DB_PATH: ruta absoluta o relativa al archivo SQLite; si se define, tiene prioridad sobre DATA_DIR. Ej.: DB_PATH='./data/tasks.db'
- MIGRATIONS_LOG_LEVEL: 'silent' para silenciar logs del migrador (en test se silencian automáticamente).
- WEB_BASE_URL: base pública de la interfaz web para construir enlaces absolutos (p. ej., /login?token=...). Obligatoria para /t web. Ej.: WEB_BASE_URL='https://wtask.org'
- DEV_AUTOSEED_DB: 'true'/'false' para sembrar automáticamente la BD en desarrollo cuando está vacía (apps/web). Ej.: DEV_AUTOSEED_DB='true'
- DEV_DEFAULT_USER: ID de usuario por defecto en desarrollo (bypass y semilla). Idealmente numérico (solo dígitos). Ej.: DEV_DEFAULT_USER='34600123456'
Endpoints operativos
- GET /metrics
- 200 si Metrics.enabled() y formato Prometheus por defecto.
- 404 si métricas deshabilitadas; 405 si método no permitido.
- GET /health
- 200 siempre (proxy interno), útil para healthcheck del contenedor.
- APIs web (requieren sesión)
- GET /api/me/tasks?status=open|recent&search=...
- status=open (por defecto): orden por due_date asc (NULL al final). Aplica gating por AllowedGroups + membresía activa (group_members). Búsqueda con LIKE ESCAPE '\'. Filtros dueBefore y soonDays (días). Paginación page/limit y hasMore/total.
- status=recent: tareas asignadas al usuario completadas en las últimas 24 h; orden por completed_at DESC; incluye campos completed y completed_at; mismo gating y búsqueda.
- GET /api/me/groups
- Devuelve solo grupos permitidos donde el usuario está activo. Incluye counts.open y counts.unassigned por grupo.
- GET /api/groups/:id/tasks?unassignedFirst=true
- Requiere que el usuario sea miembro activo del grupo y que el grupo esté permitido. Orden por due_date (NULL al final); admite parámetros unassignedFirst, onlyUnassigned y limit (clamp a 100).
- GET /api/me/preferences
- Devuelve las preferencias del usuario para recordatorios como { freq, time }. Si no hay registro previo, responde { freq: 'off', time: '08:30' }.
- POST /api/me/preferences
- Actualiza preferencias. Valida freq ∈ {off,daily,weekly,weekdays} y time en formato HH:MM (24h); normaliza hora (p. ej., 7:5 → 07:05). Upsert con updated_at; si freq='off' y no se envía time, conserva la última hora guardada (o '08:30' por defecto).
- POST /api/tasks/:id/claim
- Reclama la tarea para el usuario actual (idempotente). Requiere sesión; valida que la tarea esté abierta y aplica gating: t.group_id IS NULL o (grupo permitido y membresía activa).
- POST /api/tasks/:id/unassign
- Elimina la asignación del usuario actual (idempotente) si existe. Requiere sesión; tarea abierta y gating equivalente.
- POST /api/tasks/:id/complete
- Marca como completada (idempotente). Si es de grupo y no tiene responsables, auto-asigna al usuario que completa antes de marcarla como completada. Gating: si tiene group_id, cualquier miembro activo del grupo de un grupo allowed; si no tiene group_id, solo un asignado. Devuelve la tarea con completed y completed_at.
- PATCH /api/tasks/:id
- Actualiza { due_date: 'YYYY-MM-DD' | null, description?: string }. Valida due_date y normaliza/sanea description (texto plano, 11000 chars, colapsa espacios). Requiere sesión, tarea abierta y gating.
Arranque y servicios
- src/server.ts::start() (bot)
- proxy.ts y startup.sh (contenedor único con Bun):
- El proxy escucha en PORT (3000 por defecto) y enruta /webhook y /metrics → BOT_PORT; el resto → WEB_PORT.
- startup.sh normaliza DB_PATH/DATA_DIR a absolutas, arranca bot, espera tablas web_tokens/web_sessions y arranca la web antes del proxy.
- Valida entorno (logs de variables presentes/faltantes).
- Aplica migraciones up-only.
- Inicia HTTP y (según entorno) schedulers.
- Compresión HTTP: desactivada temporalmente (el proxy fuerza Accept-Encoding: identity hacia la web y elimina Content-Encoding/Vary/Content-Length en las respuestas; además, SvelteKit se construye con precompress=false para no generar .br/.gz).
- En tests, el migrador silencia logs; puede forzarse en cualquier entorno con MIGRATIONS_LOG_LEVEL='silent'.
Schedulers
- GroupSyncService.startGroupsScheduler() y .startMembersScheduler()
- Saltan en test; intervalos controlados por env.
- RemindersService.start()
- Tick cada minuto, filtra por zona horaria y preferencias, con ventana de gracia configurable (60 min por defecto) tras la hora programada.
- MaintenanceService.start()
- Tarea diaria; borra miembros inactivos según retención.
Datos y backups
- Data path: /app/data/tasks.db (por defecto).
- startup.sh normaliza DB_PATH y DATA_DIR a rutas absolutas para que bot y web apunten al mismo archivo, y espera a que existan web_tokens/web_sessions antes de iniciar la web.
- Migraciones con backup opcional (withBackup=false por defecto en initializeDatabase).
- Recomendación: planificar copia de seguridad periódica del directorio data/ y retención externa.
- DB_PATH permite aislar BD por rama/entorno sin tocar DATA_DIR; útil para pruebas en CapRover.
- En Docker/CapRover, el volumen por defecto es /app/data. Para persistencia, usa rutas de DB_PATH dentro de ese directorio (p. ej., /app/data/tasks-next.db).
Semilla de desarrollo (apps/web)
- Activación: establecer DEV_AUTOSEED_DB='true'. La semilla solo se ejecuta en desarrollo cuando la tabla tasks está vacía.
- Usuario por defecto: definir DEV_DEFAULT_USER con un ID numérico (p. ej., 34600123456). Se crea como usuario, se hace miembro activo de varios grupos y se usa para asignaciones.
- Ruta del archivo en dev (apps/web): por defecto tmp/tasks.db (véase apps/web/src/lib/server/env.ts). En producción, la web usa /app/data por defecto.
- Regenerar la BD de dev: detener el servidor web, borrar el archivo de BD y reiniciar con DEV_AUTOSEED_DB='true'.
- Ejemplo: rm -f tmp/tasks.db
- Datos que se crean:
- Usuarios: 35 (incluido el usuario por defecto).
- Grupos: “Familia”, “Trabajo”, “Voluntariado”, “Compras” (allowed) y “Varios” (pending).
- Allowed groups: allowed para los grupos principales; “Compras” allowed sin membresía del usuario por defecto (sirve para validar gating); “Varios” en pending.
- Membresías: el usuario por defecto activo en Familia, Trabajo y Voluntariado; otros usuarios repartidos para soportar múltiples responsables.
- Preferencias: recordatorios diarios a las 08:30 para el usuario por defecto.
- Tareas: ~3035 con mezcla rica:
- Personales (sin grupo) y de grupo.
- due_date en pasado, hoy, futuro y NULL.
- Sin responsables, con 1 responsable y con múltiples responsables (incluye “tú”).
- Completadas recientemente (≤24h) y antiguas (>48h), con completed_by coherente.
- Idempotencia: si ya existen tareas no vuelve a sembrar. Para resembrar, borra el archivo de BD o define un DB_PATH nuevo.
Métricas de referencia
- sync_runs_total, identity_alias_resolved_total, contadores/gauges específicos de colas y limpieza.
- Control de acceso por grupos (multicomunidades):
- allowed_groups_total_pending, allowed_groups_total_allowed, allowed_groups_total_blocked (gauges).
- unknown_groups_discovered_total (counter).
- messages_blocked_group_total (counter).
- commands_blocked_total (counter).
- sync_skipped_group_total (counter).
- admin_actions_total_allow, admin_actions_total_block (counters).
- Añadir nuevas métricas usando Metrics.inc/set y documentarlas aquí.
Buenas prácticas
- No arrancar schedulers en test salvo que FORCE_SCHEDULERS='true'.
- Validar nuevas env en src/server.ts::validateEnv() y documentarlas aquí.
- En apps/web, kit.csrf.checkOrigin=false debido al proxy interno; considerar alternativas si se elimina el proxy.
- Tests web con bun:test: construcción programática de apps/web (build/), arranque real del servidor y peticiones HTTP reales; ver tests/web/helpers/server.ts.
Formato de fechas en comandos
- Se aceptan únicamente YYYY-MM-DD y YY-MM-DD (YY se expande a 20YY).
- También se soportan tokens naturales: "hoy" y "mañana".
- Se rechazan otros formatos (p. ej., dd-mm, mm/dd, fechas inválidas).
- Las fechas se guardan normalizadas como YYYY-MM-DD y se muestran como dd/MM en listados y mensajes.