From 5e762dbdf750547dbc4c49b112f7dd05355e3a29 Mon Sep 17 00:00:00 2001 From: borja Date: Thu, 9 Oct 2025 10:49:04 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20a=C3=B1ade=20plan=20detallado=20para=20?= =?UTF-8?q?interfaz=20web=20con=20SvelteKit=20y=20bot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aider (openrouter/openai/gpt-5) --- docs/plan-interfaz-web.md | 256 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 docs/plan-interfaz-web.md diff --git a/docs/plan-interfaz-web.md b/docs/plan-interfaz-web.md new file mode 100644 index 0000000..9570fd6 --- /dev/null +++ b/docs/plan-interfaz-web.md @@ -0,0 +1,256 @@ +# Plan de implementación: Interfaz Web (SvelteKit) + Bot/Webhook (separado) + +Este documento define el plan para añadir una interfaz web al sistema, manteniendo el bot/webhook existente como proceso independiente y compartiendo la misma base de datos SQLite (WAL). El objetivo es proporcionar al usuario un acceso seguro, rápido y cómodo a sus tareas, configuración y feeds de calendario, con especial atención a la seguridad y a la mínima fricción. + +## 1) Decisiones fijadas + +- Arquitectura: dos procesos (apps/bot y apps/web). SvelteKit para la web (SSR, rutas de API, cookies). +- Acceso: enlace mágico por DM con token de 10 minutos, de un solo uso. Sin “recordarme”. +- Sesión: cookie de sesión (HttpOnly, SameSite=Lax, Secure en prod) + expiración por inactividad de servidor de 2 horas. +- Orden por defecto: tareas por creación descendente (más recientes primero). +- ICS: + - Horizonte temporal: 12 meses. + - Excluir tareas sin fecha. + - Feeds: + - B (por usuario+grupo, solo tareas sin responsable) como opción por defecto, con autogeneración. + - C (personal multigrupo, solo sin responsable) opcional para power users. +- Monorepo: estructura apps/bot y apps/web. Posible “shared” en el futuro para reutilizar utilidades. + +## 2) Alcance funcional (MVP) + +- Mis tareas: lista (orden creación desc), filtros (abiertas, vencen pronto), búsqueda por texto simple. +- Tareas de mis grupos: solo grupos permitidos y en los que el usuario está activo; sección destacada de “sin responsable”. +- Preferencias de recordatorios: ver y modificar frecuencia (daily/weekly/weekdays/off) y hora. Visualización de próximo recordatorio según TZ. +- Autenticación: comando /t web que devuelve URL con token. Canje en /login y cookie de sesión. +- Integraciones: + - ICS personal (solo “mis tareas” con due_date). + - ICS por usuario+grupo (solo sin responsable), autogenerados (sin clic de creación). + - ICS personal multigrupo opcional (solo sin responsable). + +## 3) Arquitectura técnica + +- apps/web (SvelteKit): + - SSR + endpoints (rutas +server.ts) para login, APIs, ICS. + - Gestión de cookies/sesión en hooks.server.ts. + - UI con Svelte (sin framework adicional salvo CSS utilitario si se desea). +- apps/bot: + - Se mantiene el webhook/servicios actuales. + - Emisión de tokens de login: puede implementarse desde el bot (insertando en DB) o delegarse al web (si el bot solo notifica al usuario la URL base + token emitido por la web). Para MVP: el bot crea el token directamente en DB y envía la URL. +- Concurrencia DB: + - SQLite en modo WAL con PRAGMA busy_timeout para ambos procesos. Reutilizar convenciones actuales de PRAGMA. + +## 4) Autenticación y sesiones + +- Emisión de token (bot): + - En /t web por DM: crear token aleatorio, guardar hash (no el token en claro), TTL 10 min, uso único, rate-limit por usuario. + - Devolver URL del tipo: https://app.example.com/login?token=XYZ +- Canje (web): + - Validar hash y caducidad; si ok, invalidar token (marcar usado). + - Crear sesión en DB (web_sessions) y emitir cookie de sesión (solo cookie de sesión, sin persistencia en disco). + - Redirigir a /app (sin token en la URL). +- Expiración: + - Idle timeout: 2 horas de inactividad. Si excede, pedir un nuevo token /t web. +- Seguridad: + - Cookies: HttpOnly, SameSite=Lax, Secure (en prod), path acotado. + - Rate limit en /login para evitar bruteforce de tokens. + - Redirección inmediata tras canje para evitar fugas por Referer. + +## 5) Calendario ICS + +- Contenido: + - Solo tareas con due_date, dentro de los próximos 12 meses. + - Título con id y descripción, notas con URL a la tarea (opcional). +- Feeds: + - Personal (usuario): “mis tareas” con due_date. + - Por usuario+grupo (B, por defecto): “sin responsable” del grupo. + - Autogeneración: “perezosa” (on-demand) al solicitar listado de feeds en la UI o al primer acceso a la URL; o proactiva (sembrado) mediante tarea que cree un token por cada par usuario+grupo activo. Recomendado: perezosa, con garantía de existencia al cargar la página “Integraciones”. + - Un feed activo por usuario+grupo (regla de unicidad). Rotación manual por el usuario (revocar y crear nuevo). + - Personal multigrupo (C, opcional): “sin responsable” agregadas de todos los grupos en los que el usuario esté activo. Un único token; revocable. +- Seguridad y control: + - Tokens largos, no caducan por tiempo (estilo ICS), siempre revocables. + - Para B/C: revocación automática al detectar que el usuario dejó de ser miembro activo del grupo (B) o de todos los grupos (C), en conciliaciones de miembros. + - Rate limit de peticiones por token/IP (ligero) y soporte de ETag/Last-Modified para minimizar carga. + +## 6) Estructura del repo (monorepo) + +- apps/bot: código actual del webhook, servicios, schedulers, migraciones. +- apps/web: SvelteKit (adapter-node). +- data/: carpeta con la base de datos SQLite compartida (ya existente). +- docs/: documentación (este archivo). +- Opcional futuro: packages/shared para extraer utilidades (normalizeWhatsAppId, métricas, tipos, etc.). + +## 7) Migraciones de base de datos (nuevas tablas) + +- web_tokens + - id, user_id, token_hash, created_at, expires_at, used_at (nullable), metadata (JSON opcional). + - Índices: (user_id), (expires_at), (token_hash único). +- web_sessions + - id (session_id), user_id, session_hash, created_at, last_seen_at, expires_at (idle cutoff), user_agent, ip (opcional). + - Índices: (user_id), (expires_at), (session_hash único). +- calendar_tokens + - id, type (‘personal’, ‘group’, ‘aggregate’), user_id, group_id (nullable), token_hash, created_at, revoked_at (nullable), last_used_at. + - Unicidad: (type, user_id, group_id) activa (si revoked_at IS NULL). + - Índices: (user_id), (group_id), (token_hash único). + +Notas: +- Guardar siempre hashes de tokens (no tokens en claro). +- Para autogeneración perezosa: crear on-demand si no existe registro activo para (user_id, group_id). + +## 8) Endpoints (apps/web) + +- Autenticación: + - GET /login?token=… (canjea token, crea sesión, redirige a /app) + - POST /api/logout (revoca sesión actual) +- APIs (todas requieren sesión válida): + - GET /api/me/tasks?status=open&search=...&page=...&limit=... + - GET /api/me/groups (grupos en los que está activo; solo allowed) + - GET /api/groups/:id/tasks?unassignedFirst=true (respeta gating y membresía) + - GET /api/me/preferences + - POST /api/me/preferences (actualiza frecuencia/hora) + - GET /api/integrations/feeds + - Genera automáticamente (si faltan) tokens B por cada grupo activo del usuario. + - Devuelve URLs para: ICS personal (mis tareas), ICS por grupo (B), y opcional ICS multigrupo (C). + - POST /api/integrations/feeds/rotate { type, groupId? } (revoca y recrea token) +- ICS (no requieren sesión; usan token en la URL): + - GET /ics/personal/:token.ics + - GET /ics/group/:token.ics + - GET /ics/aggregate/:token.ics + +## 9) UI (apps/web) + +- Páginas: + - /app (dashboard): “Mis tareas” por defecto. + - /app/groups: lista de grupos del usuario; en cada uno, “sin responsable” prominente. + - /app/preferences: frecuencia y hora de recordatorios; vista “próximo recordatorio”. + - /app/integrations: enlaces ICS + - Autogenerados: mostrar directamente botones “Copiar” y breve guía (Google/Apple/Outlook). + - Rotar/revocar: botones por feed. Avisar que rotar invalida suscripción previa. +- Interacciones: + - Filtros rápidos, búsqueda, paginación liviana. + - Estado de sesión (2h de inactividad): al expirar, mostrar mensaje con instrucción de enviar /t web. + +## 10) Seguridad + +- Tokens: + - Aleatorios criptográficos; hash en DB; TTL 10 min (web_tokens); uso único. + - calendar_tokens sin TTL (estilo ICS), siempre revocables. +- Cookies: HttpOnly, SameSite=Lax, Secure en prod, path acotado. No almacenar PII en cookies. +- CSRF: bajo riesgo con SameSite y API same-origin; añadir token anti-CSRF a mutaciones como defensa en profundidad. +- Cabeceras: + - X-Frame-Options: DENY, Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, Content-Security-Policy básica. + - Robots: noindex, nofollow. +- Gating: + - Todas las consultas filtran por user_id y validan AllowedGroups + membresía activa (group_members). +- Logs: nunca registrar tokens en claro; solo hashes o IDs. + +## 11) Observabilidad y límites + +- Métricas (via Metrics): + - web_tokens_issued_total, web_tokens_redeemed_total, web_login_success_total, web_login_failed_total + - web_sessions_active, web_api_requests_total{route=…}, ics_requests_total{type=…} + - ics_tokens_revoked_total, ics_tokens_created_total +- Rate limiting: + - Emisión de token /t web (en el bot) y /login (web). + - ICS por token/IP (p. ej., 4 req/min). +- Caching ICS: + - ETag/Last-Modified y Cache-Control: public, max-age=300 (suave), para que los clientes no abusen. + +## 12) DevOps y despliegue + +- Entornos: + - WEB_BASE_URL, COOKIE_SECRET, SESSION_IDLE_TTL_MIN=120, ICS_HORIZON_MONTHS=12, ICS_RATE_LIMIT, etc. + - Reutilizar EVOLUTION_API_* donde aplique (si se consulta API desde web). +- Build: + - SvelteKit con adapter-node; ejecución con Bun o Node en producción. +- Reverse proxy: + - apps/web servido en app.example.com (o /app). + - apps/bot mantiene su endpoint de webhook. Compartir .env según necesidad. +- Schedulers: + - Permanecen en el proceso del bot. apps/web no arranca ningún scheduler. + +## 13) Plan de trabajo por etapas + +Etapa 0 — Preparación +- Crear estructura apps/web (SvelteKit con adapter-node). +- Configurar ESLint/Prettier y CI mínimos (lint, build). +- Asegurar que la web abre la misma DB (PRAGMAs coherentes). + +Etapa 1 — Autenticación +- Migraciones: web_tokens, web_sessions. +- Bot: emisión de token de 10 min (hash, rate limit) en /t web. +- Web: endpoint /login, cookie de sesión, redirect limpio; hooks de sesión con idle timeout 2h. +- Páginas de error/expiración. + +Etapa 2 — Lectura de datos (MVP) +- APIs: /api/me/tasks, /api/me/groups, /api/groups/:id/tasks, /api/me/preferences (GET). +- UI: “Mis tareas” y “Grupos” (solo lectura). +- Orden de creación desc, filtros básicos, búsqueda. + +Etapa 3 — Preferencias +- APIs: GET/POST /api/me/preferences. +- UI: edición de frecuencia/hora y vista del próximo recordatorio. + +Etapa 4 — ICS +- Migraciones: calendar_tokens. +- APIs/UI Integraciones: autogeneración perezosa de feeds B (por usuario+grupo) y C (multigrupo opcional). +- Endpoints ICS: personal, group (B), aggregate (C), con horizonte 12 meses y solo con due_date. +- Revocación/rotación manual. Revocación automática al perder membresía (cron en bot o check dinámico). + +Etapa 5 — Pulido y observabilidad +- Métricas, rate limits, ETag/Last-Modified en ICS. +- CSP y cabeceras de seguridad. +- UX: copiar enlace, avisos claros, vacíos de estado. + +Etapa 6 — Evolutivos (posteriores) +- Edición de tareas (claim/unassign, fechas). +- Búsqueda avanzada y atajos. +- Notificaciones (SSE/polling). +- Panel admin (opcional). + +## 14) Pruebas + +- Unit: + - Emisión/canje de token, expiración, cookie y expiración por inactividad. + - Autorización de endpoints (gating, membresía). + - Generación ICS y filtros (due_date, horizonte). +- Integración: + - Flujo end-to-end: /t web → /login → /app. + - Listado de feeds y autogeneración B. +- Regresión: + - Aislar que schedulers solo corran en el bot. + +## 15) Riesgos y mitigaciones + +- Fuga de enlace de ICS: + - Tokens largos, revocación sencilla, métricas y rate-limit. Evitar ICS compartido de grupo. B y C permiten revocar por usuario. +- Doble arranque de tareas en web: + - Flag para no iniciar schedulers en apps/web. +- Concurrencia SQLite: + - WAL + busy_timeout ya configurados; operaciones ICE (lectura) mayoritarias en web. +- Fricción de login: + - /t web es rápido; expiración 10 min adecuada; mensajes claros si expira. + +## 16) Variables de entorno (propuestas, apps/web) + +- WEB_BASE_URL +- COOKIE_SECRET +- SESSION_IDLE_TTL_MIN=120 +- ICS_HORIZON_MONTHS=12 +- ICS_RATE_LIMIT_PER_MIN=4 +- NODE_ENV / BUN_ENV +- (Opcional) METRICS_ENABLED + +## 17) Métricas (nombres sugeridos) + +- web_tokens_issued_total +- web_tokens_redeemed_total +- web_login_success_total +- web_login_failed_total +- web_sessions_active +- web_api_requests_total{route} +- ics_tokens_created_total{type} +- ics_tokens_revoked_total{type} +- ics_requests_total{type} +- ics_rate_limit_hits_total + +Fin del documento.