Files
glm-hub/README.md
T
2026-07-29 16:36:10 +00:00

578 lines
22 KiB
Markdown

<div align="center">
<img src="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png" alt="GomezLee Marketing" width="260" />
# GLM Hub
### El punto de entrada a las aplicaciones internas de GomezLee Marketing
<p>
<img src="https://img.shields.io/badge/Estado-Listo%20para%20producci%C3%B3n-6CC24A?style=for-the-badge" alt="Estado" />
<img src="https://img.shields.io/badge/Versi%C3%B3n-1.0.0-4F758B?style=for-the-badge" alt="Versión" />
<img src="https://img.shields.io/badge/Frontend-React%20%2B%20TypeScript-4F758B?style=for-the-badge" alt="Frontend" />
<img src="https://img.shields.io/badge/Backend-Supabase-6CC24A?style=for-the-badge" alt="Backend" />
</p>
> 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)
</div>
---
## 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
<table>
<tr>
<td><strong>Estado</strong><br>🟢 Listo para producción</td>
<td><strong>Versión</strong><br>v1.0.0</td>
<td><strong>Producción</strong><br><a href="https://home.digitalcompass.agency/">home.digitalcompass.agency</a></td>
<td><strong>Responsable</strong><br>Isaac Aracena</td>
</tr>
</table>
| 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<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]
```
<details>
<summary><strong>Ver flujo técnico en formato texto</strong></summary>
```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
```
</details>
### 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` |
| Administrador técnico | Eidan Then | `ethen@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.
- [ ] 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.
---
<div align="center">
---
**Documento mantenido por el equipo GLM IT**
GomezLee Marketing · 2026
</div>