GomezLee Marketing # GLM Hub ### El punto de entrada a las aplicaciones internas de GomezLee Marketing

Estado Versión Frontend Backend

> Portal interno para centralizar aplicaciones corporativas, administrar un catálogo compartido y gestionar solicitudes de acceso mediante Supabase, n8n y autenticación con Google. [Producción](https://home.digitalcompass.agency/) · [Repositorio](https://git.digitalcompass.agency/Isaac_Aracena/glm-hub)
--- ## Navegación rápida | | | | |---|---|---| | [Información general](#-información-general) | [Objetivo](#-objetivo) | [Arquitectura](#-arquitectura) | | [Reglas de negocio](#-reglas-de-negocio) | [Configuración](#-configuración-y-setup) | [Cómo funciona](#-cómo-funciona) | | [Testing](#-testing) | [Troubleshooting](#-errores-conocidos-y-troubleshooting) | [Monitoreo](#-monitoreo) | | [Estructura](#-estructura-del-repositorio) | [Changelog](#-changelog) | [Definition of Done](#-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 | | 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 ```mermaid flowchart LR U[Usuario] --> F[GLM Hub
React + Vite] F --> A[Supabase Auth
Google OAuth] F --> D[(Supabase Postgres)] F --> S[Supabase Storage] F --> N1[n8n
Generar ícono] F --> N2[n8n
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 ```text 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 1. Solo pueden iniciar sesión usuarios activos registrados en `glm_hub_authorized_users` y con correo del dominio `@gomezleemarketing.com`. 2. Los roles válidos son `admin` y `member`. 3. 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` 4. Solo los administradores pueden abrir el apartado **Administrar**, crear, editar, publicar, ocultar o eliminar aplicaciones. 5. Todos los usuarios autorizados ven el mismo catálogo de aplicaciones publicadas. 6. El Hub no controla el acceso interno de cada aplicación; ese permiso se administra desde el login o backend de cada portal. 7. Las categorías disponibles son **Administración**, **Recursos Humanos** y **CDC**. 8. No se puede publicar una aplicación sin ícono. 9. El nombre debe tener entre 2 y 60 caracteres y la descripción un máximo de 180 caracteres. 10. Los íconos admitidos son PNG, JPG o WebP y no pueden superar 1 MB. 11. Los favoritos y accesos recientes son personales y no afectan la vista de otros usuarios. 12. Los favoritos se ordenan antes que las demás aplicaciones antes de aplicar la paginación. 13. Los usuarios normales pueden solicitar acceso a cualquier aplicación publicada. 14. No se permite una segunda solicitud pendiente del mismo usuario para la misma aplicación. 15. Las solicitudes se envían individualmente a Isaac Aracena, José Leopoldo Gómez y Máximo Gómez. 16. La primera decisión registrada gana; cualquier intento posterior queda bloqueado y no cambia el resultado. 17. Después de aprobar o rechazar, IT Support recibe un correo con el solicitante, la aplicación, el resultado y la persona que decidió. 18. 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 claves `service_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: ```text SUPABASE-GLM-HUB-COMPLETO.sql ``` El script crea o actualiza: - `glm_hub_authorized_users` - `glm_hub_apps` - `glm_hub_favorites` - `glm_hub_recent_apps` - `glm_hub_access_requests` - `glm_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: ```text 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 `anon` donde 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 ```bash 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: ```powershell Copy-Item .env.example .env ``` ### Build ```bash npm run build ``` La compilación se genera en: ```text 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. ```ts export default defineConfig({ plugins: [react()], }); ``` Copiar el contenido de `dist/` directamente al Document Root del subdominio: ```text 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: ```text https://home.digitalcompass.agency/ http://localhost:4173/ ``` Si se utiliza un VirtualHost local, registrar también su URL completa. --- ## 🔄 Cómo funciona ### Flujo general 1. El usuario entra al Hub y selecciona **Continuar con Google**. 2. Supabase Auth valida la cuenta de Google. 3. El frontend consulta `glm_hub_authorized_users` para confirmar que el usuario esté activo y conocer su rol. 4. El catálogo compartido se carga desde `glm_hub_apps`. 5. Los favoritos y recientes se cargan para el usuario autenticado. 6. Supabase Realtime mantiene el catálogo actualizado en sesiones abiertas. 7. Los administradores pueden crear, editar, ocultar o eliminar aplicaciones. 8. Los usuarios normales pueden solicitar acceso a una aplicación publicada. ### Generación de íconos 1. El administrador completa nombre y descripción. 2. El frontend llama el webhook `glm-hub-generar-icono`. 3. n8n valida el JWT y confirma el rol de administrador. 4. Gemini genera una imagen cuadrada sin texto. 5. n8n devuelve el archivo binario al frontend. 6. El ícono se previsualiza y se guarda en Supabase Storage al publicar. ### Solicitud de acceso 1. El usuario selecciona un área y una aplicación publicada. 2. n8n registra la solicitud en Supabase. 3. Supabase emite un token único para cada aprobador. 4. Gmail envía un correo individual a cada aprobador. 5. El aprobador confirma **Aceptar** o **Rechazar**. 6. Supabase registra atómicamente la primera decisión. 7. n8n envía el resultado a `itsupport@gomezleemarketing.com`. 8. 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_requests` filtrando `status = '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: ```sql 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 ```text 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 `localStorage` solo existía en el navegador del administrador. - **Opciones consideradas:** `localStorage` vs. 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` | | Soporte | GLM IT Support | `itsupport@gomezleemarketing.com` | --- ## ✅ Definition of Done - [x] Catálogo compartido en Supabase. - [x] Autenticación con Google operativa. - [x] Roles de administrador y usuario aplicados. - [x] RLS configurado para tablas y Storage. - [x] Favoritos y recientes por usuario. - [x] Paginación implementada. - [x] Generación de íconos con IA operativa. - [x] Workflows de n8n exportados en `/n8n`. - [x] SQL completo incluido en el repositorio. - [x] Variables documentadas en `.env.example`. - [x] Solicitudes de acceso y primera decisión validadas. - [x] Código y build listos para Gitea. - [x] Probado localmente y con webhooks reales. ---
--- **Documento mantenido por el equipo GLM IT** GomezLee Marketing · 2026