Docs

Almacenamiento y Media

Tabla de Contenidos

  1. Arquitectura General
  2. Buckets de Almacenamiento
  3. URLs Pre-firmadas (Presigned URLs)
  4. Tipos de Archivo Permitidos
  5. Limites de Tamano por Plan
  6. Proveedores de Almacenamiento
  7. URLs de CDN

Arquitectura General

El sistema de almacenamiento de Nvito utiliza el protocolo S3 como capa de abstraccion, permitiendo cambiar entre proveedores (MinIO en desarrollo, Cloudflare R2 en produccion) sin modificar el codigo de la aplicacion.

Componentes Principales

ComponenteArchivoResponsabilidad
StorageServicemodules/storage/storage.service.tsOperaciones de almacenamiento (upload, download, delete, presign)
storage.config.tsconfig/storage.config.tsConfiguracion de buckets, CDN, limites y MIME types
file-validation.utilcommon/utils/file-validation.util.tsValidacion de archivos por magic bytes

S3 Client

El servicio inicializa un S3Client del AWS SDK v3 con configuracion adaptativa:

this.s3Client = new S3Client({
  region: 'auto',
  endpoint: endpoint || `https://${accountId}.r2.cloudflarestorage.com`,
  credentials: { accessKeyId, secretAccessKey },
  forcePathStyle: isMinIO,  // MinIO requiere path-style, R2 usa virtual-hosted
});

Buckets de Almacenamiento

El sistema organiza los archivos en 4 buckets separados por proposito:

BucketVariablePropositoAcceso
nvito-uploadsR2_BUCKET_UPLOADSMedia subida por usuarios (fotos, videos, audio)Publico via CDN
nvito-assetsR2_BUCKET_ASSETSAssets del sistema (logos, iconos, fondos)Publico via CDN
nvito-privateR2_BUCKET_PRIVATEArchivos privados (contratos, documentos)Solo con URL firmada
nvito-templatesR2_BUCKET_TEMPLATESHTML compilado de templates e invitacionesPublico via CDN

Estructura de Archivos en nvito-uploads

nvito-uploads/
└── org_{organizationId}/
    └── events/
        └── {eventId}/
            ├── media/
            │   ├── {timestamp}_{filename}.jpg     # Fotos
            │   ├── {timestamp}_{filename}.mp4     # Videos
            │   └── {timestamp}_{filename}.mp3     # Audio
            └── invitations/
                └── {invitationId}/
                    ├── versions/
                    │   └── v{N}/
                    │       ├── index.html         # HTML compilado
                    │       ├── styles.css          # CSS compilado
                    │       └── script.js           # JS compilado (opcional)
                    └── live/
                        ├── index.html             # Version publicada
                        ├── styles.css
                        └── script.js

Estructura de Archivos en nvito-templates

nvito-templates/
└── templates/
    └── {templateId}/
        ├── template.html              # HTML del template base
        └── assets/
            └── {timestamp}_{file}     # Imagenes preview, fondos, etc.

URLs Pre-firmadas (Presigned URLs)

Para subir archivos, el frontend no envia el archivo al API. En su lugar, solicita una URL pre-firmada y sube directamente al almacenamiento, reduciendo la carga en el servidor.

Generacion de Key

El key se construye con la siguiente estructura para evitar colisiones:

org_{organizationId}/events/{eventId}/media/{timestamp}_{sanitizedFilename}

El filename se sanitiza eliminando caracteres especiales: filename.replace(/[^a-zA-Z0-9._-]/g, '_').

Expiracion

Las URLs pre-firmadas expiran despues de 3600 segundos (1 hora) por defecto. El frontend recibe la fecha de expiracion en expiresAt.

Validacion Post-Upload

Despues de que el frontend sube el archivo, el API descarga el archivo del bucket y valida:

  1. Magic bytes: Verifica que el contenido real coincide con el MIME type declarado
  2. Ejecutables: Rechaza archivos ejecutables detectados por analisis de bytes
  3. Accion en fallo: Si la validacion falla, el archivo se elimina automaticamente del bucket

Tipos de Archivo Permitidos

MIME Types Aceptados

CategoriaFormatoMIME Type
ImagenesJPEGimage/jpeg
PNGimage/png
WebPimage/webp
GIFimage/gif
VideoMP4video/mp4
WebMvideo/webm
AudioMP3audio/mpeg, audio/mp3
WAVaudio/wav
M4Aaudio/m4a

Archivos de Invitacion (generados por el sistema)

Ademas de los archivos subidos por usuarios, el sistema genera y almacena:

TipoContent-TypeCache-Control
HTML compiladotext/htmlpublic, max-age=3600 (1 hora)
CSS compiladotext/csspublic, max-age=31536000 (1 ano)
JS compiladoapplication/javascriptpublic, max-age=31536000 (1 ano)

Limites de Tamano por Plan

Los limites de tamano de archivo varian segun el plan contratado por la organizacion:

PlanLimiteBytes
Free5 MB5,242,880
Essential25 MB26,214,400
Plus50 MB52,428,800
VIP100 MB104,857,600

Variables de Entorno para Limites

MAX_FILE_SIZE_FREE=5242880          # 5 MB
MAX_FILE_SIZE_ESSENTIAL=26214400    # 25 MB
MAX_FILE_SIZE_PLUS=52428800         # 50 MB
MAX_FILE_SIZE_VIP=104857600         # 100 MB

Estos valores son configurables por variable de entorno, permitiendo ajustarlos sin recompilar.

Proveedores de Almacenamiento

MinIO (Desarrollo Local)

MinIO es un servidor de almacenamiento de objetos compatible con S3, ideal para desarrollo local:

ParametroValor
Endpointhttp://localhost:9000
Consola Webhttp://localhost:9001
Regionauto
Path Styletrue (forcePathStyle)
Access KeyConfigurado en .env
Secret KeyConfigurado en .env

Deteccion automatica: Si la variable R2_ENDPOINT esta configurada (apuntando a MinIO), el servicio activa forcePathStyle: true automaticamente, ya que MinIO requiere path-style en lugar de virtual-hosted.

Cloudflare R2 (Produccion)

Cloudflare R2 es el proveedor de almacenamiento para ambientes remotos:

ParametroValor
Endpointhttps://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com
Regionauto
Path Stylefalse (virtual-hosted)
Account IDVariable R2_ACCOUNT_ID
Access KeyVariable R2_ACCESS_KEY_ID
Secret KeyVariable R2_SECRET_ACCESS_KEY

Tabla Comparativa

CaracteristicaMinIO (Local)Cloudflare R2 (Prod)
Endpointhttp://localhost:9000https://{id}.r2.cloudflarestorage.com
Path StyleSi (forcePathStyle: true)No (virtual-hosted)
URL publicahttp://localhost:9000/{bucket}/{key}https://cdn.nvito.mx/{key}
CostoGratisPay-per-use (egress gratis)
DisponibilidadSolo localGlobal
ConsolaMinIO Console (:9001)Cloudflare Dashboard

URLs de CDN

Construccion de URLs Publicas

El servicio construye URLs publicas de forma diferente segun el proveedor:

MinIO (local):

http://localhost:9000/{bucket}/{key}
Ejemplo: http://localhost:9000/nvito-uploads/org_abc123/events/evt1/media/1707123456_foto.jpg

Cloudflare R2 (produccion):

https://cdn.nvito.mx/{key}
Ejemplo: https://cdn.nvito.mx/org_abc123/events/evt1/media/1707123456_foto.jpg

Diferencia Clave

En MinIO, el nombre del bucket se incluye en la URL publica (/{bucket}/{key}). En R2, el bucket no aparece en la URL porque se configura a nivel de dominio personalizado (/{key}).

Cache-Control

Los archivos subidos por usuarios se sirven con cache agresivo:

Cache-Control: public, max-age=31536000    # 1 ano (media de usuario)
Cache-Control: public, max-age=3600        # 1 hora (HTML de invitacion)

URLs Privadas

Para archivos en el bucket nvito-private, se generan URLs firmadas de lectura con expiracion configurable:

async getPrivateUrl(key: string, expiresIn: number = 3600): Promise<string>

Estas URLs son temporales y permiten descargar el archivo sin exponer credenciales.

Operaciones del StorageService

Resumen de Metodos

MetodoDescripcion
uploadPublic()Subir archivo con buffer a bucket publico
getUploadUrl()Generar URL pre-firmada para upload directo
exists()Verificar si un archivo existe
getObject()Descargar un archivo como Buffer
delete()Eliminar un archivo
deleteEventFiles()Eliminar todos los archivos de un evento
getPrivateUrl()Generar URL firmada para contenido privado
validateUploadedFileContent()Validar archivo subido por magic bytes
uploadTemplateHtml()Subir HTML de template al bucket de templates
uploadTemplateAsset()Subir asset de template (preview, fondo)
deleteTemplateFiles()Eliminar archivos de un template
uploadInvitationHtml()Subir HTML compilado de invitacion (versionado)
uploadInvitationCss()Subir CSS compilado de invitacion
uploadInvitationJs()Subir JS compilado de invitacion
publishInvitationVersion()Copiar version a directorio live/ (publicar)

Variables de Entorno

VariableDescripcionDefault
R2_ACCOUNT_IDAccount ID de Cloudflare R2-
R2_ACCESS_KEY_IDAccess Key del proveedor S3(requerido)
R2_SECRET_ACCESS_KEYSecret Key del proveedor S3(requerido)
R2_ENDPOINTEndpoint custom (MinIO: http://localhost:9000)- (usa R2)
R2_BUCKET_UPLOADSNombre del bucket de uploadsnvito-uploads
R2_BUCKET_ASSETSNombre del bucket de assetsnvito-assets
R2_BUCKET_PRIVATENombre del bucket privadonvito-private
R2_BUCKET_TEMPLATESNombre del bucket de templatesnvito-templates
CDN_URLURL base del CDNhttps://cdn.nvito.mx
CDN_ALLOWED_ORIGINSOrigenes permitidos (separados por coma)-
MAX_FILE_SIZE_FREELimite de archivo para plan Free5242880
MAX_FILE_SIZE_ESSENTIALLimite para plan Essential26214400
MAX_FILE_SIZE_PLUSLimite para plan Plus52428800
MAX_FILE_SIZE_VIPLimite para plan VIP104857600

Referencias

  • StorageService: src/modules/storage/storage.service.ts
  • Storage Config: src/config/storage.config.ts
  • File Validation: src/common/utils/file-validation.util.ts
Esta pagina fue util?