CEAD Wiki

🛠️ CEAD Académico — Wiki técnica

Documentación técnica del sistema del Centro Educativo de Alto Desempeño "Félix de Guarania" (CEAD). DB schema v17 · Requiere WordPress 6.4+ y PHP 8.1+.

<!-- La versión del plugin NO va escrita acá: el pie de /wiki la imprime desde CEAD_ACAD_VERSION. Escrita a mano se quedó en 0.44.1 mientras el pie de la misma página decía 0.59.0. bin/check-symbols.php ahora lo detecta. -->

Esta wiki está pensada para quien quiera entender cómo está construido el proyecto: arquitectura, módulos, modelo de datos, el bot, seguridad y despliegue. Para la guía de uso en lenguaje simple, ver Wiki del usuario.

Ambas wikis se sirven online (solo lectura) en /wiki y /wiki/tecnica, renderizadas por el plugin desde estos mismos archivos Markdown (ver §5, módulo wiki/).


1. Resumen del proyecto

CEAD Académico es la plataforma digital de una institución educativa. Resuelve, en un solo sistema, toda la operación académica y de comunicación del colegio:

No es un LMS genérico: está hecho a medida para el flujo real de la institución, con roles, permisos y audiencias propios.


2. Arquitectura general

El repositorio es un monorepo con tres componentes que cooperan:

Diagrama de arquitectura: canales (web, app, WhatsApp), WordPress con tema y plugin, base de datos y bridge Vista general: los tres canales entran a WordPress (tema + plugin); el plugin habla con la base de datos y, para WhatsApp, con el bridge.

El mismo diagrama en texto:

┌─────────────────────────────────────────────────────────────┐
│                      WordPress (PHP 8.1+)                     │
│                                                              │
│   cead/ (tema)            cead-acad/ (plugin)                │
│   ───────────             ──────────────────                │
│   Landing pública    +    Toda la lógica académica + panel   │
│   (CPT Divisiones/        (/panel, /ingresar), bot, import.│
│    Vida, Customizer)      REST API, cron, autoupdater        │
└───────────────────────────────┬──────────────────────────────┘
                                 │  REST  /wp-json/caag-bot/v1/*
                                 │  (header X-Caag-Token)
                                 ▼
                  ┌───────────────────────────────┐
                  │   Bridge Node.js (Baileys)    │
                  │   WhatsApp Web ↔ WordPress     │
                  │   Corre en una PC/servidor     │
                  │   expuesto vía Cloudflare Tunnel│
                  └───────────────────────────────┘

La separación tema/plugin es deliberada: el tema puede cambiar sin perder datos ni lógica, y el plugin funciona incluso con otro tema activo (encola estilos de fallback).


3. Stack tecnológico

Capa Tecnología
Backend PHP 8.1+ (usa match, enums, named args), WordPress 6.4+
Base de datos MySQL/MariaDB (tablas custom wp_cead_acad_* + CPTs nativos de WP)
Frontend panel PHP server-rendered + CSS plano + JS vanilla (sin build step)
App móvil PWA (service worker, manifest, instalable, offline básico)
Bot Node.js + Baileys 7 (WhatsApp Web) + nginx/HTTPS o Cloudflare Tunnel
IA del bot API compatible con OpenAI (DeepSeek por defecto, configurable) + tool calling nativo
Voz Transcripción de notas de voz (STT) en el bridge
Imágenes Lectura por modelo con visión (opcional, formato multimodal OpenAI)
Documentos Extracción de texto de PDF / .docx / .odt en el servidor (opcional)
Autoupdate plugin-update-checker + GitHub Releases
CI GitHub Actions (PHPUnit + empaquetado y publicación de releases)

Decisión de diseño: cero dependencias pesadas de build. No hay Webpack, Tailwind ni Composer en runtime para el frontend; el lector de XLSX, por ejemplo, está hecho a mano con ZipArchive + SimpleXML.


4. El tema (cead/)

Tema clásico portado de un HTML+PHP original. CSS plano, sin Tailwind.


5. El plugin (cead-acad/)

Arquitectura modular. cead-acad.php es el bootstrap: define constantes, hace require_once de cada módulo y arranca Cead_Acad_Plugin::instance()->boot() en plugins_loaded.

Módulos

Módulo Responsabilidad
auth/ Login/registro estilizados, invitaciones por token, reset de contraseña, suspensión de usuarios
courses/ CPT de cursos, taxonomías (cohorte/grado), roster usuario↔curso
broadcasts/ Comunicados con audiencia polimórfica, targeting, read receipts, feed por usuario
surveys/ Builder de encuestas (6 tipos de pregunta), ventana de fechas, modo anónimo, export CSV
schedule/ CPT de eventos, feed por audiencia, export iCal (suscripción Google/Apple)
resources/ CPT de recursos (archivo o URL), taxonomías materia/tipo, ACL por audiencia
grades/ Tabla de calificaciones por (alumno × materia × periodo), boletín
panels/ CPT de tareas del delegado (estado/prioridad/vencimiento) + frontend
importers/ Importación guiada CSV/XLSX (alumnado, notas, cursos, eventos) en 3 pasos
whatsapp/ Bot CEADI: motor de estados, IA, broadcasting, cron, REST, admin (11 clases + bridge)
account/ Perfil, carné digital con QR, verificación de WhatsApp
pwa/ Service worker, manifest, instalación como app, offline
notifications/ Centro de notificaciones in-app (campana): no leídos, próximos eventos, tareas
faq/ Preguntas frecuentes configurables
wiki/ Sirve esta documentación en /wiki y /wiki/tecnica (solo lectura, desde los .md empaquetados; Markdown→HTML con Parsedown)
updates/ Autoactualización desde GitHub Releases (ver §11)
admin-ui/ Escritorio wp-admin con estética CEAD y métricas

Capas transversales (includes/)

class-cead-acad-plugin.php (orquestador), class-cead-acad-activator.php (dbDelta, roles, seed), class-cead-acad-capabilities.php (roles y caps), class-cead-acad-rewrites.php (rutas frontend), class-cead-acad-template-loader.php (override de templates desde el tema), class-cead-acad-audit.php (audit log), class-cead-acad-email.php (emails con branding), helpers.php (utilidades globales).

Convenciones de código

Tipo Convención
Funciones globales cead_acad_*
Clases Cead_Acad_* (PascalCase)
Tablas {$wpdb->prefix}cead_acad_*
Meta keys privadas _cead_acad_*
Clases CSS / Options / Cron cead-acad-* / cead_acad_*
Text-domain cead-acad
REST namespace caag-bot/v1

6. Modelo de datos

Dos familias de datos: CPTs nativos de WordPress y tablas custom.

CPTs

cead_acad_course, cead_acad_broadcast, cead_acad_event, cead_acad_resource, cead_acad_survey, cead_acad_task — con sus taxonomías (cohorte, grado, categoría de comunicado, materia, tipo de recurso).

Tablas custom (wp_cead_acad_*)

Se crean/actualizan idempotentemente con dbDelta() cuando sube CEAD_ACAD_DB_VERSION (actual: 17).

Grupo Tablas
Académico invitations, roster, grades, import_jobs, audit_log
Comunicación audiences (polimórfica, compartida por comunicados/encuestas/eventos), broadcast_reads, survey_questions, survey_responses, survey_answers
Bot WhatsApp wa_session, wa_registry, wa_state, wa_messages, wa_reports, wa_suggestions, wa_scheduled, wa_logs

Patrón clave — audiencias polimórficas: una sola tabla audiences modela "a quién va dirigido" para comunicados, encuestas y eventos, con tipos all / role / course / cohort / user. Eso permite targeting flexible sin duplicar lógica.

Las inserciones académicas son idempotentes (UNIQUE keys), de modo que reimportar un CSV actualiza en vez de duplicar.


7. El bot CEADI (módulo whatsapp/)

Arquitectura interna

Bridge (Node/Baileys) ──POST /caag-bot/v1/incoming──► WA_REST
                                                         │
                                                         ▼
                                                     WA_Engine  ←── WA_AI (IA opcional)
                                              (máquina de estados por teléfono)
                          ┌──────────────────────┼───────────────────────┐
                          ▼                      ▼                        ▼
                       WA_Store            WA_Identity            Datos del plugin
                       (tablas wa_*)       (phone→user_id)        (Schedule, Broadcasts…)
                          ▲
                  WA_Broadcaster ◄── WA_Cron ──► WA_Bridge_Client ──► bridge /api/send*

El bridge (Node.js)

bridge/index.js corre Baileys y expone endpoints autenticados con X-Caag-Token: /api/send, /api/send-image, /api/status (estado + QR), /api/restart, /api/logout. Se conecta al WordPress vía Cloudflare Tunnel. Tiene reintentos con backoff y mantiene el indicador "escribiendo…" mientras WordPress procesa.

Reportes confidenciales

Cuerpo cifrado con libsodium (sodium_crypto_secretbox) o AES-256-GCM como fallback, con código de seguimiento AAAA-#### para follow-up anónimo. Se gestionan en el buzón del panel.


8. Rutas y REST API

Frontend (/panel, gestionado por rewrites)

Públicas: /ingresar (con /login redirigiendo ahí por compatibilidad), /registro?t=<token>, /recuperar, /salir, /wiki y /wiki/tecnica (esta documentación). Autenticadas: /panel, /panel/comunicados, /panel/encuestas, /panel/horarios, /panel/recursos, /panel/boletin, /panel/delegado, /panel/secretaria, /panel/direccion.

wp-login.php se bloquea por defecto y redirige a /ingresar para usuarios del plugin.

Bloquear esa pantalla no alcanza, y por eso existe Cead_Acad_Hardening: hay al menos tres formas de autenticarse contra WordPress sin pasar nunca por ella. Un candado en la puerta principal no sirve si la ventana del fondo está abierta.

Freno de login (cead_acad_login_permitido()): dos contadores y solo cuentan los fallos. Uno por cuenta (5 en 15 min), que frena a quien adivina aunque cambie de IP; otro por IP, alto (60), para el barrido desde un mismo lugar. El tope anterior era por IP a secas y era peor que nada: todo el colegio sale por una sola IP pública, así que en un acto de registro el noveno alumno de la fila quedaba afuera sin haber hecho nada mal. La ruta se mudó de /login justamente porque las dos direcciones se confundían entre sí; el mapa nombre-interno → slug público vive en cead_acad_route_slugs().

REST API

Método Ruta Descripción
POST /wp-json/caag-bot/v1/incoming Mensaje entrante del bridge (con rate limiting)
GET /wp-json/caag-bot/v1/status Estado de la sesión (connected, number, qr)
POST /wp-json/caag-bot/v1/update-bridge-url Actualiza la URL del bridge

Los CPTs del plugin también están en /wp-json/wp/v2/<tipo> respetando capabilities.


9. Roles, permisos y seguridad

7 roles custom

cead_acad_direction, cead_acad_secretary, cead_acad_teacher, cead_acad_delegate, cead_acad_student, cead_acad_guardian, cead_acad_student_council — cada uno con capabilities cead_acad_* propias. Los admins de WP (manage_options) reciben todas las caps vía filtro user_has_cap.

Seguridad


10. Cron (tareas programadas)

Hook Frecuencia Función
cead_acad_wa_heartbeat 1 min Estado del bridge → wa_session
cead_acad_wa_broadcast 5 min Procesa un lote del job de broadcasting (batch de 10, 1s entre mensajes)
cead_acad_wa_scheduled 5 min Lanza comunicados programados vencidos
cead_acad_wa_reminders Diario Recordatorios de eventos a quienes optaron in
cead_acad_wa_gc 1 hora Limpia estados viejos (>60 min) y logs antiguos (>90 días)

11. Despliegue y autoactualización

Autoupdater

El plugin se actualiza desde GitHub Releases con plugin-update-checker (modules/updates/). Como es un repo privado, requiere un token de GitHub (constante CEAD_ACAD_GITHUB_TOKEN en wp-config.php, o guardado en CEAD Académico → Actualizaciones).

Detalles importantes:

Cómo se publica un Release (CI)

El workflow .github/workflows/publish-releases.yml se dispara en cada push a main:

  1. Lee la versión del plugin (cead-acad/cead-acad.php) y del tema (cead/style.css).
  2. Si no existe ya un Release con ese tag, lo crea con su zip:
    • Plugin → tag 0.x.y, Release normal, asset cead-acad.zip.
    • Tema → tag 1.x.y, Release normal, asset cead-theme.zip.
  3. Es idempotente: si la versión no cambió, no hace nada.

Por eso, para publicar una actualización basta con subir el número de versión en cead-acad.php y mergear a main: el Release se crea solo y los WordPress instalados lo ven en Plugins → Actualizaciones.

release-plugin.yml es un workflow manual (workflow_dispatch) que solo arma los zips como artefactos descargables.

El bridge

Se despliega aparte (Node.js). Dos escenarios soportados:

Variables relevantes: HOST (interfaz de escucha), PORT_STRICT (fallar si el puerto fijo está ocupado, en vez de correrse a otro), TUNNEL=off (no levantar cloudflared) y MAX_BODY_SIZE (tope del JSON entrante; las imágenes viajan en base64).


12. Tests y CI

php -l cead-acad/modules/whatsapp/class-wa-engine.php   # lint PHP
node --check cead-acad/bridge/index.js                  # lint del bridge
php cead-acad/tests/test-phone-normalization.php        # test de normalización

13. Para profundizar

Documento Contenido
cead-acad/README.md Referencia completa del plugin (módulo por módulo)
cead-acad/modules/whatsapp/README.md Detalle del bot
cead-acad/modules/whatsapp/EXTENDING.md Recetas para extender el bot
cead-acad/bridge/INSTALACION-VPS.md Instalación del bridge en una VPS (recomendado)
cead-acad/bridge/INSTALACION.md Instalación del bridge en una PC del colegio
cead-acad/docs/GOOGLE-CALENDAR.md Suscripción iCal a Google Calendar
ROADMAP.md Roadmap de features (las 10 implementadas)

CEAD Académico · Félix de Guarania · Documentación técnica.