CEMI / Defensa de Tesis
← Volver
Guía de Defensa

Plataforma CEMI — Funcionamiento Integral

Documentación técnica y funcional de los tres paneles del sistema, con foco en la arquitectura de seguridad.

Panel de Administrador

dashboard_admin.html — Control total del sistema

Descripción General

El administrador tiene acceso completo al sistema. El panel funciona como una SPA (Single Page Application): cada botón del sidebar ejecuta una función JavaScript que hace fetch a la API, recibe datos en JSON y genera el HTML dinámicamente dentro del contenedor mainContent, sin recargar la página. El sidebar se organiza en dos grupos colapsables (Administración General y Seguridad) más botones individuales.

Grupo: Administración General

📚 Cursos

Hace GET /api/cursos. Renderiza una tabla con acordeones expandibles por curso que muestran: profesor asignado, alumnos inscriptos, aula, horario, nivel e idioma. Permite crear cursos nuevos (POST), editarlos (PUT), eliminarlos (DELETE), asignar profesor y gestionar inscripciones. Incluye filtros por idioma, nivel y estado, paginación tipo slider, y exportación PDF con jsPDF.

👥 Alumnos

Hace GET /api/alumnos. Tabla con todos los alumnos mostrando legajo, nombre, apellido, email, DNI y estado. Cada fila se puede expandir para ver cursos inscriptos, calificaciones y pagos. Permite crear alumno (POST), editar datos (PUT), cambiar estado activo/inactivo, cambiar credenciales del Classroom, y eliminar (DELETE). Incluye barra de búsqueda en tiempo real.

👨‍🏫 Profesores

Hace GET /api/profesores. Misma estructura que Alumnos. Muestra datos personales y cursos asignados. CRUD completo. El admin puede asignar/desasignar cursos al profesor directamente, cambiar credenciales Classroom, y gestionar estado. Cada profesor tiene CemiKey y legajo únicos.

🛡️ Administradores

Hace GET /api/administradores. Lista de todos los administradores con su CemiKey, legajo, y datos personales. El admin puede crear otros administradores, editarlos y cambiar sus credenciales. Esta sección solo es visible para usuarios con rol administrador.

🚪 Aulas

Hace GET /api/aulas. CRUD de aulas físicas: nombre, capacidad máxima, edificio/ubicación. Las aulas se vinculan a los cursos. Se puede verificar disponibilidad y conflictos de horario. Tabla simple con botones de editar y eliminar.

🌐 Idiomas

Hace GET /api/idiomas. CRUD de idiomas disponibles en la institución: nombre del idioma y bandera emoji asociada. Los idiomas se relacionan con los cursos y niveles. Al crear un curso, el admin selecciona idioma y luego nivel.

Grupo: Seguridad (detallado en la sección Seguridad)

📊 Status

Monitor en tiempo real del estado del sistema. Muestra servicios, métricas del servidor, logs y gestión de incidentes. Ver sección Seguridad para detalle completo.

🔑 Recuperación

Gestión de solicitudes de cambio de contraseña. El admin aprueba o rechaza pedidos. Ver sección Seguridad para detalle completo.

🎟️ Códigos CEMI

Generación y administración de códigos de registro. Ver sección Seguridad para detalle completo.

Botones Individuales

💳 Pagos

Hace GET /api/pagos. Gestión financiera completa: listado de cuotas por alumno/curso, registro de pagos, generación de recibos, filtros por estado (pagado, pendiente, vencido), y reportes financieros en PDF. Permite registrar pagos individuales o masivos y generar tickets de pago estilizados.

💬 Chat Soporte

Usa Socket.IO para comunicación en tiempo real. El admin ve todas las conversaciones de soporte, puede filtrar por estado (pendiente, activa, cerrada, resuelta), responder mensajes, y recibe notificaciones de escritorio con sonido. Clase AdminChatManager gestiona la conexión WebSocket.

❓ Ayuda

Centro de ayuda integrado con documentación del sistema, preguntas frecuentes, y guías de uso para el administrador. Contenido renderizado directamente desde HTML estático en el mainContent.

🚪 Cerrar Sesión

Ejecuta localStorage.clear() y redirige a login.html. Elimina token JWT, datos del usuario y cualquier cache de sesión. Confirma con SweetAlert2 antes de ejecutar.

Panel del Profesor

dashboard_profesor.html — Gestión académica de cursos asignados

Descripción General

El profesor accede a un panel enfocado en la gestión académica de sus cursos. No tiene permisos de escritura sobre datos administrativos (no puede inscribir alumnos, ni crear cursos, ni gestionar pagos). El sidebar es plano, sin grupos colapsables. Al cargar, muestra "Mis Cursos" automáticamente.

Botones del Sidebar — Detalle

📖 Mis Cursos

Hace GET /api/cursos/profesor/:id. Muestra tarjetas de estadísticas (total cursos, total alumnos, promedio) y una tabla de cursos activos con idioma, nivel, horario, aula y cantidad de alumnos. Incluye historial de cursos finalizados en una segunda tabla. El profesor puede ver el detalle expandible de cada curso.

📋 Calificaciones

Primero muestra un selector de curso (dropdown con los cursos asignados). Al seleccionar, hace GET /api/calificaciones/:id_curso y renderiza una tabla editable con columnas: Alumno, Parcial 1, Parcial 2, Final, Promedio. El profesor modifica las notas inline y al guardar ejecuta PUT /api/calificaciones. El promedio se calcula automáticamente en el frontend.

📅 Asistencias

Selector de curso + selector de fecha. Hace GET /api/asistencias/:id_curso/:fecha. Muestra tabla con cada alumno y un dropdown de estado: Presente, Ausente, Tarde, Justificado. Al guardar, ejecuta POST /api/asistencias. Incluye vista de historial de asistencias por fecha con resumen estadístico.

👤 Mi Perfil

Hace GET /api/profesores/:id. Muestra datos personales en modo solo lectura (nombre, apellido, email, DNI, legajo, CemiKey). No se pueden editar — solo el admin puede modificar datos de un profesor. Incluye estadísticas de cursos activos e históricos.

🏫 Classroom

Botón de redirección directa a classroom.html. No carga contenido en el mainContent. Ejecuta window.location.href para navegar al aula virtual, donde el profesor gestiona tareas, anuncios, recursos y foros de sus cursos.

💬 Chat Soporte

Usa UserChatManager con Socket.IO. Permite al profesor enviar mensajes al equipo de administración. Muestra historial de conversaciones y estado de la solicitud. Recibe notificaciones de respuesta en tiempo real con badge de notificación en el botón del sidebar.

❓ Ayuda

Centro de ayuda con documentación específica para profesores: cómo cargar notas, registrar asistencias, usar el Classroom, etc.

🚪 Cerrar Sesión

Limpia localStorage y redirige a login. Confirma con SweetAlert2.

Dato clave: El profesor NO puede editar datos personales, inscribir alumnos ni gestionar pagos. Esta separación de responsabilidades es parte del diseño RBAC del sistema.

Panel del Alumno

dashboard_alumno.html — Consulta académica y financiera

Descripción General

El alumno tiene un panel de solo lectura sobre datos académicos. No puede modificar notas, inscripciones ni pagos — solo consultarlos. Tiene acceso al Classroom y al chat de soporte. Sidebar plano sin grupos.

Botones del Sidebar — Detalle

📖 Mis Cursos

Hace GET /api/cursos/alumno/:id. Muestra tarjetas de estadísticas (cursos activos, promedio general, aprobados) y tabla de cursos con estado visual: Aprobado (≥7.0), Regular (4.0–6.9), Reprobado (<4.0), Pendiente. Cada curso se puede expandir para ver horario, profesor, aula y detalle de notas.

📋 Mis Calificaciones

Hace GET /api/calificaciones/alumno/:id. Tabla de solo lectura con columnas: Curso, Parcial 1, Parcial 2, Final, Promedio. Escala visual de colores según rendimiento. Muestra el promedio general del alumno calculado en el frontend sumando y promediando todas las notas. Solo consulta, no puede editar.

💳 Mis Pagos

Hace GET /api/pagos/alumno/:id. Muestra resumen financiero (total pagado, total pendiente, cuotas impagas) y tarjetas estilizadas tipo credit-card para cada cuota. Cada tarjeta muestra: monto, fecha de vencimiento, estado (Pagado/Pendiente/Vencido), y número de referencia. Puede descargar tickets de pago como comprobante. Usa credit-card.css para el diseño visual.

👤 Mi Perfil

Hace GET /api/alumnos/:id. Datos personales en solo lectura: nombre, apellido, email, DNI, legajo, CemiKey. Incluye estadísticas de rendimiento académico (promedio, cursos aprobados, porcentaje de asistencia). No editable por el alumno.

🏫 Classroom

Redirección directa a classroom.html. Acceso al aula virtual para ver tareas, entregas, anuncios, recursos y participar en foros de discusión de sus cursos inscriptos.

💬 Chat Soporte

Mismo UserChatManager que el profesor. Permite comunicarse con la administración para consultas, problemas técnicos o solicitudes. Notificaciones en tiempo real vía Socket.IO.

❓ Ayuda

Documentación para alumnos: cómo ver notas, entender estados de pago, acceder al Classroom, etc.

🚪 Cerrar Sesión

Limpia localStorage y redirige. Confirma con SweetAlert2.

Apartado de Seguridad

Arquitectura de protección + Secciones del panel admin

Sección clave para la defensa. El sistema implementa múltiples capas de seguridad tanto en backend como frontend.

📊 Sección "Status" (Panel Admin → Seguridad → Status)

Sistema de monitoreo en tiempo real del estado de la plataforma. Al hacer clic en "Status" se renderizan las siguientes funcionalidades:

  • Estado Global: Indicador visual (operational / degraded / outage / maintenance) calculado a partir del estado de los servicios individuales y el incidente activo.
  • Servicios: Lista de 5 servicios monitoreados: Plataforma Principal, Classroom, Sistema de Pagos, Chat de Soporte, API/Backend. Cada uno tiene un estado que el admin puede cambiar manualmente (PUT /api/status/services).
  • Métricas del Servidor: Hace GET /api/status/metrics que devuelve datos reales del servidor: uso de CPU (os.cpus()), memoria RAM (os.totalmem/freemem), uptime del proceso, versión de Node.js, hostname, PID del proceso, requests/segundo y conexiones activas.
  • Ping a Servicios: Botón para hacer GET /api/status/ping/:service que mide la latencia real del servicio. Para la BD ejecuta SELECT 1 y mide el tiempo. Devuelve latencia en ms y paquetes enviados/recibidos.
  • Gestión de Incidentes: El admin puede crear incidentes (POST /api/status/incident) con título, mensaje, severidad y servicios afectados. Puede agregar actualizaciones al incidente (PUT), resolverlo (restaura el estado de los servicios afectados a operational), o eliminarlo. Los incidentes activos muestran un banner en la página principal de CEMI.
  • Logs del Sistema: Hace GET /api/status/logs que combina logs internos (systemLogs) con el eventLogger centralizado. Muestra timestamp, nivel (INFO/WARN/ERROR/DEBUG), mensaje, servicio, y categoría. Filtrable por servicio y nivel.
  • Actividad Reciente: Feed de actividad real desde GET /api/status/activity que muestra eventos del eventLogger: logins, mensajes de chat, tareas del classroom, pagos, errores, etc.
  • Reinicio de Servicios: Botón para simular reinicio de un servicio (POST /api/status/restart/:service). Ejecuta pasos secuenciales (detener → limpiar cache → iniciar → healthcheck) con feedback visual.

🔑 Sección "Recuperación" (Panel Admin → Seguridad → Recuperación)

Sistema de recuperación de contraseñas supervisado por el administrador. No es un reset automático — requiere aprobación humana.

  • Flujo completo: El usuario va a la página de recuperación → ingresa email + DNI → se verifica que coincidan en la BD (POST /api/recuperacion/solicitar) → se crea una solicitud con estado "pendiente" → se envía notificación automática a todos los administradores activos via notificaciones_sistema.
  • Vista del Admin: Hace GET /api/recuperacion/pendientes (requiere token). Muestra tabla con todas las solicitudes: nombre del solicitante, username, rol, email, DNI, fecha, estado. Las pendientes tienen badge de notificación en el sidebar.
  • Aprobar: PUT /api/recuperacion/:id/aprobar. Cambia el estado a "aprobada" y registra qué administrador aprobó. El usuario puede entonces cambiar su contraseña desde la página pública.
  • Rechazar: PUT /api/recuperacion/:id/rechazar. Cambia estado a "rechazada" y envía notificación al usuario informándole que fue rechazada.
  • Cambio de contraseña: Una vez aprobada, el usuario accede a POST /api/recuperacion/cambiar-password. La nueva contraseña se hashea con bcrypt (salt 10 rounds) y se actualiza en la BD. La solicitud se marca como "completada" y se notifica al usuario.
  • Seguridad: Los endpoints de solicitar, verificar y cambiar contraseña son públicos (sin token) porque el usuario no puede loguearse. Los endpoints de aprobar/rechazar/listar requieren token JWT con rol admin.

🎟️ Sección "Códigos CEMI" (Panel Admin → Seguridad → Códigos CEMI)

Sistema de códigos de invitación para controlar el registro de nuevos usuarios.

  • ¿Qué son? Códigos únicos tipo CEMI-AXXXXXX (alumno) o CEMI-PXXXXXX (profesor) que el admin genera para habilitar el registro. Sin código, nadie puede registrarse.
  • Generación: POST /api/codigos-cemi/generar. El admin ingresa: rol destino (alumno/profesor) y nombre del destinatario. Se genera un código con crypto.randomBytes(6) usando caracteres no ambiguos (sin 0, O, 1, I, L). Se verifica unicidad en la BD con máximo 10 intentos.
  • Listado: GET /api/codigos-cemi. Tabla con todos los códigos generados: código, rol, destinatario, nombre del admin que lo generó, fecha, y estado (activo/usado).
  • Validación (registro): POST /api/codigos-cemi/validar. Cuando un usuario intenta registrarse, ingresa el código. El sistema verifica que exista y esté en estado "activo". Si es válido, retorna el rol y nombre del destinatario. Al completar el registro, el código se marca como "usado".
  • Eliminación: DELETE /api/codigos-cemi/:id. Solo se pueden eliminar códigos en estado "activo" (no usados).
Arquitectura de Seguridad del Backend

1. Autenticación (bcrypt + CemiKey)

Dos métodos de login:

  • Usuario + Contraseña: Se compara con hash bcrypt almacenado. La función isBcrypt() verifica prefijo $2. Salt de 10 rounds.
  • CemiKey: Clave única formato XXXX-XXXX-XXXX con caracteres no ambiguos. Busca en las 3 tablas de roles (administradores, profesores, alumnos).

2. JWT (JSON Web Tokens)

Tras login exitoso se genera un token con: id_usuario, id_persona, rol, username, id_especifico. Expira en 24h (configurable). El middleware verificarToken extrae el token del header Authorization: Bearer TOKEN, lo verifica con jwt.verify(), y adjunta req.user.

3. RBAC (Control de Acceso por Roles)

Middleware verificarRol(...rolesPermitidos) se encadena después de verificarToken. Compara req.user.rol con los roles permitidos. Si no coincide, retorna 403 Forbidden. Tres roles: administrador, profesor, alumno.

4. Helmet.js (Headers HTTP)

Content Security Policy estricta: defaultSrc: ["'self'"], solo CDNs aprobados en scriptSrc, objectSrc: ["'none'"] (bloquea plugins), frameSrc controlado (previene clickjacking).

5. Rate Limiting

Ventana de 15 minutos, máximo 1000 requests por IP. Excluye rutas de chat (/api/chat) para no afectar la comunicación en tiempo real. Mensaje personalizado al exceder.

6. CORS

Solo orígenes registrados: localhost:8080, localhost:3000, FRONTEND_URL, RAILWAY_PUBLIC_DOMAIN. Se filtran con .filter(Boolean) para ignorar variables no definidas.

7. Validación de Datos

express-validator en todas las rutas POST/PUT. Sanitización con trim(), normalizeEmail(). Validación de tipos: isEmail(), isLength(), matches(/^[a-zA-Z0-9_.-]+$/). Queries parametrizadas (?) contra SQL injection.

8. Auditoría (eventLogger)

Registra eventos críticos: auth.loginSuccess, auth.loginFailed, auth.passwordRecovery, auth.register. Cada evento incluye timestamp, categoría, severidad, mensaje e IP del request. Se consultan desde Status.

9. GDPR

Exportación de datos (POST /api/gdpr/solicitar-exportacion, formatos JSON/CSV/PDF), eliminación de cuenta (POST /api/gdpr/solicitar-eliminacion), sistema de cookies configurables con banner, referencias únicas para seguimiento, notificaciones por email al usuario y admin.

10. Capas de Seguridad — Flujo Completo

Request HTTP Helmet CORS Rate Limit JWT Verify RBAC Validación SQL Param

Comunicación Backend ↔ Frontend

REST API + WebSockets

Stack Tecnológico

  • Backend: Node.js + Express.js (ES Modules)
  • Base de Datos: MySQL con mysql2/promise (pool de conexiones)
  • Frontend: HTML5/CSS3/JavaScript Vanilla (SPA dinámica)
  • Tiempo Real: Socket.IO para chat de soporte
  • Emails: Nodemailer con templates HTML personalizados
  • Deployment: Railway (PaaS). Frontend servido como estáticos por Express.

Patrón de Comunicación

El frontend consume una REST API con 31 archivos de rutas. Todas las rutas protegidas pasan por verificarToken como middleware.

// server.js — Montaje de rutas app.use("/api/auth", authRoutes); // Público app.use("/api/alumnos", verificarToken, alumnosRoutes); app.use("/api/profesores", verificarToken, profesoresRoutes); app.use("/api/cursos", verificarToken, cursosRoutes); app.use("/api/pagos", verificarToken, pagosRoutes); app.use("/api/classroom", verificarToken, classroomRoutes); // ... 25+ módulos más

Flujo de una Petición Típica

Frontend (fetch) Authorization: Bearer TOKEN Express middleware Route handler MySQL query JSON response
// Frontend: petición con token const res = await fetch(`${API_URL}/alumnos/${id}`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' } }); const data = await res.json();

Endpoints Principales

MétodoEndpointDescripciónAcceso
POST/api/auth/loginLogin usuario/contraseñaPúblico
POST/api/auth/login-keyLogin por CemiKeyPúblico
POST/api/auth/registerRegistro con código CEMIPúblico
GET/api/cursosTodos los cursosAdmin
GET/api/alumnos/:idDatos del alumnoAdmin/Alumno
PUT/api/calificacionesActualizar notasProfesor
POST/api/asistenciasRegistrar asistenciaProfesor
GET/api/pagos/alumno/:idPagos del alumnoAdmin/Alumno
GET/api/status/metricsMétricas del servidorAdmin
POST/api/codigos-cemi/generarGenerar código registroAdmin
GET/api/recuperacion/pendientesSolicitudes de passwordAdmin

Modelo de Base de Datos (tablas principales)

  • personas → Datos base (nombre, apellido, mail, dni)
  • usuarios → Credenciales (FK: id_persona, id_perfil)
  • perfiles → Roles (1=admin, 2=profesor, 3=alumno)
  • alumnos / profesores / administradores → Datos específicos con legajo y CemiKey
  • cursos → FK a idiomas, niveles, profesores, aulas
  • inscripciones → Relación alumno↔curso
  • calificaciones → Notas vinculadas a inscripciones
  • pagos → Control financiero por alumno/curso
  • chat_conversaciones / chat_mensajes → Chat de soporte
  • codigos_cemi → Códigos de registro
  • solicitudes_recuperacion → Flujo de reset de password
  • notificaciones_sistema → Notificaciones internas
  • sistema_servicios / sistema_incidentes → Status del sistema

Posibles Preguntas de Defensa

Hacé clic en cada pregunta para ver la respuesta sugerida

Arquitectura y Tecnología

¿Por qué eligieron Node.js como tecnología backend?

+
Node.js nos permite usar JavaScript tanto en frontend como en backend, simplificando el desarrollo con un solo lenguaje. Su modelo de I/O no bloqueante es ideal para manejar múltiples conexiones simultáneas, como el chat en tiempo real con Socket.IO. Express.js nos da un framework minimalista y flexible para construir la API REST.

¿Por qué MySQL y no MongoDB u otra base NoSQL?

+
El dominio es altamente relacional: alumnos inscritos en cursos, cursos con profesores, calificaciones dependientes de inscripciones, pagos vinculados a alumnos y cursos. MySQL garantiza integridad referencial con foreign keys, soporta transacciones ACID (usadas en el registro de usuarios con ROLLBACK), y permite JOINs complejos para datos consolidados.

¿Cómo funciona el patrón SPA que implementaron?

+
Los dashboards son SPA sin framework. Cada botón del sidebar ejecuta una función que: 1) hace fetch a la API REST, 2) recibe datos en JSON, 3) genera HTML con template literals, y 4) lo inyecta en el contenedor mainContent con innerHTML. Esto evita recargas de página completas. Usamos classList para animaciones de transición entre secciones.

¿Cómo se despliega la aplicación en producción?

+
Usamos Railway como PaaS. Detecta Node.js automáticamente, ejecuta npm startserver.js. Las variables de entorno (JWT_SECRET, credenciales de BD, etc.) se configuran en el dashboard de Railway. MySQL también está hosteada ahí. El frontend es servido como archivos estáticos por Express con express.static('frontend').
Seguridad

¿Cómo protegen las contraseñas de los usuarios?

+
Se hashean con bcrypt usando salt de 10 rounds. Nunca se almacenan en texto plano en producción. La función isBcrypt() verifica que el hash comience con $2 (prefijo estándar de bcrypt). La variable ALLOW_PLAINTEXT_LOGIN existe para desarrollo pero nunca se activa en producción gracias al flag isProd.

¿Cómo previenen inyecciones SQL?

+
Queries parametrizadas en todas las consultas. La librería mysql2 usa prepared statements: pool.query("SELECT * FROM usuarios WHERE username = ?", [username]). Los valores nunca se interpretan como código SQL. Además, express-validator sanitiza inputs antes de que lleguen a las queries.

¿Qué pasa si un token JWT expira?

+
El middleware captura TokenExpiredError y responde con status 401 y flag expired: true. En el frontend, cada dashboard verifica al cargar si existe token en localStorage; si no existe, redirige a login.html?session=expired. Tokens expiran en 24 horas por defecto.

¿Cómo implementaron el sistema de roles?

+
Tres roles en tabla perfiles: admin (1), profesor (2), alumno (3). Al hacer login, el rol se incluye en el JWT. El middleware verificarRol(...roles) verifica que el rol del token esté permitido para esa ruta. Cada dashboard es un HTML separado que carga solo funciones de su rol. Un alumno no puede acceder a rutas de admin aunque tenga un token válido.

¿Qué es el sistema CemiKey y para qué sirve?

+
CemiKey es un sistema de autenticación alternativo. Cada usuario recibe una clave única en formato XXXX-XXXX-XXXX, generada con crypto.randomBytes y caracteres no ambiguos (excluye 0, O, 1, I, L). Permite acceder sin usuario/contraseña. El generador verifica unicidad en las tres tablas de roles antes de asignar, con máximo 10 intentos.

¿Cómo protegen contra ataques XSS?

+
Helmet.js configura Content Security Policy limitando de dónde se cargan scripts (solo CDNs aprobados). objectSrc: 'none' bloquea plugins, frameSrc controlado previene clickjacking. La validación con express-validator sanitiza datos de entrada. Los datos se insertan como textContent cuando es posible.
Funcionalidad

¿Cómo funciona el registro de usuarios?

+
Requiere un Código CEMI válido generado por un admin. El proceso usa una transacción MySQL: 1) Crea en personas, 2) Crea en usuarios con password hasheado, 3) Genera CemiKey única, 4) Crea en tabla del rol (alumnos/profesores) con legajo auto-incrementado, 5) Marca código como usado. Si cualquier paso falla → ROLLBACK.

¿Cómo funciona el chat en tiempo real?

+
Socket.IO sobre HTTP. El servidor crea ChatServer que maneja conexiones WebSocket. Usuarios usan UserChatManager, admins usan AdminChatManager. Mensajes se persisten en MySQL (chat_conversaciones + chat_mensajes) y transmiten en tiempo real. Conversaciones tienen estados (pendiente, activa, cerrada, resuelta) y notificaciones push del navegador.

¿El sistema cumple con normativas de protección de datos?

+
Implementamos funcionalidades alineadas con GDPR: derecho a exportar datos (JSON, CSV, PDF), derecho a solicitar eliminación de cuenta, cookies configurables con banner, política de privacidad, y sistema de referencias para seguimiento. Cada solicitud genera emails automáticos al usuario y al admin con Nodemailer.

¿Cómo manejan los errores del servidor?

+
Todas las rutas usan try/catch que devuelven JSON estructurado: { success: false, message: "..." }. Morgan para logging HTTP. El eventLogger registra eventos de seguridad. En el frontend, cada fetch tiene manejo de errores con SweetAlert2 para mensajes amigables.

¿Qué mejoras implementarían a futuro?

+
1) Migrar a React o Vue para mejor mantenimiento. 2) Refresh tokens para renovar JWTs sin re-login. 3) 2FA funcional con TOTP. 4) Backups automáticos programados. 5) Tests con Jest. 6) CI/CD con GitHub Actions. 7) Dashboard de métricas con monitoreo real.

¿Qué es el Classroom y cómo se relaciona con el Dashboard?

+
El Classroom es el aula virtual (tareas, entregas, anuncios, recursos, foros). El Dashboard es el panel administrativo-académico (inscripciones, calificaciones, asistencias, pagos). Comparten credenciales pero tienen logins separados: Dashboard usa /api/auth/login, Classroom usa /api/auth/classroom-login. Ambos generan tokens JWT válidos.

¿Cómo manejaron el trabajo en equipo?

+
Git para control de versiones y GitHub como repositorio. Equipo dividido entre frontend y backend, con jefe de proyecto coordinando entregas. Branches por funcionalidad y merges controlados. Comunicación constante para asegurar que la API cumpla con las necesidades del frontend.