CreaRack-SL

Endpoint /api/correo/digest — GET/POST informe de correo

Descripción

Handler CloudFlare Workers que gestiona la publicación y lectura de informes de buzón Zoho. Implementa un patrón double-method (POST para escribir, GET para leer) sobre el recurso /api/correo/digest.

Archivo: functions/api/correo/digest.ts (194 LOC)

Autenticación

Soporta dos caminos:

  1. CF Access (sesión del navegador): resolveTeamMember(request) — trae la identidad del header CF-Access.
  2. Bearer tokens personales (MCP_TOKENS): variable de entorno con pares nombre:token separados por comas.
    • Ej: EDER:abc123,DANI:xyz789
    • Mapea nombre normalizado → TeamMember (Edu, Dani, Txell).

Restricción crítica: tokens de servicio (maintenance-agent, dr-backup, etc.) NO pueden publicar. El informe siempre pertenece a una persona.

Función memberFromBearer(request, env): TeamMember | null

  • Extrae Authorization: Bearer <token> del header.
  • Itera pares en MCP_TOKENS, busca token coincidente.
  • Normaliza nombre (primer char mayúscula, resto minúscula).
  • Valida que sea miembro conocido (isTeamMember(cap)).

Función resolveMember(request, env): TeamMember | null

  • Intenta CF Access primero, fallback a Bearer.
  • Devuelve null si ninguno aplica (→ 403 Forbidden).

POST /api/correo/digest

Descripción: publica un nuevo informe de pasada.

Identidad requerida: miembro del staff (personal, no servicio).

Request body (JSON):

interface DigestBody {
  generated_at: string;      // ISO 8601: timestamp de la pasada
  resumen?: string;           // línea de resumen (opcional)
  ruido_count?: number;       // nº mensajes filtrados como ruido
  items: DigestItem[];        // max 50 items
}

interface DigestItem {
  nivel: 'actua' | 'enterate'; // categoría (obligatorio)
  urgente?: boolean;           // true → entra en alerta general
  remitente: string;           // dirección o nombre remitente
  asunto: string;              // línea asunto
  porque: string;              // síntesis "por qué importa"
  zoho_link?: string;          // URL del mensaje en Zoho (opcional)
}

Validaciones:

  • generated_at: string no vacío.
  • items: array con 0–50 elementos.
  • Cada item: nivel en {‘actua’, ‘enterate’}, otros campos truncados a 500 chars.

Acción:

  1. Inserta en tabla correo_digests (D1).
  2. Poda automática: elimina registros del mismo miembro con created_at < now - 30 days (decisión grill).
  3. Si hay items con urgente=true + nivel='actua':
    • Desactiva alerta anterior (UPDATE alerts SET active=0).
    • Crea nueva alerta general con tipo 'warning'.
    • Llama logActivity() para auditoría.
  4. Devuelve { ok: true, owner, items: count, urgentes: count } (201).

Errores:

  • 403 Forbidden: identidad no válida o es token de servicio.
  • 400 Bad Request: body JSON inválido, generated_at vacío, items > 50 o campo truncado.

GET /api/correo/digest

Descripción: obtiene el último informe DEL MIEMBRO que pregunta.

Identidad requerida: miembro del staff.

Respuesta (JSON):

interface DigestResponse {
  owner: string;           // 'Edu' | 'Dani' | 'Txell'
  generated_at: string;    // ISO 8601 de la pasada
  resumen: string | null;
  ruido_count: number;
  items: DigestItem[];
  created_at: string;      // timestamp de inserción en D1
}

Lógica:

  • Query: SELECT ... FROM correo_digests WHERE owner = ? ORDER BY created_at DESC LIMIT 1
  • Parsea payload (JSON) como items.
  • Si no hay registro, devuelve null (200).
  • Si payload está corrupto, devuelve informe sin items (graceful degradation).

Privacidad: cada miembro solo ve su propio informe. Si intenta GET sin autenticación válida → 403.

Tabla D1 correo_digests

Ver [[entity—migrations—table—correo-digests]].

Resumen:

  • Columnas: id, owner, generated_at, resumen, ruido_count, payload (JSON), created_at.
  • Índice: (owner, created_at DESC) para queries rápidas por miembro ordenadas.

Componentes y funciones

Importaciones

  • resolveTeamMember, isTeamMember de _lib/staff — validación de identidad.
  • logActivity de ../mcp/handlers/activity — registro de auditoría (alertas urgentes).

Constantes

  • MAX_ITEMS = 50: límite de items por pasada.
  • MAX_FIELD = 500: máximo de caracteres por campo (remitente, asunto, porque, zoho_link).

Interfaces

  • Env: acceso a D1 y MCP_TOKENS env var.
  • DigestItem, DigestBody: esquema del informe.

Flujo E2E típico

  1. Miembro ejecuta /correo en Claude (skill desatendido).
  2. Extrae categorías, remitentes, asuntos del buzón Zoho.
  3. Envía POST /api/correo/digest { generated_at, items: [...] } con su token personal.
  4. Handler valida identidad, inserta en D1, poda registros viejos.
  5. Si hay urgentes, crea alerta visual.
  6. Miembro abre dashboard o /correo → GET trae último informe.
  7. UI pinta secciones Actúa/Entérate con links a Zoho.

Testing

  • Local dev: endpoint accesible sin CF Access; pruebas manuales con curl + token.
  • E2E en sesión: pasada real de /correo → informe en D1 → widget actualizado.

Véase también

  • [[feature—workspace—correo-menu-f0]] — contexto y decisiones de la feature.
  • [[entity—src—page—correo-ui]] — página y widget que consumen este endpoint.
  • [[entity—migrations—table—correo-digests]] — esquema D1.
  • [[concept—authentication—personal-tokens]] — autenticación con MCP_TOKENS.
  • [[concept—infra—cloudflare-workers]] — contexto de workers CF.