GLM Hub
El punto de entrada a las aplicaciones internas de GomezLee Marketing
Portal interno para centralizar aplicaciones corporativas, administrar un catálogo compartido y gestionar solicitudes de acceso mediante Supabase, n8n y autenticación con Google.
Navegación rápida
| Información general | Objetivo | Arquitectura |
| Reglas de negocio | Configuración | Cómo funciona |
| Testing | Troubleshooting | Monitoreo |
| Estructura | Changelog | Definition of Done |
📌 Información general
| Estado 🟢 Listo para producción |
Versión v1.0.0 |
Producción home.digitalcompass.agency |
Responsable Isaac Aracena |
| Campo | Detalle |
|---|---|
| Proyecto | GLM Hub |
| Área | IT / Corporativo |
| Estado | Listo para producción |
| Developer principal | Isaac Aracena |
| IT Manager | Luis Matos |
| Product Owner | Máximo Gómez |
| Fecha de inicio | 2026-07-22 |
| Fecha de cierre | 2026-07-29 |
| Ciclo Shape Up | No definido en el repositorio |
| Board de ejecución | Pendiente de enlazar |
| PRD del proyecto | Pendiente de enlazar |
| Repositorio | https://git.digitalcompass.agency/Isaac_Aracena/glm-hub |
| URL de producción | https://home.digitalcompass.agency/ |
✨ Capacidades principales
| Catálogo compartido | Experiencia personalizada | Automatización |
|---|---|---|
| Aplicaciones publicadas visibles para todos los usuarios autorizados | Favoritos y accesos recientes por usuario | Generación de íconos con Gemini |
| Administración sin modificar código | Rol Administrador o Usuario | Solicitudes y decisiones por correo |
| Actualización en tiempo real | Paginación y búsqueda | Primera decisión protegida en Supabase |
🎯 Objetivo
Problema que resuelve
Las aplicaciones internas de GomezLee Marketing estaban distribuidas en diferentes enlaces y dependían de comunicación manual para que los colaboradores pudieran encontrarlas, identificar cuál necesitaban y solicitar acceso. Además, no existía un catálogo central compartido que pudiera ser mantenido por administradores sin modificar el código.
Solución implementada
GLM Hub centraliza los portales corporativos en una sola interfaz, permite que los administradores creen y mantengan el catálogo desde la aplicación, muestra a todos los usuarios autorizados las mismas aplicaciones publicadas, conserva favoritos y accesos recientes por usuario, genera íconos con IA y gestiona solicitudes de acceso mediante aprobaciones por correo.
Usuarios / Beneficiarios
- Colaboradores autorizados de GomezLee Marketing.
- Administradores del catálogo de aplicaciones.
- Equipo de IT Support que recibe las decisiones de acceso.
- Desarrolladores responsables de administrar los permisos dentro de cada aplicación individual.
🏗️ Arquitectura
Diagrama de flujo
flowchart LR
U[Usuario] --> F[GLM Hub<br/>React + Vite]
F --> A[Supabase Auth<br/>Google OAuth]
F --> D[(Supabase Postgres)]
F --> S[Supabase Storage]
F --> N1[n8n<br/>Generar ícono]
F --> N2[n8n<br/>Solicitar acceso]
N1 --> G[Google Gemini]
N2 --> M[Gmail]
D --> R[Roles · Catálogo · Favoritos · Solicitudes]
M --> AP[Aprobadores]
AP --> IT[IT Support]
Ver flujo técnico en formato texto
Usuario
└── Frontend React + Vite
├── Supabase Auth (Google OAuth)
├── Supabase Postgres + RLS + Realtime
├── Supabase Storage (íconos)
├── n8n → Gemini (generación de íconos)
└── n8n → Gmail → aprobadores → IT Support
Stack tecnológico
| Componente | Tecnología | Propósito |
|---|---|---|
| Frontend | React 19 + TypeScript | Interfaz del Hub |
| Build | Vite 6 | Desarrollo, validación y compilación |
| UI | CSS + Lucide React | Estilos e iconografía |
| Autenticación | Supabase Auth + Google OAuth | Inicio de sesión corporativo |
| Base de datos | Supabase / PostgreSQL | Catálogo, usuarios, favoritos, recientes y solicitudes |
| Seguridad | Row Level Security | Control de lectura y escritura según usuario y rol |
| Archivos | Supabase Storage | Almacenamiento de íconos |
| Sincronización | Supabase Realtime | Actualización del catálogo en sesiones abiertas |
| Automatización | n8n | Orquestación de generación de íconos y solicitudes |
| IA | Google Gemini | Generación de íconos para nuevas aplicaciones |
| Notificaciones | Gmail OAuth | Envío de solicitudes y decisiones por correo |
| Repositorio | Gitea | Control de versiones |
| Hosting | Servidor web GLM | Publicación del contenido de dist/ |
Integraciones externas
| Sistema | Tipo de integración | Datos que fluyen |
|---|---|---|
| Google OAuth | OAuth 2.0 mediante Supabase | Identidad, correo, nombre y foto del usuario |
| Supabase | SDK, REST, RPC, Storage y Realtime | Usuarios, roles, catálogo, íconos, favoritos, recientes y solicitudes |
| n8n | Webhooks HTTPS | Solicitudes del frontend y respuestas procesadas |
| Google Gemini | Credencial nativa de n8n | Nombre y descripción de la app; devuelve una imagen |
| Gmail | OAuth mediante n8n | Correos de aprobación y notificación a IT Support |
📐 Reglas de negocio
- Solo pueden iniciar sesión usuarios activos registrados en
glm_hub_authorized_usersy con correo del dominio@gomezleemarketing.com. - Los roles válidos son
adminymember. - Los administradores autorizados son:
- José Leopoldo Gómez —
jgomez@gomezleemarketing.com - Isaac Aracena —
iaracena@gomezleemarketing.com - Eidan Then —
ethen@gomezleemarketing.com - Máximo Gómez —
mgomez@gomezleemarketing.com - Luis Matos —
lmatos@gomezleemarketing.com
- José Leopoldo Gómez —
- Solo los administradores pueden abrir el apartado Administrar, crear, editar, publicar, ocultar o eliminar aplicaciones.
- Todos los usuarios autorizados ven el mismo catálogo de aplicaciones publicadas.
- El Hub no controla el acceso interno de cada aplicación; ese permiso se administra desde el login o backend de cada portal.
- Las categorías disponibles son Administración, Recursos Humanos y CDC.
- No se puede publicar una aplicación sin ícono.
- El nombre debe tener entre 2 y 60 caracteres y la descripción un máximo de 180 caracteres.
- Los íconos admitidos son PNG, JPG o WebP y no pueden superar 1 MB.
- Los favoritos y accesos recientes son personales y no afectan la vista de otros usuarios.
- Los favoritos se ordenan antes que las demás aplicaciones antes de aplicar la paginación.
- Los usuarios normales pueden solicitar acceso a cualquier aplicación publicada.
- No se permite una segunda solicitud pendiente del mismo usuario para la misma aplicación.
- Las solicitudes se envían individualmente a Isaac Aracena, José Leopoldo Gómez y Máximo Gómez.
- La primera decisión registrada gana; cualquier intento posterior queda bloqueado y no cambia el resultado.
- Después de aprobar o rechazar, IT Support recibe un correo con el solicitante, la aplicación, el resultado y la persona que decidió.
- La generación de íconos con IA solo está disponible para administradores y requiere nombre y descripción.
⚙️ Configuración y setup
Prerrequisitos
- Node.js y npm instalados.
- Acceso al proyecto Supabase de GLM.
- Proveedor Google habilitado en Supabase Auth.
- Instancia de n8n operativa.
- Credencial de Google Gemini configurada en n8n.
- Credencial Gmail OAuth configurada en n8n.
- Acceso al servidor o subdominio de producción.
Variables de entorno
Crear .env a partir de .env.example:
| Variable | Descripción | Dónde se obtiene |
|---|---|---|
VITE_SUPABASE_URL |
URL pública del proyecto Supabase | Supabase / configuración del proyecto |
VITE_SUPABASE_ANON_KEY |
Clave pública anon para el frontend |
Supabase / API Keys |
VITE_ICON_GENERATOR_WEBHOOK_URL |
Webhook de n8n para generar íconos | Workflow de generación de íconos |
VITE_ACCESS_REQUEST_WEBHOOK_URL |
Webhook de n8n para solicitudes de acceso | Workflow de solicitudes y aprobación |
Caution
Nunca commitear credenciales reales. El archivo
.env, las clavesservice_role, los tokens y las credenciales de Gmail o Gemini deben mantenerse fuera del repositorio.
Esquema de base de datos
Ejecutar en Supabase SQL Editor:
SUPABASE-GLM-HUB-COMPLETO.sql
El script crea o actualiza:
glm_hub_authorized_usersglm_hub_appsglm_hub_favoritesglm_hub_recent_appsglm_hub_access_requestsglm_hub_access_request_approvers- Funciones RPC para validación, solicitudes y decisiones.
- Bucket público
glm-hub-icons. - Políticas RLS.
- Publicación Realtime del catálogo.
Workflows de n8n
Importar y configurar:
n8n/GLM-Hub-Generar-Icono-Gemini-Nodo-Nativo.json
n8n/GLM-Hub-Solicitudes-Acceso-Aprobacion.json
Configuración necesaria:
- Seleccionar una credencial de Gemini en Generate an image.
- Seleccionar una credencial Gmail válida en los dos nodos de envío.
- Usar la clave
anondonde la llamada representa al usuario autenticado. - Usar
service_roleúnicamente en los nodos internos que emiten tokens o registran decisiones. - Mantener las claves únicamente en n8n; nunca enviarlas al navegador.
- En producción, Enviar solicitud a aprobadores debe usar
={{ $json.sendTo }}. - En producción, Enviar decisión a IT Support debe enviar a
itsupport@gomezleemarketing.com.
Instalación local
git clone https://git.digitalcompass.agency/Isaac_Aracena/glm-hub.git
cd glm-hub
npm install
cp .env.example .env
# Editar .env con los valores reales
npm run typecheck
npm run dev
En PowerShell, puede usarse:
Copy-Item .env.example .env
Build
npm run build
La compilación se genera en:
dist/
Deploy
El proyecto se publica en la raíz de un subdominio, por lo que vite.config.ts debe permanecer sin una propiedad base personalizada.
export default defineConfig({
plugins: [react()],
});
Copiar el contenido de dist/ directamente al Document Root del subdominio:
DocumentRoot/
|-- index.html
|-- assets/
|-- glm-logo.png
`-- archivos de favicon
No colocar una carpeta dist dentro del Document Root.
Para probar el mismo build con XAMPP, usar un VirtualHost que apunte directamente a la carpeta donde se copió el contenido de dist/. Servirlo como http://localhost/glm-hub/ sin configurar base hará que los assets se busquen desde una ruta incorrecta.
Redirects de autenticación
Registrar en Supabase las URLs permitidas que correspondan al entorno:
https://home.digitalcompass.agency/
http://localhost:4173/
Si se utiliza un VirtualHost local, registrar también su URL completa.
🔄 Cómo funciona
Flujo general
- El usuario entra al Hub y selecciona Continuar con Google.
- Supabase Auth valida la cuenta de Google.
- El frontend consulta
glm_hub_authorized_userspara confirmar que el usuario esté activo y conocer su rol. - El catálogo compartido se carga desde
glm_hub_apps. - Los favoritos y recientes se cargan para el usuario autenticado.
- Supabase Realtime mantiene el catálogo actualizado en sesiones abiertas.
- Los administradores pueden crear, editar, ocultar o eliminar aplicaciones.
- Los usuarios normales pueden solicitar acceso a una aplicación publicada.
Generación de íconos
- El administrador completa nombre y descripción.
- El frontend llama el webhook
glm-hub-generar-icono. - n8n valida el JWT y confirma el rol de administrador.
- Gemini genera una imagen cuadrada sin texto.
- n8n devuelve el archivo binario al frontend.
- El ícono se previsualiza y se guarda en Supabase Storage al publicar.
Solicitud de acceso
- El usuario selecciona un área y una aplicación publicada.
- n8n registra la solicitud en Supabase.
- Supabase emite un token único para cada aprobador.
- Gmail envía un correo individual a cada aprobador.
- El aprobador confirma Aceptar o Rechazar.
- Supabase registra atómicamente la primera decisión.
- n8n envía el resultado a
itsupport@gomezleemarketing.com. - Cualquier clic posterior muestra que la solicitud ya fue atendida.
Triggers
| Trigger | Frecuencia | Descripción |
|---|---|---|
| Google OAuth | Bajo demanda | Inicio de sesión de un usuario |
| Realtime de Supabase | En tiempo real | Actualización del catálogo compartido |
POST /webhook/glm-hub-generar-icono |
Bajo demanda | Generación de ícono con Gemini |
POST /webhook/glm-hub-solicitar-acceso |
Bajo demanda | Registro y envío de solicitud |
GET /webhook/glm-hub-confirmar-solicitud |
Bajo demanda | Pantalla de confirmación de decisión |
POST /webhook/glm-hub-resolver-solicitud |
Bajo demanda | Registro de aprobación o rechazo |
🧪 Testing
Casos de prueba mínimos
| Caso | Input | Output esperado | Estado |
|---|---|---|---|
| Login autorizado | Usuario activo del dominio GLM | Acceso al Hub | Validado |
| Usuario inactivo | Registro con is_active = false |
Acceso denegado | Validado |
| Rol administrador | Cuenta con role = admin |
Muestra Administrar | Validado |
| Rol usuario | Cuenta con role = member |
No muestra controles administrativos | Validado |
| Catálogo compartido | Admin publica una app | Todos ven la misma app | Validado en Supabase; realizar smoke test final con segundo usuario |
| Publicación sin ícono | Formulario sin archivo | Publicación bloqueada | Validado |
| Generar ícono | Nombre y descripción válidos | Imagen generada y previsualizada | Validado |
| Cambio de pestaña | Salir y regresar al navegador | Mantiene vista y formulario | Validado |
| Favoritos | Marcar una aplicación | Se guarda solo para el usuario | Validado |
| Paginación | Más de 10 aplicaciones | Navegación compacta y usable | Implementado |
| Solicitud de acceso | Usuario y app válidos | Correos enviados a aprobadores | Validado |
| Primera decisión | Un aprobador acepta o rechaza | Registra resultado y notifica a IT | Validado |
| Segundo clic | Otro aprobador intenta decidir | No modifica la primera decisión | Validado |
| Build de producción | npm run build |
Genera dist/ sin errores |
Validado |
🛠️ Errores conocidos y troubleshooting
| Error | Causa probable | Solución |
|---|---|---|
| Pantalla en blanco en XAMPP | El build de raíz se sirve dentro de /glm-hub/ |
Usar VirtualHost o npm run preview; no agregar base para producción en subdominio raíz |
| Assets con 404 | index.html apunta a /assets/ pero el servidor usa una subcarpeta |
Servir el contenido de dist/ desde el Document Root |
| Google regresa a una URL incorrecta | Redirect no autorizado | Agregar la URL exacta en Supabase Auth |
| Usuario no ve Administrar | Rol desactualizado o sesión antigua | Confirmar role = admin, cerrar sesión y volver a entrar |
| HTTP 401 en n8n | JWT vencido o encabezado incorrecto | Usar el token dinámico enviado por el frontend |
| HTTP 403 en una RPC | Clave o permisos incorrectos | Verificar si el nodo requiere anon, JWT o service_role |
| Output vacío de HTTP Request | Respuesta configurada como archivo o envuelta con headers | Usar Response Format: JSON y revisar la estructura esperada |
| Solicitud duplicada | Ya existe una solicitud pendiente | Atender o eliminar la solicitud anterior |
| No se genera ícono | Credencial Gemini no seleccionada | Revisar el nodo Generate an image |
| Ícono rechazado | Archivo mayor a 1 MB o MIME no permitido | Convertir a PNG/JPG/WebP y reducir tamaño |
📊 Monitoreo
- n8n Executions: revisar ejecuciones fallidas o detenidas en ramas de error.
- Supabase Logs: revisar errores de Auth, REST, RLS y Storage.
- Solicitudes pendientes: consultar
glm_hub_access_requestsfiltrandostatus = 'pending'. - Correos: confirmar entregas desde la cuenta Gmail conectada a n8n.
- Output normal: catálogo cargado, sesión persistente, Realtime activo y webhooks respondiendo correctamente.
- Alertas automáticas: no configuradas actualmente; el monitoreo se realiza desde n8n y Supabase.
Consulta rápida:
select
requester_name,
requester_email,
app_name,
status,
decided_by_name,
decided_by_email,
created_at,
decided_at
from public.glm_hub_access_requests
order by created_at desc;
🗂️ Estructura del repositorio
glm-hub/
|-- README.md
|-- .env.example
|-- index.html
|-- package.json
|-- package-lock.json
|-- tsconfig.json
|-- vite.config.ts
|-- SUPABASE-GLM-HUB-COMPLETO.sql
|-- dist/
| |-- index.html
| `-- assets/
|-- n8n/
| |-- GLM-Hub-Generar-Icono-Gemini-Nodo-Nativo.json
| `-- GLM-Hub-Solicitudes-Acceso-Aprobacion.json
|-- public/
| `-- logos y favicons
|-- scripts/
|-- src/
| |-- components/
| |-- lib/
| |-- App.tsx
| `-- main.tsx
`-- documentación técnica adicional
node_modules/ y .env nunca deben subirse al repositorio. En el flujo actual de despliegue manual, dist/ puede mantenerse versionado para entregar una compilación ya validada.
🧾 Changelog
2026-07-29 — v1.0.0
- Catálogo compartido en Supabase.
- Autenticación con Google y control de roles.
- Favoritos y accesos recientes por usuario.
- Administración completa del catálogo.
- Realtime para aplicaciones publicadas.
- Generación de íconos con Gemini mediante n8n.
- Solicitudes de acceso con aprobación o rechazo por correo.
- Primera decisión protegida de forma atómica.
- Paginación automática en acceso rápido, catálogo y administración.
- Build preparado para la raíz de un subdominio.
2026-07-28 — v0.9.0
- Persistencia de pantalla al cambiar de pestaña.
- Correcciones de CORS y JWT para generación de íconos.
- Integración con el nodo nativo de Gemini en n8n.
2026-07-22 — v0.1.0
- Inicio del proyecto y diseño inicial del portal.
🧠 Decisions log
DEC-001 — Catálogo compartido en Supabase
- Fecha: 2026-07-25
- Contexto: El catálogo guardado en
localStoragesolo existía en el navegador del administrador. - Opciones consideradas:
localStoragevs. base de datos compartida. - Decisión: Guardar aplicaciones en Supabase.
- Razón: Todos los usuarios deben ver el mismo catálogo y los administradores deben actualizarlo sin modificar código.
DEC-002 — Acceso individual fuera del Hub
- Fecha: 2026-07-29
- Contexto: Se evaluó controlar permisos por aplicación desde el Hub.
- Opciones consideradas: Permisos centralizados en el Hub vs. permisos en cada aplicación.
- Decisión: El Hub muestra todas las aplicaciones publicadas y cada aplicación controla su propio acceso.
- Razón: Evita duplicar reglas y mantiene la responsabilidad de seguridad en cada portal.
DEC-003 — Favoritos y recientes por usuario
- Fecha: 2026-07-28
- Contexto: El catálogo debe ser compartido, pero la personalización no.
- Decisión: Guardar favoritos y accesos recientes asociados al usuario autenticado.
- Razón: Cada persona conserva su propia experiencia sin afectar a los demás.
DEC-004 — Generación de íconos desde n8n
- Fecha: 2026-07-28
- Contexto: La clave de Gemini no puede exponerse en el frontend.
- Decisión: Usar un webhook de n8n y el nodo nativo Generate an image.
- Razón: Mantiene las credenciales fuera del navegador y permite validar el rol administrativo.
DEC-005 — Primera decisión gana
- Fecha: 2026-07-29
- Contexto: Tres aprobadores reciben botones para la misma solicitud.
- Decisión: Registrar atómicamente la primera aprobación o rechazo y bloquear las siguientes.
- Razón: Evita decisiones contradictorias y correos duplicados a IT Support.
DEC-006 — Build sin base personalizado
- Fecha: 2026-07-29
- Contexto: La aplicación se desplegará en la raíz de un subdominio.
- Decisión: No configurar
base: '/glm-hub/'en Vite. - Razón: Los assets deben resolverse desde
/en producción.
👥 Contactos del proyecto
| Rol | Nombre | Contacto |
|---|---|---|
| Product Owner | Máximo Gómez | mgomez@gomezleemarketing.com |
| IT Manager | Luis Matos | lmatos@gomezleemarketing.com |
| Developer principal | Isaac Aracena | iaracena@gomezleemarketing.com |
| Administrador técnico | Eidan Then | ethen@gomezleemarketing.com |
| Soporte | GLM IT Support | itsupport@gomezleemarketing.com |
✅ Definition of Done
- Catálogo compartido en Supabase.
- Autenticación con Google operativa.
- Roles de administrador y usuario aplicados.
- RLS configurado para tablas y Storage.
- Favoritos y recientes por usuario.
- Paginación implementada.
- Generación de íconos con IA operativa.
- Workflows de n8n exportados en
/n8n. - SQL completo incluido en el repositorio.
- Variables documentadas en
.env.example. - Solicitudes de acceso y primera decisión validadas.
- Código y build listos para Gitea.
- Probado localmente y con webhooks reales.
- Ejecutar smoke test final con un segundo usuario en producción.
- Enlazar el board y el PRD oficiales.
- Registrar validación final de Luis Matos.
- Registrar aprobación final de Máximo Gómez.
Documento mantenido por el equipo GLM IT
GomezLee Marketing · 2026