Portal interno INSTA: base de conocimiento con Magic Login y permisos por rol
Base de conocimiento para el personal del Instituto Santiago Tapia: oferta educativa (en tarjetas y en tabla), requisitos y titulacion, becas y fechas de inscripcion, links de clases de Google Meet, preguntas frecuentes, mensajes listos y datos del instituto. Acceso con Magic Login: enlace de un solo uso, 15 minutos de vigencia, y en la base solo se guarda el SHA-256 del token. El canje es atomico, asi que dos canjes simultaneos del mismo enlace no pueden crear dos sesiones. Tres roles con 19 permisos. El catalogo lo edita direccion; los links de clases, direccion y administracion; el resto del equipo consulta. El menu se pinta con lo que el servidor autoriza y cada endpoint exige la misma clave, asi que no hay pantallas alcanzables escribiendo la direccion a mano. Toda edicion pasa por una lista blanca de campos validados y queda en la bitacora. Un precio vacio no se guarda como cero, un link que no es una direccion se rechaza, y no se puede marcar una validez oficial como confirmada sin numero de acuerdo. Pensado para leerse comodo: texto de 17 px, contraste AA, areas de toque de 44 px, tema claro y bloques plegados en telefono. La oferta, los costos, las becas, los requisitos y el contacto son los reales del instituto, tomados del CSV de oferta educativa y de la base de conocimiento. Los usuarios de acceso y los links de Meet son de demostracion. Verificado: 103 comprobaciones de API y 51 pruebas en WebKit escritorio y iPhone 13. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// Configuración central. Todo tiene valor por omisión para que la demo
|
||||
// arranque sin archivo .env.
|
||||
const bandera = (valor, porOmision) => (valor === undefined ? porOmision : valor === 'true');
|
||||
|
||||
export const config = {
|
||||
port: Number(process.env.PORT ?? 4180),
|
||||
db: {
|
||||
host: process.env.DB_HOST ?? '127.0.0.1',
|
||||
port: Number(process.env.DB_PORT ?? 3307),
|
||||
user: process.env.DB_USER ?? 'root',
|
||||
password: process.env.DB_PASSWORD ?? 'insta_root_demo',
|
||||
database: process.env.DB_NAME ?? 'insta_portal',
|
||||
},
|
||||
|
||||
// Vigencia del enlace de acceso y de la sesión, en minutos.
|
||||
magicLinkMinutos: Number(process.env.MAGIC_LINK_MINUTOS ?? 15),
|
||||
sesionMinutos: Number(process.env.SESION_MINUTOS ?? 480),
|
||||
cookieNombre: 'insta_sesion',
|
||||
|
||||
/**
|
||||
* En la demo el enlace de acceso se muestra en pantalla en lugar de
|
||||
* enviarse por correo, para que cualquiera pueda entrar y probarla.
|
||||
* OJO: con esto activo, quien conozca un correo dado de alta obtiene una
|
||||
* sesión sin más. Es una decisión consciente para una demostración con
|
||||
* datos ficticios. Para uso real hay que ponerlo en false y enviar el
|
||||
* enlace por correo.
|
||||
*/
|
||||
mostrarEnlaceEnPantalla: bandera(process.env.MOSTRAR_ENLACE, true),
|
||||
|
||||
// Cookie Secure y SameSite=Strict cuando el portal va detrás de HTTPS.
|
||||
cookieSegura: bandera(process.env.COOKIE_SEGURA, false),
|
||||
|
||||
// Solo con un proxy de confianza delante se cree el X-Forwarded-For.
|
||||
confiarProxy: bandera(process.env.CONFIAR_PROXY, false),
|
||||
|
||||
/**
|
||||
* Límite de enlaces de acceso por origen y por ventana, para que nadie los
|
||||
* pida en ráfaga. El loopback queda exento: ahí viven el desarrollo y las
|
||||
* pruebas.
|
||||
*
|
||||
* OJO detrás de un proxy: sin CONFIAR_PROXY=true todas las peticiones se
|
||||
* ven llegar desde la IP del proxy y comparten el mismo cupo, así que un
|
||||
* puñado de personas probando la demo lo agotaría entre todas.
|
||||
*/
|
||||
enlacesPorVentana: Number(process.env.ENLACES_POR_VENTANA ?? 60),
|
||||
ventanaMinutos: Number(process.env.VENTANA_MINUTOS ?? 5),
|
||||
};
|
||||
Reference in New Issue
Block a user