1. Maquina de Estados
Cada invitación en Nvito sigue un ciclo de vida estricto controlado por una maquina de estados. El servicio ApprovalWorkflowService define las transiciones validas y garantiza que ningún cambio de estado ocurra fuera de las reglas establecidas.
1.1 Diagrama de Estados
Maquina de Estados — Invitacion
in_construction→unpublishedSubmit for Approvalunpublished→publishedApproveunpublished→in_constructionReject / Request Changespublished→unpublishedUnpublishpublished→closedClose / Evento Cancelado / Completadoclosed→publishedReopen (evento ACTIVE, fecha vigente)in_construction→in_constructionEditar / Guardar1.2 Transiciones Detalladas
| Transición | 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 está 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 descripción de cambios | POST /invitations/:id/reject (action: request_changes) |
| Unpublish | PUBLISHED | UNPUBLISHED | Usuario (owner/admin) | Webhook de despublicación 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 Sincronización con Eventos
La maquina de estados de invitaciones está sincronizada con el ciclo de vida de eventos:
- Invitación publicada (
PUBLISHED): Si el evento está enDRAFT, automáticamente transiciona aACTIVEviaEventLifecycleService.activateEventOnPublish(). - Evento cancelado (
CANCELLED): Todas las invitaciones enUNPUBLISHEDoPUBLISHEDse cierran automáticamente (CLOSED). - Evento completado (
COMPLETED): Cron job diario (medianoche, timezoneAmerica/Mexico_City) cierra invitaciones de eventos cuya fecha ya paso.
2. Tipos de Invitación (InvitationSource)
Nvito soporta dos origenes de invitación, discriminados por el campo source del modelo Invitation:
| Source | Descripción | Creación | Edicion | Versiónamiento |
|---|---|---|---|---|
INTERNAL (default) | Invitación 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 versión 1) |
Las invitaciones externas comparten la misma maquina de estados, el mismo sistema de publicación/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 invitación interna, desde su creación hasta que un invitado la visualiza:
Paso 1: Creación de la Invitación
El usuario crea una invitación asociada a un evento existente. Puede elegir entre:
- Seleccionar un template: Se obtiene del catálogo 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: Máximo 3 invitaciones por evento. Se genera un slug único con formato {eventSlug}-inv-{randomSuffix}.
Endpoint: POST /invitations - Requiere permiso invitations:create:assigned.
Paso 2: Generación con IA (Opcional)
El usuario puede utilizar la generación con IA para crear o modificar el contenido de la invitación a partir de lenguaje natural. El servicio AiGenerationService genera el schema y theme correspondientes, creando una nueva versión en el proceso.
Paso 3: Edicion con Editor Visual Artisan
El usuario edita la invitación en el editor visual de nvito-admin:
- Modifica secciones, textos, imagenes, colores y fuentes.
- Reordena secciones con drag & drop.
- Selecciona variantes visuales para cada sección.
- 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 sección puede configurarse con una variante visual diferente. El usuario selecciona variantes desde el catálogo de section-variants/ y la configuración 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 invitación compilada sin necesidad de publicarla, accesible via GET /invitations/preview/:token.
Paso 6: Publicación
Al aprobar la invitación (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 revalidación a nvito-invitations para ISR.
- Si el evento está en DRAFT, se activa automáticamente.
Paso 7: Visualización por Invitados
El invitado recibe un enlace con formato https://nvito.mx/i/{slug} o, si se habilita la personalización, https://nvito.mx/i/{slug}?t={shortCode}. La aplicación 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.
- Si la URL incluye
?t={shortCode}, resuelve los datos del invitado viaGET /invitations/public/guest/{shortCode}y personaliza el loader (pantalla de carga) y la sección RSVP con el nombre del invitado.
La personalización es 100% client-side: el HTML estático es identico para todos los invitados, y la resolución del nombre ocurre en JavaScript al cargar la página. Si el shortCode no existe o es invalido, se muestran textos de fallback generico configurables. Ver detalles en Motor de Invitaciones — Personalización por Invitado.
Paso 8: RSVP del Invitado
El invitado interactua con la sección RSVP integrada en el micrositio. Si la invitación fue abierta con un shortCode valido, la sección RSVP muestra un saludo personalizado con el nombre y título del invitado (ej: "Ing. Juan, confirma tu asistencia"). 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 diseños HTML propios (ej. diseñadores, agencias) publicar sus invitaciones a traves de la plataforma Nvito, aprovechando el hosting, las analiticas, el sistema RSVP y la distribución via comunicaciones.
4.1 Diagrama de Secuencia
Flujo de Invitaciones Externas (EXTERNAL)
4.2 Creación
El usuario sube un archivo HTML (.html / .htm) desde nvito-admin. El archivo se envia como string al endpoint POST /invitations/external.
Proceso de creación:
- Se verifica que el evento exista y pertenezca a la organización del usuario.
- Se verifica el limite de 3 invitaciones por evento.
- Se valida el HTML con
HtmlValidatorService(ver sección 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 invitación 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 |
| Generación/modificación con IA | Si | No |
| Secciones editables | Si | No |
| Versiónamiento (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 |
| Publicación | Si (genera HTML) | Si (usa HTML existente) |
| Analiticas | Si | Si |
| RSVP | Si | Si (si el HTML incluye formulario RSVP) |
| Comunicaciones | Si | Si |
4.4 Validación de HTML (HtmlValidatorService)
El servicio HtmlValidatorService ejecuta un pipeline de 9 pasos de validación que retorna { valid: boolean, errors: string[], warnings: string[] }:
| Paso | Tipo | Descripción |
|---|---|---|
| 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 creación y retornan 400. Los warnings no bloquean pero se informan al usuario.
4.5 Re-upload
El usuario puede reemplazar el HTML de una invitación 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 está publicada, debe despublicarse primero) - El nuevo HTML pasa por la misma validación 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 Publicación de Externas
La publicación 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 creación/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 está en DRAFT (igual que internas).
- Se envia webhook ISR a nvito-invitations para revalidar la página.
4.8 Endpoints de Invitaciones Externas
| Metodo | Endpoint | Descripción | Permiso |
|---|---|---|---|
POST | /invitations/external | Subir HTML y crear invitación externa | invitations:create:assigned |
PUT | /invitations/:id/external/reupload | Reemplazar HTML de invitación externa | invitations:update |
GET | /invitations/:id/external/download | Descargar HTML original | Autenticado |
5. Templates
5.1 Catálogo de Templates
Los templates son plantillas prediseadas almacenadas en la base de datos (tabla templates) con estado ACTIVE. Al iniciar el módulo, ArtisanTemplateRegistryService carga todos los templates activos en un cache en memoria.
Cada template está asociado a un tipo de evento y provee una configuración completa:
| Campo | Descripción |
|---|---|
slug | Identificador único (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 catálogo |
tags | Etiquetas para filtrado y búsqueda |
5.2 Templates por Defecto por Tipo de Evento
| Tipo de Evento | Template por Defecto |
|---|---|
| Wedding | wedding-classic |
| XV Años | 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 públicos: GET /invitations/templates (catálogo filtrable por eventType), GET /invitations/templates/:templateId (metadata), GET /invitations/variants (variantes por tipo).
6. Secciones
6.1 Tipos de Sección Disponibles
El sistema define 18 tipos de sección 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 Sección
interface SectionVariant {
id: string; // e.g., "hero-centered-overlay"
sectionType: string; // e.g., "hero"
name: string; // Nombre legible en español
description: string; // Descripción corta
css: string; // CSS específico 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). - Configuración por sección:
sectionConfig[sectionType]almacena configuración específica, incluyendovariantIdpara override de la variante visual.
6.4 Resolución de Variantes
Cuando se compila una invitación, el HtmlGeneratorService resuelve las variantes para cada sección visible:
- Si el usuario selecciono un
variantIdensectionConfig, se usa esa variante. - Si la sección existe en el template pero el usuario cambio la variante, se reemplaza el bloque HTML/CSS.
- Si la sección no existe en el template, se inyecta la variante seleccionada (o la primera disponible).
7. Versiónamiento
7.1 Modelo InvitationVersión
Cada cambio significativo en una invitación genera una versión (snapshot). El servicio VersiónManagerService gestióna el ciclo de vida de las versiones. Campos principales: id (UUID), invitationId, versiónNumber (secuencial), schema (JSON), theme (JSON), changeType (enum: ai_generation | ai_modify | manual_edit | rollback), changePrompt, createdBy, createdAt, htmlUrl, cssUrl, jsUrl.
7.2 Creación de Versiónes
Las versiones se crean en los siguientes escenarios:
- Edicion manual (
manual_edit): Cuando el usuario guarda cambios desde el editor visual. - Generación con IA (
ai_generation): Cuando se genera contenido nuevo con IA. - Modificación con IA (
ai_modify): Cuando se modifica contenido existente con IA. - Rollback (
rollback): Cuando se restaura una versión anterior.
Limite: Máximo 50 versiones por invitación. Al exceder el limite, se elimina la versión más antigua automáticamente.
7.3 Rollback a Versión Anterior
El proceso de rollback (POST /invitations/:id/versións/:versiónNumber/rollback):
- Se obtiene la versión objetivo por número.
- Se actualiza el schema y theme de la invitación con los datos de la versión objetivo.
- Se crea una nueva versión de tipo
rollbackque registra la restauración. - Se retorna la invitación actualizada y la nueva versión creada.
7.4 Comparacion y Endpoints
El endpoint GET /invitations/:id/versións/compare?versión1=X&versión2=Y compara dos versiones aplanando claves JSON y reportando campos agregados, eliminados y modificados.
Endpoints: POST /versións (crear), GET /versións (historial), GET /versións/stats, GET /versións/:n (específica), POST /versións/:n/rollback, GET /versións/compare.
8. Generación HTML
8.1 Pipeline de Compilacion
El proceso de compilacion transforma el schema JSON de una invitación en HTML/CSS/JS optimizado y listo para producción. 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 revalidación ISR |
8.2 Proceso de Compilacion Paso a Paso
- Obtener schema y theme de la invitación (o de una versión específica).
- 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 sección 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 invitación. - Webhook ISR: Notificar a nvito-invitations para revalidar la página.
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) | Generación independiente sin cambiar estado |
| Publish | POST /invitations/:id/publish-html | Si (CDN + BD + estado) | Generación + publicación en un solo paso |
8.4 Diagrama de Publicación (Internas)
Pipeline de Publicacion — Invitaciones Internas
8.5 Rutas de Almacenamiento en CDN
Los archivos se almacenan con la siguiente estructura en R2/MinIO:
/{organizationId}/{eventId}/{invitationId}/v{versión}/index.html
/{organizationId}/{eventId}/{invitationId}/v{versión}/styles.css
/{organizationId}/{eventId}/{invitationId}/v{versión}/scripts.js
8.6 Custom Domains
El WebhookService soporta dominios personalizados. Al publicar, busca CustomDomain activo y verificado del evento, construye paths de revalidación (/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 interacción de los invitados con las invitaciones publicadas. Los eventos se almacenan en InvitationAnalyticsEvent:
| Tipo de Evento | Descripción |
|---|---|
VIEW | El invitado visualizo la invitación |
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, ubicación, etc.) |
SHARE | El invitado compartio la invitación |
DOWNLOAD | El invitado descargo contenido de la invitación |
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 invitación se mantiene un resumen agregado que se actualiza en tiempo real (fire-and-forget) después de cada evento:
| Metrica | Descripción |
|---|---|
totalViews | Total de visualizaciones |
uniqueVisitors | Visitantes únicos (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 |
conversiónRate | Tasa de conversión: (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 público) detecta la interacción del invitado.
- Envia un
POST /events/:eventId/analytics/trackcon los datos del evento (endpoint público, sin autenticación). - 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 únicos si es un evento VIEW.
- Recalcula metricas agregadas (conversión rate, desglose por dispositivo, pais y referrers).
9.5 Endpoints de Analiticas
| Endpoint | Metodo | Autenticación |
|---|---|---|
/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, versión-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. Documentación relacionada: CICLO_VIDA_EVENTOS_INVITACIONES.md.