Radiografía completa de la plataforma: arquitectura, modelo de datos, subsistemas (Relacionamiento, Contenidos y Eventos), módulo de Beneficios, sistema de codificación, autenticación y roles, API, automatizaciones y reglas de negocio. Reescritura de la V.2 (03-jun-2026): actualizada contra el código real en producción e incorporando todo lo construido desde entonces.
Stack tecnológico
Plataforma SvelteKit + Supabase, desplegada en Vercel.
| Capa | Tecnología | Versión | Rol |
|---|---|---|---|
| Frontend | SvelteKit (Svelte 4) | 2.x | SSR + SPA híbrido |
| Styling | TailwindCSS | 3.4.x | utilidades CSS |
| Build | Vite | 5.x | bundler + HMR |
| Backend / BaaS | Supabase | supabase-js 2.45+ | Auth, DB (Postgres), Storage, RPC |
| SSR Auth | @supabase/ssr | 0.5.x | cookies server-side |
| Nodemailer + Resend | 8.x | SMTP relay vía lpdi.co | |
| Hosting | Vercel | adapter-vercel 5.4 | Edge + Serverless + Cron |
| DNS / CDN | Cloudflare | — | DNS, SSL, caché |
| QR | qrcode | 1.5.x | generación de QR |
| Testing | Playwright / Vitest | 1.59 / 4.1 | E2E / unitarios |
| Tipos | TypeScript | 5.x | type safety |
.from().select() + RPC). El estado del esquema se versiona en supabase/migrations/ (205 migraciones a la fecha).Arquitectura y subsistemas
Una plataforma (SL) con tres subsistemas de gestión + módulos transversales.
| Sigla | Subsistema / módulo | Qué gestiona |
|---|---|---|
| SL | Sistema LPDI | la plataforma completa (eco.lpdi.co) |
| SGR | Gestión del Relacionamiento CRM | registro y perfiles de Startups/PyMEs e ICGs, usuarios, equipos, matchmaking, ecosistema |
| SGC | Gestión de Contenidos | contenidos del ecosistema (subsistema hermano, comparte instancia Supabase) |
| SGE | Gestión de Eventos nuevo | registro, curaduría y directorio de eventos y organizadores (§5) |
| Perks | Beneficios nuevo | catálogo público de beneficios de aliados (§6) |
| SC | Sistema de Codificación | códigos únicos internos de entidades y eventos (§7) |
Todos comparten la misma instancia de Supabase (Auth + Postgres + RLS) y el mismo despliegue en Vercel. El ruteo es por convención SvelteKit: rutas públicas (registro, ecosistema, perks, ficha de evento), rutas autenticadas bajo /dashboard, y endpoints bajo /api. La lógica sensible siempre corre server-side (+page.server.ts / +server.ts); RLS habilitado en todas las tablas.
Modelo de datos
Tablas por dominio. Todas con RLS; el esquema vive en supabase/migrations/.
| Tabla | Contenido / campos clave | RLS |
|---|---|---|
| profiles | PK id = auth.uid(). Identidad (email, nombres, país, ciudad, empresa, cargo, teléfono, foto), redes sociales, languages/industries (jsonb), completion_score, timestamps. | solo el propio usuario (id = auth.uid()) |
| startups | Startup/PyME/Scaleup. Identidad + negocio (pitch, ICP, problema, solución, modelo), niveles BRL/TRL/CRL, financiero, legal, presencia digital, equipo (CEO), status (DRAFT/PENDING/APPROVED/REJECTED), completion_score, deleted_at. | owner ALL; contact_email y founders SELECT |
| icgs | Inversionista/Corporativo/Gestor. icg_type (7 valores), rol e industrias de interés, niveles preferidos, contacto designado, consentimiento, deleted_at. | owner/contact ALL; públicos (is_public) SELECT |
| startup_founders | Miembros del equipo: role, can_edit_form, verification_status, invitation_token. | heredada por joins |
| consent_log | Consentimiento auditable: consent_type (terms/privacy), versión, IP, user-agent, metadata. | append-only |
| profile_deletions_log / bajas | Registro de bajas y anonimización (para el reporte diario y el flujo de anonimización). | service-role |
| Tabla | Contenido |
|---|---|
| eventos | Evento completo (contacto, datos clave, temática, registro) + workflow (status, approved_*, rejected_at, edited_*, deleted_*, publish_at) + evento_codigo, codigo_unico, organizadores_raw. Esquema detallado en el reporte del SGE. |
| super_eventos | Agrupador padre (ej. una "Tech Week"): nombre, mnemotecnia, país, años, código. |
| directorio_organizadores | Directorio vivo de organizadores (una fila por nombre normalizado); calidad, estado, vínculo a entidad, merged_into_id. |
| evento_organizadores | Puente N:M evento↔organizador (rol_en_evento, orden). |
| evento_favoritos · invitaciones_organizador · evento_permisos_gestion · organizador_criterios · evento_codigo_counters | Favoritos, invitaciones a organizadores no registrados, permisos de gestión por evento, criterios de calidad del directorio, y contadores del consecutivo de código. |
| Tabla | Contenido |
|---|---|
| perks | Beneficio de un aliado: entidad ofertante (entity_type/entity_id), categoría, headline, value_badge, público objetivo, países, start/end_date, permanent, status, slug, contenido de la ficha (about, includes, steps). |
| perk_categories | Catálogo de categorías de beneficio (id, label, color) — fuente del color de marca por categoría. |
| admin_roles | Rol GLOBAL: super_admin, curador (con scope). Legacy purgados en mig 077. |
| module_permissions | Rol POR MÓDULO (eventos/beneficios): admin, curador, editor_confianza, duplicador, publicador, organizadores. |
| event_editor_privileges · event_autopublish_privileges | Privilegios por correo: editar con auto-aprobación / publicar sin curaduría. |
industries · subindustries · paises / catalogo_pais · catalogo_red_social · catalogo_rol_ecosistema · catalogo_tematica_evento · catalogo_actividad_evento · catalogo_industria
catalogo_tematica_evento, catalogo_actividad_evento y catalogo_industria tienen un espejo hardcodeado en src/lib/data/*.ts regenerado por scripts/regen-event-catalogs.py. Cambiar la tabla sin regenerar el TS desalinea el formulario. El selector de país del formulario de eventos usa la lista hardcodeada (countries.ts), no la tabla.SGR — Subsistema de Relacionamiento
El CRM: registro y perfiles de entidades y usuarios.
| Sigla | Formulario | Ruta | Notas |
|---|---|---|---|
| FSS | Startup — Simplificado | /registro-startup | crea auth user + profile + startup DRAFT; envía welcome |
| FSD | Startup — Detallado | /registro-startup-detallado · /dashboard/startups/nuevo-detallado | 6 paneles con stepper, autosave, draft resume |
| FIS | ICG — Simplificado | /registro-icg · /dashboard/icg/nuevo-simplificado | crea ICG + opcionalmente usuario del contacto designado |
| FID | ICG — Detallado | /dashboard/icg/nuevo | — |
| FUS | Usuario — Simplificado | /registro-usuario | registro standalone sin entidad |
| FUD | Usuario — Detallado | /registro-usuario-detallado | + about, empresa, cargo, industrias, idiomas, redes, foto |
Los 6 paneles del FSD: (1) datos básicos, (2) negocio + niveles BRL/TRL/CRL, (3) financiero (necesidades con picker multinivel), (4) legal, (5) presencia digital, (6) equipo fundador (invitación por email + permisos). Validación por panel con highlight de faltantes.
/dashboard): sidebar con Inicio, Mis registros, Perfil, Ecosistema; cards de entidades propias con score/estado; papelera; sub-rutas de edición (/dashboard/startups/[id], /dashboard/icg/[id]) y de consulta admin (/dashboard/consulta/*)./perfil-startup?id=, etc.)./ecosistema): vista pública con listado de startups e ICGs públicos y filtros por tipo/industria/país./networking) y transferencia de entidades (/api/entity-transfer/*, ver §14).SGE — Subsistema de Gestión de Eventos
Registro, curaduría y directorio de eventos. Detalle completo en su reporte dedicado.
El SGE es el subsistema más nuevo. Su pieza central es el Formulario de registro de Eventos (FE / EventFormShell) de 4 pasos, público (/registro-evento) o desde el dashboard, que inserta en la tabla eventos vía service-role. Un evento nuevo entra como PENDING y pasa por el panel de aprobación (/dashboard/eventos/admin/aprobacion); los organizadores se sincronizan a un directorio con detección de duplicados y fusión. Cada evento recibe dos códigos (§7).
Módulo de Beneficios (Perks)
Catálogo público de beneficios ofrecidos por los aliados del ecosistema.
/perks): catálogo con búsqueda y filtros multiselección (categoría, público objetivo, país); cada beneficio es una card con la marca del aliado y el color de su categoría./perks/[slug]): hero con el descuento, "Qué incluye", "Cómo activarlo", tarjeta resumen y "Más perks de la comunidad"; accesible sin sesión (RLS filtra a status='publicado')./dashboard/beneficios/nuevo), y panel admin (/api/admin/perks/*) con aprobación, reasociación de entidad y beneficios externos.perks (categoría, headline, value_badge, público, países, vigencia, slug, contenido de ficha) + perk_categories (color por categoría). Roles vía module_permissions módulo beneficios.Sistema de Codificación (SC)
Códigos únicos internos para entidades y eventos.
El SC asigna identificadores legibles y únicos. Para eventos hay dos:
evento_codigo (cadena legible) con patrón M-PP-TTT-AA-NNNNN: modalidad (P/V/H/D) · país ISO-2 (o 00 si online) · temática principal (3 díg) · año (2 díg) · consecutivo anual. Sufijo -MNEMOTECNIA-AA si pertenece a un superevento. Ej. H-CO-008-26-00025.codigo_unico (entero correlativo interno). Ambos se generan 100% server-side en el submit final; nunca en borradores.Documento de referencia del SC: codigos-sistema-lpdi. Detalle de la generación en el reporte del SGE.
Autenticación y autorización
Supabase Auth + cookies SSR + RLS + guardas globales.
auth.admin.createUser() con email_confirm. Sesión: cookies vía @supabase/ssr, validada server-side con getUser(). Recuperación: links con token hasheado.hooks.server.ts): redirects 301 de URLs legacy; inyección de locals.supabase y locals.safeGetSession; guard global de TyC (si un usuario autenticado no tiene registro en consent_log → /aceptar-terminos, con bypass para rutas públicas/auth/API/registro); modo dual del FD según sesión.profiles CRUD solo propio; startups ALL para owner y SELECT para contacto/founders; icgs ALL para owner/contacto y SELECT para públicos; catálogos SELECT público; eventos lectura pública solo APPROVED.Roles y permisos
Dos capas: rol global y rol por módulo (arquitectura vigente, mig 080).
| Capa | Tabla | Roles | Se lee en |
|---|---|---|---|
| Rol GLOBAL | admin_roles | super_admin, curador | is_super_admin() |
| Rol POR MÓDULO | module_permissions | admin, curador, editor_confianza, duplicador, publicador, organizadores | is_module_admin(), has_module_permission(), is_event_admin(), has_event_role(), can_duplicate(), can_manage_organizadores() |
Del lado del SGR/entidades, los roles operativos son de negocio: Owner (registered_by), CEO (ceo_user_id, gestiona equipo), Editor (can_edit_form=true), Viewer, Contacto ICG. La matriz completa de rol × capacidad para eventos está en el reporte del SGE.
admin_roles, pero los gates de enforcement vigentes leen module_permissions. Los roles reales de módulo se asignan por /api/admin/module-permissions.API — endpoints
80+ endpoints bajo /api. Selección por dominio.
| Dominio | Endpoints (selección) |
|---|---|
| Auth / consentimiento | auth/register · auth/accept-consent · auth/verify-event-user · check-email · check-company-name · check-contact-email · check-profile-slug |
| Cuenta | account/delete · account/pre-delete-check · account/update-email · account/update-password · account/assign-editor · account/logout-all |
| Entidades | entities/trash · entities/delete · entities/restore · entidades/search · entidades/similares · entidades/solicitudes · me/entidades |
| Transferencia de entidad | entity-transfer/create · entity-transfer/candidates · entity-transfer/[action] |
| ICG / equipo | icg/confirm · icg/contact/invite · icg/reject · icg/unlink · icg/visibility · team/confirm · team/reject · team/resend |
| Eventos (público) | eventos/[id] · eventos/[id]/duplicar · eventos/[id]/restore · eventos/favoritos · eventos/invitar-organizador · eventos/permisos · eventos/speaker-lookup · eventos/speaker-confirmacion(-todos) · eventos/trash |
| Eventos (admin) | admin/eventos/[id] · admin/eventos/autopublish · admin/eventos/editor-privileges · admin/eventos/roles · admin/module-permissions · admin/super-eventos(/[id]/restore) |
| Organizadores | admin/organizadores(/[id]) · admin/organizadores/fusionar · admin/organizadores/criterios(/[id]) |
| Perks | perks · admin/perks/[id](/reassociate) · admin/perks/entidades · admin/perks/external |
| Otros | cities · country-iso2 · alianzas · ideas · admin/legal/publish · admin/referral-access · user-profile-autosave · dev/email-preview |
Cron jobs y automatizaciones
Vercel Cron (declarados en vercel.json) + jobs disparados por Bearer CRON_SECRET.
| Cron | Horario | Función |
|---|---|---|
| publish-scheduled | 05:00 | libera eventos aprobados con publish_at vencida |
| purge-eventos | 04:00 | REJECTED >30d → papelera; hard-delete de papelera vencida (excepto publicados/finalizados); purga supereventos |
| purge-draft-entities | 04:15 | purga de borradores de entidad abandonados |
| join-request-tick | 04:30 | procesa solicitudes de unión a entidad |
| organizador-invite-reminders | 04:45 | recordatorios a organizadores no registrados |
| speaker-host-invite-reminders | 04:50 | recordatorios a speakers/hosts por confirmar |
Otros jobs del sistema (vía pg_cron o cron externo): daily-report (reporte + CSV a [email protected]), startup-followup, startup-welcome-reminders (reminders día 3/6 + purge día 8), team-reminders, icg-followup, purge-entities, purge-trash, entity-transfer-tick, account-confirmation-lifecycle, account-definitive-anonymize (anonimización de bajas).
Taxonomías (SSOTs)
Fuentes únicas de verdad en src/lib/data/, con lint que detecta duplicación.
entity-types.ts: COMPANY_TYPES (STARTUP/PYME/SCALEUP), ICG_TYPES (7 valores), ENTITY_STATUSES (DRAFT/PENDING/APPROVED/REJECTED) con labels, colores y type-guards.investment-categories.ts: 4 categorías top (capital dilusivo/no-dilusivo/deuda/alternativas) con ~20 opciones; router que resuelve cualquier business_needs al grupo correcto.countries.ts, ecosystemRoles.ts, industriesTech.ts, industriesTraditional.ts, languages.ts, phoneCountryCodes.ts; y del SGE: eventModalities, eventCostOptions, eventThemes, eventActivities, eventThemeCodes, timezones.npm run lint:investment-categories, lint:entity-types — detectan valores fuera del SSOT.Sistema de correos
Transaccionales + Supabase Auth + reporte diario.
~22 correos transaccionales + 3 de Supabase Auth + 1 reporte diario con CSV. Implementados en email.ts, email-team.ts y email-daily-report.ts. El inventario completo (IC) es la fuente de verdad de textos y disparadores: informe-emails-lpdi-completo.
Ciclo de vida del dato
Soft-delete, purga, anonimización y transferencia.
deleted_at (+ delete_scheduled_for); restaurables por 30 días.account-definitive-anonymize). La anonimización marca los campos personales; el estado de la entidad se rige por formalized_at, no por status.entity-transfer/* + entity-transfer-tick).Integraciones externas
Servicios y credenciales.
| Servicio | Uso | Credenciales |
|---|---|---|
| Supabase | Auth, DB, Storage, RPC | PUBLIC_SUPABASE_URL, SUPABASE_ROLE_KEY (sb_secret_) |
| Resend | SMTP relay vía lpdi.co | SMTP_HOST/USER/PASS |
| Google Maps / Places | autocomplete de ubicación en el FE | PUBLIC_GOOGLE_PLACES_API_KEY |
| Vercel | hosting, Edge, Cron | token en CI/CD |
| Cloudflare | DNS, SSL | cuenta [email protected] |
Reglas de negocio
Reglas documentadas en el código.
can_edit_form=true editan; los cambios se notifican por email.consent_log (IP, user-agent, versión).PENDING y pasan por curaduría; solo correos con autopublish entran APPROVED; el histórico se importó como APPROVED. Reglas detalladas en el reporte del SGE.