Documentación técnica y funcional de los tres paneles del sistema, con foco en la arquitectura de seguridad.
dashboard_admin.html — Control total del sistema
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.
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.
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.
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.
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.
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.
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.
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.
Gestión de solicitudes de cambio de contraseña. El admin aprueba o rechaza pedidos. Ver sección Seguridad para detalle completo.
Generación y administración de códigos de registro. Ver sección Seguridad para detalle completo.
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.
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.
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.
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.
dashboard_profesor.html — Gestión académica de cursos asignados
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.
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.
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.
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.
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.
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.
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.
Centro de ayuda con documentación específica para profesores: cómo cargar notas, registrar asistencias, usar el Classroom, etc.
Limpia localStorage y redirige a login. Confirma con SweetAlert2.
dashboard_alumno.html — Consulta académica y financiera
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.
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.
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.
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.
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.
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.
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.
Documentación para alumnos: cómo ver notas, entender estados de pago, acceder al Classroom, etc.
Limpia localStorage y redirige. Confirma con SweetAlert2.
Arquitectura de protección + Secciones del panel admin
Sistema de monitoreo en tiempo real del estado de la plataforma. Al hacer clic en "Status" se renderizan las siguientes funcionalidades:
PUT /api/status/services).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.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.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.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.GET /api/status/activity que muestra eventos del eventLogger: logins, mensajes de chat, tareas del classroom, pagos, errores, etc.POST /api/status/restart/:service). Ejecuta pasos secuenciales (detener → limpiar cache → iniciar → healthcheck) con feedback visual.Sistema de recuperación de contraseñas supervisado por el administrador. No es un reset automático — requiere aprobación humana.
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.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.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.PUT /api/recuperacion/:id/rechazar. Cambia estado a "rechazada" y envía notificación al usuario informándole que fue rechazada.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.Sistema de códigos de invitación para controlar el registro de nuevos usuarios.
CEMI-AXXXXXX (alumno) o CEMI-PXXXXXX (profesor) que el admin genera para habilitar el registro. Sin código, nadie puede registrarse.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.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).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".DELETE /api/codigos-cemi/:id. Solo se pueden eliminar códigos en estado "activo" (no usados).Dos métodos de login:
isBcrypt() verifica prefijo $2. Salt de 10 rounds.XXXX-XXXX-XXXX con caracteres no ambiguos. Busca en las 3 tablas de roles (administradores, profesores, alumnos).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.
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.
Content Security Policy estricta: defaultSrc: ["'self'"], solo CDNs aprobados en scriptSrc, objectSrc: ["'none'"] (bloquea plugins), frameSrc controlado (previene clickjacking).
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.
Solo orígenes registrados: localhost:8080, localhost:3000, FRONTEND_URL, RAILWAY_PUBLIC_DOMAIN. Se filtran con .filter(Boolean) para ignorar variables no definidas.
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.
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.
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.
REST API + WebSockets
El frontend consume una REST API con 31 archivos de rutas. Todas las rutas protegidas pasan por verificarToken como middleware.
| Método | Endpoint | Descripción | Acceso |
|---|---|---|---|
| POST | /api/auth/login | Login usuario/contraseña | Público |
| POST | /api/auth/login-key | Login por CemiKey | Público |
| POST | /api/auth/register | Registro con código CEMI | Público |
| GET | /api/cursos | Todos los cursos | Admin |
| GET | /api/alumnos/:id | Datos del alumno | Admin/Alumno |
| PUT | /api/calificaciones | Actualizar notas | Profesor |
| POST | /api/asistencias | Registrar asistencia | Profesor |
| GET | /api/pagos/alumno/:id | Pagos del alumno | Admin/Alumno |
| GET | /api/status/metrics | Métricas del servidor | Admin |
| POST | /api/codigos-cemi/generar | Generar código registro | Admin |
| GET | /api/recuperacion/pendientes | Solicitudes de password | Admin |
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 CemiKeycursos → FK a idiomas, niveles, profesores, aulasinscripciones → Relación alumno↔cursocalificaciones → Notas vinculadas a inscripcionespagos → Control financiero por alumno/cursochat_conversaciones / chat_mensajes → Chat de soportecodigos_cemi → Códigos de registrosolicitudes_recuperacion → Flujo de reset de passwordnotificaciones_sistema → Notificaciones internassistema_servicios / sistema_incidentes → Status del sistemaHacé clic en cada pregunta para ver la respuesta sugerida
mainContent con innerHTML. Esto evita recargas de página completas. Usamos classList para animaciones de transición entre secciones.
npm start → server.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').
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.
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.
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.
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.
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.
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.
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.
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.
{ 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.
/api/auth/login, Classroom usa /api/auth/classroom-login. Ambos generan tokens JWT válidos.