Sistema de Invitaciones
Documento tecnico que describe el sistema completo de invitaciones en Nvito: maquina de estados, flujos de creacion (internas y externas), templates, secciones, versionamiento, generacion HTML y analiticas.
| Campo | Valor |
|---|---|
| Version | 1.1 |
| Fecha | Marzo 2026 |
| Estado | En revision |
| Audiencia | Equipo de desarrollo, arquitectos, stakeholders |
1. Maquina de Estados
Cada invitacion en Nvito sigue un ciclo de vida estricto controlado por una maquina de estados. El servicio ApprovalWorkflowService define las transiciones validas y garantiza que ningun cambio de estado ocurra fuera de las reglas establecidas.
1.1 Diagrama de Estados
1.2 Transiciones Detalladas
| Transicion | De | A | Quien la dispara | Validaciones | Endpoint |
|---|---|---|---|---|---|
| Submit | IN_CONSTRUCTION | UNPUBLISHED | Usuario (owner/admin) | Schema no vacio, secciones requeridas presentes (solo internas) | POST /invitations/:id/submit-for-approval |
| Approve | UNPUBLISHED | PUBLISHED | Usuario (owner/admin) | Internas: genera HTML y sube a CDN. Externas: verifica que htmlUrl exista. Activa evento si esta en DRAFT | POST /invitations/:id/approve |
| Reject | UNPUBLISHED | IN_CONSTRUCTION | Usuario (owner/admin) | Requiere razon de rechazo | POST /invitations/:id/reject |
| Request Changes | UNPUBLISHED | IN_CONSTRUCTION | Usuario (owner/admin) | Requiere descripcion de cambios | POST /invitations/:id/reject (action: request_changes) |
| Unpublish | PUBLISHED | UNPUBLISHED | Usuario (owner/admin) | Webhook de despublicacion enviado | POST /invitations/:id/unpublish |
| Close | PUBLISHED | CLOSED | Usuario o sistema (auto-complete/cancel) | Registra closedBy y closeReason | POST /invitations/:id/close |
| Reopen | CLOSED | PUBLISHED | Usuario (owner/admin) | Evento debe ser ACTIVE, fecha no debe haber pasado | POST /invitations/:id/reopen |
1.3 Sincronizacion con Eventos
La maquina de estados de invitaciones esta sincronizada con el ciclo de vida de eventos:
- Invitacion publicada (
PUBLISHED): Si el evento esta enDRAFT, automaticamente transiciona aACTIVEviaEventLifecycleService.activateEventOnPublish(). - Evento cancelado (
CANCELLED): Todas las invitaciones enUNPUBLISHEDoPUBLISHEDse cierran automaticamente (CLOSED). - Evento completado (
COMPLETED): Cron job diario (medianoche, timezoneAmerica/Mexico_City) cierra invitaciones de eventos cuya fecha ya paso.
2. Tipos de Invitacion (InvitationSource)
Nvito soporta dos origenes de invitacion, discriminados por el campo source del modelo Invitation:
| Source | Descripcion | Creacion | Edicion | Versionamiento |
|---|---|---|---|---|
INTERNAL (default) | Invitacion creada con el motor de templates y IA de Nvito | Template + IA + Editor Visual Artisan | Editor visual, secciones, IA conversacional | Si (max 50 versiones) |
EXTERNAL | HTML disenado fuera de Nvito y cargado manualmente | Upload de archivo HTML | Re-upload completo del HTML | No (siempre version 1) |
Las invitaciones externas comparten la misma maquina de estados, el mismo sistema de publicacion/preview, las mismas analiticas y el mismo renderizado en nvito-invitations. La diferencia radica en como se crea y edita el contenido.
3. Flujo Completo — Invitaciones Internas (INTERNAL)
El recorrido end-to-end de una invitacion interna, desde su creacion hasta que un invitado la visualiza:
Paso 1: Creacion de la Invitacion
El usuario crea una invitacion asociada a un evento existente. Puede elegir entre:
- Seleccionar un template: Se obtiene del catalogo via
ArtisanTemplateRegistryService.getCatalog(). El template provee colores, fuentes y secciones predeterminadas. - Empezar desde cero: Se usa el schema por defecto del tipo de evento (
event.eventType.defaultSections).
Restriccion: Maximo 3 invitaciones por evento. Se genera un slug unico con formato {eventSlug}-inv-{randomSuffix}.
Endpoint: POST /invitations - Requiere permiso invitations:create:assigned.
Paso 2: Generacion con IA (Opcional)
El usuario puede utilizar la generacion con IA para crear o modificar el contenido de la invitacion a partir de lenguaje natural. El servicio AiGenerationService genera el schema y theme correspondientes, creando una nueva version en el proceso.
Paso 3: Edicion con Editor Visual Artisan
El usuario edita la invitacion en el editor visual de nvito-admin:
- Modifica secciones, textos, imagenes, colores y fuentes.
- Reordena secciones con drag & drop.
- Selecciona variantes visuales para cada seccion.
- Previsualiza en tiempo real via
POST /invitations/:id/render-draft(compilacion sin persistencia a CDN).
Endpoint: PATCH /invitations/:id - Actualiza schema y/o theme.
Paso 4: Secciones y Variantes
Cada seccion puede configurarse con una variante visual diferente. El usuario selecciona variantes desde el catalogo de section-variants/ y la configuracion se guarda en schema.sectionConfig[sectionType].variantId.
Paso 5: Preview
El usuario genera un token temporal de preview (JWT, 15 minutos de validez) via POST /invitations/:id/preview-token. Este token permite visualizar la invitacion compilada sin necesidad de publicarla, accesible via GET /invitations/preview/:token.
Paso 6: Publicacion
Al aprobar la invitacion (POST /invitations/:id/approve):
- Se compila el schema a HTML/CSS/JS via
TemplateCompilerService. - Se optimiza y agregan meta tags SEO via
HtmlOptimizerService. - Se sube a CDN (R2/MinIO) via
StorageService. - Se actualizan
htmlUrl,cssUrl,jsUrlen la BD. - Se envia webhook de revalidacion a nvito-invitations para ISR.
- Si el evento esta en DRAFT, se activa automaticamente.
Paso 7: Visualizacion por Invitados
El invitado recibe un enlace con formato https://nvito.mx/i/{slug}. La aplicacion nvito-invitations (Next.js) renderiza el micrositio usando ISR (Incremental Static Regeneration):
- Consulta
GET /invitations/public/:slugpara obtener las URLs del HTML. - Renderiza el micrositio completo con las secciones configuradas.
- Registra eventos de analiticas (VIEW) en la API.
Paso 8: RSVP del Invitado
El invitado interactua con la seccion RSVP integrada en el micrositio. Al confirmar o declinar, se registra el RSVP en la API y se dispara un evento de analitica RSVP_SUBMIT.
4. Flujo Completo — Invitaciones Externas (EXTERNAL)
Las invitaciones externas permiten a usuarios con disenos HTML propios (ej. disenadores, agencias) publicar sus invitaciones a traves de la plataforma Nvito, aprovechando el hosting, las analiticas, el sistema RSVP y la distribucion via comunicaciones.
4.1 Diagrama de Secuencia
4.2 Creacion
El usuario sube un archivo HTML (.html / .htm) desde nvito-admin. El archivo se envia como string al endpoint POST /invitations/external.
Proceso de creacion:
- Se verifica que el evento exista y pertenezca a la organizacion del usuario.
- Se verifica el limite de 3 invitaciones por evento.
- Se valida el HTML con
HtmlValidatorService(ver seccion 4.4). - Se genera un slug temporal con formato
{eventSlug}-ext-{random6chars}. - Se crea el registro en BD con
source: 'EXTERNAL',status: 'IN_CONSTRUCTION',schema: {},theme: {}. - Se sube el HTML a CDN (R2/MinIO) via
StorageService. - Se guarda un backup del HTML raw en el campo
externalHtmlRawde la BD. - Se retorna la invitacion creada junto con
warnings(si los hay).
Campos opcionales: seoTitle (max 200 chars) y seoDescription (max 500 chars) para personalizar los meta tags SEO.
4.3 Restricciones
Las invitaciones externas tienen las siguientes restricciones respecto a las internas:
| Funcionalidad | Interna | Externa |
|---|---|---|
| Editor Visual Artisan | Si | No |
| Generacion/modificacion con IA | Si | No |
| Secciones editables | Si | No |
| Versionamiento (rollback) | Si (max 50) | No |
| Edicion de schema/theme | Si (PATCH /invitations/:id) | No (lanza error) |
| Re-upload de HTML | No | Si (solo en IN_CONSTRUCTION) |
| Descarga del HTML original | No | Si |
| Preview con token | Si | Si |
| Publicacion | Si (genera HTML) | Si (usa HTML existente) |
| Analiticas | Si | Si |
| RSVP | Si | Si (si el HTML incluye formulario RSVP) |
| Comunicaciones | Si | Si |
4.4 Validacion de HTML (HtmlValidatorService)
El servicio HtmlValidatorService ejecuta un pipeline de 9 pasos de validacion que retorna { valid: boolean, errors: string[], warnings: string[] }:
| Paso | Tipo | Descripcion |
|---|---|---|
| 1. Tamano | Error | Bloquea si el HTML supera 5 MB |
| 2. Estructura | Error | Requiere <!DOCTYPE html> o <html> + etiqueta <body> |
| 3. Scripts peligrosos | Error | Bloquea: eval(), new Function(), document.cookie, document.domain, window.location =, XMLHttpRequest, WebSocket(), importScripts(), navigator.sendBeacon, fetch() |
| 4. Iframes | Error | Solo permitidos desde dominios de whitelist (Google, YouTube, Vimeo, Spotify, Maps) |
| 5. Formularios externos | Error | <form action="http(s)://..."> con URL absoluta externa bloqueado |
| 6. Meta refresh | Error | <meta http-equiv="refresh"> bloqueado |
| 7. Event handlers | Error | onerror/onload con eval, fetch, document.cookie, window.location bloqueado |
| 8. URLs relativas | Warning | Advierte sobre src/href con paths relativos (que no funcionaran) |
| 9. Viewport | Warning | Advierte si falta <meta name="viewport"> |
Los errores bloquean la creacion y retornan 400. Los warnings no bloquean pero se informan al usuario.
4.5 Re-upload
El usuario puede reemplazar el HTML de una invitacion externa que este en estado IN_CONSTRUCTION. El HTML anterior se sobreescribe de forma irreversible.
Restricciones del re-upload:
- Solo invitaciones con
source: 'EXTERNAL' - Solo en estado
IN_CONSTRUCTION(si esta publicada, debe despublicarse primero) - El nuevo HTML pasa por la misma validacion de 9 pasos
Endpoint: PUT /invitations/:id/external/reupload
4.6 Descarga del HTML Original
El usuario puede descargar el HTML original que subio, almacenado en el campo externalHtmlRaw de la BD (no requiere acceso a S3).
Endpoint: GET /invitations/:id/external/download
4.7 Publicacion de Externas
La publicacion de invitaciones externas usa el mismo flujo de aprobacion (POST /invitations/:id/approve), pero con diferencias:
- No se genera HTML: el HTML ya existe en CDN (subido en el paso de creacion/re-upload).
- No se validan secciones: solo se verifica que
htmlUrlno sea nulo. - Se genera slug amigable: si el evento tiene un custom domain activo y verificado, el slug se actualiza con el path del custom domain (ej.
boda-real-2026). Si no, se usa el nombre del evento slugificado. - Se activa el evento si esta en DRAFT (igual que internas).
- Se envia webhook ISR a nvito-invitations para revalidar la pagina.
4.8 Endpoints de Invitaciones Externas
| Metodo | Endpoint | Descripcion | Permiso |
|---|---|---|---|
POST | /invitations/external | Subir HTML y crear invitacion externa | invitations:create:assigned |
PUT | /invitations/:id/external/reupload | Reemplazar HTML de invitacion externa | invitations:update |
GET | /invitations/:id/external/download | Descargar HTML original | Autenticado |
5. Templates
5.1 Catalogo de Templates
Los templates son plantillas prediseadas almacenadas en la base de datos (tabla templates) con estado ACTIVE. Al iniciar el modulo, ArtisanTemplateRegistryService carga todos los templates activos en un cache en memoria.
Cada template esta asociado a un tipo de evento y provee una configuracion completa:
| Campo | Descripcion |
|---|---|
slug | Identificador unico (e.g., wedding-classic, xv-princess) |
eventType | Tipo de evento asociado (e.g., wedding, graduation, birthday) |
config | Objeto JSON con colores, fuentes, secciones y orden |
previewImageUrl | URL de imagen de preview para el catalogo |
tags | Etiquetas para filtrado y busqueda |
5.2 Templates por Defecto por Tipo de Evento
| Tipo de Evento | Template por Defecto |
|---|---|
| Wedding | wedding-classic |
| XV Anos | xv-princess |
| Birthday | birthday-festive |
| Baptism | baptism-celestial |
| Communion | communion-serene |
| Baby Shower | babyshower-sweet |
| Graduation | graduation-classic |
| Corporate | corporate-clean |
| Other | other-versatile |
5.3 Estructura del Template (TemplateMeta)
Cada template expone: id (slug), eventType, name, description, previewImageUrl, tags, defaultColors (primary, secondary, accent, background, text), defaultFonts (heading, body), defaultSections (visibles por defecto) y optionalSections (ocultas por defecto).
5.4 Compilacion de Templates
El TemplateBuilderService construye el HTML a partir de la config: lee secciones, selecciona variantes del registro section-variants/, ensambla HTML con placeholders Handlebars ({{variable}}), y cachea en ArtisanTemplateRegistryService.
Endpoints publicos: GET /invitations/templates (catalogo filtrable por eventType), GET /invitations/templates/:templateId (metadata), GET /invitations/variants (variantes por tipo).
6. Secciones
6.1 Tipos de Seccion Disponibles
El sistema define 18 tipos de seccion con 121 variantes visuales en total:
hero (13 variantes), welcome (9), event_details (7), countdown (8), story (8), gallery (11), itinerary (5), location (6), rsvp (8), registry (5), accommodation (5), faq (5), music (5), sponsors (5), prayer (5), thank_you (5), dress_code (5), footer (6).
6.2 Estructura de una Variante de Seccion
interface SectionVariant {
id: string; // e.g., "hero-centered-overlay"
sectionType: string; // e.g., "hero"
name: string; // Nombre legible en espanol
description: string; // Descripcion corta
css: string; // CSS especifico de la variante
html: string; // HTML con placeholders {{}} Handlebars
}
6.3 Ordenamiento y Visibilidad
- Orden: El schema almacena un array
sectionOrderque define el orden de renderizado. El usuario puede reordenar secciones con drag & drop en el editor visual. - Visibilidad: El objeto
sectionVisibilitydel schema controla que secciones se muestran (true) o se ocultan (false). - Configuracion por seccion:
sectionConfig[sectionType]almacena configuracion especifica, incluyendovariantIdpara override de la variante visual.
6.4 Resolucion de Variantes
Cuando se compila una invitacion, el HtmlGeneratorService resuelve las variantes para cada seccion visible:
- Si el usuario selecciono un
variantIdensectionConfig, se usa esa variante. - Si la seccion existe en el template pero el usuario cambio la variante, se reemplaza el bloque HTML/CSS.
- Si la seccion no existe en el template, se inyecta la variante seleccionada (o la primera disponible).
7. Versionamiento
7.1 Modelo InvitationVersion
Cada cambio significativo en una invitacion genera una version (snapshot). El servicio VersionManagerService gestiona el ciclo de vida de las versiones. Campos principales: id (UUID), invitationId, versionNumber (secuencial), schema (JSON), theme (JSON), changeType (enum: ai_generation | ai_modify | manual_edit | rollback), changePrompt, createdBy, createdAt, htmlUrl, cssUrl, jsUrl.
7.2 Creacion de Versiones
Las versiones se crean en los siguientes escenarios:
- Edicion manual (
manual_edit): Cuando el usuario guarda cambios desde el editor visual. - Generacion con IA (
ai_generation): Cuando se genera contenido nuevo con IA. - Modificacion con IA (
ai_modify): Cuando se modifica contenido existente con IA. - Rollback (
rollback): Cuando se restaura una version anterior.
Limite: Maximo 50 versiones por invitacion. Al exceder el limite, se elimina la version mas antigua automaticamente.
7.3 Rollback a Version Anterior
El proceso de rollback (POST /invitations/:id/versions/:versionNumber/rollback):
- Se obtiene la version objetivo por numero.
- Se actualiza el schema y theme de la invitacion con los datos de la version objetivo.
- Se crea una nueva version de tipo
rollbackque registra la restauracion. - Se retorna la invitacion actualizada y la nueva version creada.
7.4 Comparacion y Endpoints
El endpoint GET /invitations/:id/versions/compare?version1=X&version2=Y compara dos versiones aplanando claves JSON y reportando campos agregados, eliminados y modificados.
Endpoints: POST /versions (crear), GET /versions (historial), GET /versions/stats, GET /versions/:n (especifica), POST /versions/:n/rollback, GET /versions/compare.
8. Generacion HTML
8.1 Pipeline de Compilacion
El proceso de compilacion transforma el schema JSON de una invitacion en HTML/CSS/JS optimizado y listo para produccion. Los servicios involucrados son:
| Servicio | Responsabilidad |
|---|---|
HtmlGeneratorService | Orquestador principal del pipeline |
ArtisanTemplateRegistryService | Provee el HTML base del template |
TemplateCompilerService | Compila placeholders Handlebars con datos reales |
HtmlOptimizerService | Optimiza bundle y agrega meta tags SEO |
StorageService | Sube archivos a CDN (R2/MinIO) |
WebhookService | Notifica a nvito-invitations para revalidacion ISR |
8.2 Proceso de Compilacion Paso a Paso
- Obtener schema y theme de la invitacion (o de una version especifica).
- Resolver template: Obtener el HTML base del registro via
templateIddel schema. - Construir TemplateData: Combinar datos del schema con colores, fuentes y metadatos.
- Calcular secciones visibles: Aplicar
sectionOrderysectionVisibility. - Resolver variantes: Reemplazar bloques de seccion con variantes seleccionadas por el usuario.
- Compilar Handlebars: Reemplazar todos los placeholders
{{variable}}con datos reales. - Optimizar: Minificar, agregar meta tags SEO, Open Graph, etc.
- Subir a CDN: Persistir
index.html,styles.cssyscripts.jsen R2/MinIO. - Actualizar BD: Guardar
htmlUrl,cssUrl,jsUrlyhtmlGeneratedAten la invitacion. - Webhook ISR: Notificar a nvito-invitations para revalidar la pagina.
8.3 Modos de Compilacion
| Modo | Endpoint | Persistencia | Uso |
|---|---|---|---|
| Draft | POST /invitations/:id/render-draft | No (solo respuesta) | Editor visual, preview en tiempo real via iframe |
| Generate | POST /invitations/:id/generate-html | Si (CDN + BD) | Generacion independiente sin cambiar estado |
| Publish | POST /invitations/:id/publish-html | Si (CDN + BD + estado) | Generacion + publicacion en un solo paso |
8.4 Diagrama de Publicacion (Internas)
8.5 Rutas de Almacenamiento en CDN
Los archivos se almacenan con la siguiente estructura en R2/MinIO:
/{organizationId}/{eventId}/{invitationId}/v{version}/index.html
/{organizationId}/{eventId}/{invitationId}/v{version}/styles.css
/{organizationId}/{eventId}/{invitationId}/v{version}/scripts.js
8.6 Custom Domains
El WebhookService soporta dominios personalizados. Al publicar, busca CustomDomain activo y verificado del evento, construye paths de revalidacion (/i/{slug} + /d/{customDomain}), y envia webhook con header X-Revalidate-Secret.
9. Analiticas
9.1 Modelo de Eventos
El sistema de analiticas rastrea la interaccion de los invitados con las invitaciones publicadas. Los eventos se almacenan en InvitationAnalyticsEvent:
| Tipo de Evento | Descripcion |
|---|---|
VIEW | El invitado visualizo la invitacion |
RSVP_OPEN | El invitado abrio el formulario de RSVP |
RSVP_SUBMIT | El invitado envio su respuesta RSVP |
LINK_CLICK | El invitado hizo clic en un enlace (mesa de regalos, ubicacion, etc.) |
SHARE | El invitado compartio la invitacion |
DOWNLOAD | El invitado descargo contenido de la invitacion |
9.2 Datos Capturados por Evento
Cada evento registra: invitationId, eventType, visitorId (anonimo via cookie/fingerprint), ipAddress, userAgent, country (ISO 2 letras), city, device (mobile/desktop/tablet), browser, os, referrer y metadata (JSON adicional).
9.3 Resumen Agregado (InvitationAnalyticsSummary)
Para cada invitacion se mantiene un resumen agregado que se actualiza en tiempo real (fire-and-forget) despues de cada evento:
| Metrica | Descripcion |
|---|---|
totalViews | Total de visualizaciones |
uniqueVisitors | Visitantes unicos (por visitorId distinto) |
rsvpOpens | Veces que se abrio el formulario RSVP |
rsvpSubmits | Respuestas RSVP enviadas |
linkClicks | Clics en enlaces |
shares | Veces compartida |
downloads | Descargas realizadas |
conversionRate | Tasa de conversion: (rsvpSubmits / totalViews) * 100 |
deviceBreakdown | Distribucion por tipo de dispositivo (JSON) |
countryBreakdown | Distribucion por pais (JSON) |
topReferrers | Top 10 origenes de trafico (JSON) |
9.4 Flujo de Tracking
- nvito-invitations (frontend publico) detecta la interaccion del invitado.
- Envia un
POST /events/:eventId/analytics/trackcon los datos del evento (endpoint publico, sin autenticacion). - nvito-api crea el registro en
InvitationAnalyticsEvent. - Asincrona y de forma no bloqueante (fire-and-forget), actualiza el
InvitationAnalyticsSummary:- Incrementa el contador correspondiente.
- Recalcula visitantes unicos si es un evento VIEW.
- Recalcula metricas agregadas (conversion rate, desglose por dispositivo, pais y referrers).
9.5 Endpoints de Analiticas
| Endpoint | Metodo | Autenticacion |
|---|---|---|
/events/:eventId/analytics/track | POST | Publico |
/events/:eventId/analytics/overview | GET | owner/admin |
/events/:eventId/analytics/guests | GET | owner/admin |
/events/:eventId/analytics/rsvp | GET | owner/admin |
/events/:eventId/analytics/timeline | GET | owner/admin |
/events/:eventId/analytics/invitations/:id/summary | GET | owner/admin |
/events/:eventId/analytics/invitations/:id/detailed | GET | owner/admin |
9.6 Analiticas a Nivel de Evento
El AnalyticsService provee analiticas agregadas a nivel de evento combinando datos de todas las invitaciones: overview (invitados, RSVP, mesas, QR), guest analytics (por grupo y estado RSVP), RSVP timeline (ultimos 30 dias), y actividad reciente (audit logs con fallback a consulta directa).
Referencias
Archivos fuente principales: invitations.service.ts, invitations.controller.ts, external-invitation.controller.ts, external-invitation.service.ts, html-validator.service.ts, approval-workflow.service.ts, version-manager.service.ts, html-generator.service.ts, template-compiler.service.ts, artisan-template-registry.service.ts, html-optimizer.service.ts, webhook.service.ts, analytics.service.ts, section-variants/index.ts. Documentacion relacionada: CICLO_VIDA_EVENTOS_INVITACIONES.md.