Saltar al contenido principal

Almacenamiento

Configura el almacenamiento de objetos que Fluxo necesita para las subidas, la lectura pública de recursos y la entrega opcional por CDN.

Empieza por la visión general de autoalojamiento si prefieres ver antes la foto completa y vuelve luego aquí a configurar el almacenamiento. Cuando las subidas funcionen, termina de montar tu infraestructura con la configuración del correo.

Fluxo necesita almacenamiento de objetos para las subidas de los usuarios, los recursos de marca y los adjuntos de las conversaciones. En despliegues autoalojados, el camino recomendado es Amazon S3, porque el repositorio ya incluye un módulo de Terraform que encaja con la forma en que la aplicación genera hoy las URLs de subida.

Por qué Fluxo necesita almacenamiento

La aplicación está construida en torno a subidas directas desde el navegador, en lugar de hacer pasar archivos grandes por el servidor de la API.

Eso te da algunas ventajas prácticas:

  • las subidas van directamente del navegador al almacenamiento de objetos mediante URLs prefirmadas
  • la API solo firma la petición de subida, en lugar de almacenar en memoria el contenido del archivo
  • los recursos subidos se pueden volver a leer mediante URLs públicas estables
  • puedes añadir una CDN más adelante sin cambiar el flujo de subida a nivel de aplicación

Cómo usa Fluxo el almacenamiento por dentro

En ejecución, el flujo de subida es así:

  1. Un cliente pide a la API una URL de subida firmada.
  2. La API crea un PUT prefirmado para el bucket configurado.
  3. El cliente sube el archivo directamente al almacenamiento de objetos.
  4. La API devuelve una URL pública que la aplicación guarda y muestra después.

Las subidas no se escriben en un espacio de nombres plano del bucket. Las claves se acotan por inquilino y por área funcional, de modo que los datos quedan organizados por organización, sitio web y entidad. Según la funcionalidad, esa entidad puede ser una conversación, un visitante, un usuario o un contacto.

Ejemplos de la forma de las claves:

<organizationId>/<websiteId>/<conversationId>/attachment.png
<organizationId>/<websiteId>/<userId>/avatar.png
cdn/<organizationId>/<websiteId>/<visitorId>/image.jpg

Los sufijos exactos varían, pero lo importante es que el almacenamiento tiene en cuenta el inquilino y la funcionalidad.

Camino de instalación recomendado

El módulo de almacenamiento orientado a AWS está en infra/aws/s3-public-setup.

Crea:

  • un bucket de S3 para los archivos subidos
  • acceso público de lectura a las URLs exactas de los objetos
  • reglas de CORS para las subidas directas desde el navegador
  • un usuario de IAM que la API pueda usar para generar URLs de subida prefirmadas

El módulo que se incluye es un punto de partida permisivo, no una base de almacenamiento endurecida para producción. Activa force_destroy, desactiva los bloqueos de acceso público de S3, permite lecturas públicas, admite CORS desde * y crea claves de acceso de IAM de larga duración. Trata cada URL de objeto como pública y revisa cada ajuste antes de usar el módulo fuera de un entorno de desarrollo aislado.

Para producción, restringe el CORS a tus orígenes reales, prefiere objetos privados con entrega firmada o por CDN siempre que puedas, activa el cifrado, el versionado, la retención y los registros de acceso, sustituye las claves estáticas por roles de carga de trabajo cuando tu plataforma lo permita, rota las claves que queden y prueba la copia de seguridad y la recuperación.

Paso 1: despliega el módulo de almacenamiento

Entra en el módulo de Terraform:

cd infra/aws/s3-public-setup

Copia el archivo de variables de ejemplo:

cp terraform.tfvars.example terraform.dev.tfvars

Define un nombre de bucket único a nivel global y la región que quieras usar:

terraform.dev.tfvars
bucket_name = "fluxo-dev-your-unique-suffix"
environment = "dev"
aws_region  = "us-east-1"

Inicializa Terraform:

terraform init

Usa estado remoto cifrado y con bloqueo en entornos compartidos o de producción. El estado contiene detalles sensibles de los recursos y el módulo devuelve un secreto de IAM. No subas al repositorio el estado, los planes, los tfvars ni ninguna salida que contenga credenciales.

Crea o selecciona un workspace, revisa un plan guardado y aplica exactamente ese plan:

terraform workspace new dev
terraform workspace select dev
terraform plan -var-file="terraform.dev.tfvars" -out="fluxo-storage.tfplan"
terraform apply "fluxo-storage.tfplan"

Repite el mismo patrón para producción con un nombre de bucket y un workspace de producción.

Paso 2: reúne los valores que la aplicación necesita

Después de terraform apply, anota:

  • el nombre del bucket
  • la región de AWS
  • el ID de la clave de acceso de IAM
  • la clave de acceso secreta de IAM

Esos valores son los que usa la API cuando genera URLs de subida prefirmadas.

Paso 3: configura el entorno de ejecución

Para el camino estándar de AWS, define estas variables de entorno en el runtime de la API:

.env
S3_BUCKET_NAME=fluxo-dev-your-unique-suffix
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=AKIA...
S3_SECRET_ACCESS_KEY=...
S3_PUBLIC_BASE_URL=https://fluxo-dev-your-unique-suffix.s3.us-east-1.amazonaws.com
S3_CDN_BASE_URL=
S3_SIGNED_URL_EXPIRATION_SECONDS=900

Qué hace cada una:

  • S3_BUCKET_NAME: el bucket que recibe las subidas
  • S3_REGION: la región de AWS de ese bucket
  • S3_ACCESS_KEY_ID y S3_SECRET_ACCESS_KEY: las credenciales que la API usa para firmar las subidas
  • S3_PUBLIC_BASE_URL: la URL base de lectura cuando sirves los archivos directamente desde S3
  • S3_CDN_BASE_URL: una URL base de CDN opcional si más adelante pones CloudFront u otra CDN delante de las lecturas
  • S3_SIGNED_URL_EXPIRATION_SECONDS: la vida útil de las URLs de subida generadas; por defecto, 900 segundos

Si todavía no usas una CDN, deja S3_CDN_BASE_URL vacío.

Paso 4: reinicia la API y comprueba las subidas

Reinicia la API para que tome la nueva configuración de almacenamiento y verifica el flujo completo:

  1. Genera una URL de subida prefirmada desde la aplicación.
  2. Sube un archivo desde el navegador.
  3. Abre directamente la URL pública devuelta.
  4. Confirma que el recurso se muestra bien donde se subió.

Una buena ronda de verificación incluye:

  • las subidas de avatar de perfil
  • las subidas de marca del sitio web
  • las subidas de adjuntos en las conversaciones

Nota avanzada: almacenamiento compatible con S3

El camino documentado prioriza AWS, pero el runtime también admite proveedores compatibles con S3.

Si usas a propósito otro servicio compatible con S3, los ajustes adicionales son:

.env
S3_ENDPOINT=https://your-object-store.example.com
S3_FORCE_PATH_STYLE=true

Trátalo como una opción avanzada. El módulo de Terraform de este repositorio aprovisiona S3 de AWS, no almacenamiento de objetos de terceros.

Lista de comprobación

Antes de dar el almacenamiento por terminado, confirma todo lo siguiente:

  • que se puede generar correctamente una URL de subida prefirmada
  • que una subida desde el navegador termina sin errores de CORS
  • que la URL pública devuelta resuelve correctamente
  • que los archivos subidos aparecen en el panel donde esperas
  • que el S3_PUBLIC_BASE_URL o el S3_CDN_BASE_URL configurados coinciden con las URLs que guarda la aplicación
  • que se han revisado explícitamente el CORS de producción, el acceso público, la retención, el cifrado, las credenciales y el comportamiento al destruir la infraestructura

Antes de cualquier terraform destroy, recuerda que el ajuste force_destroy del módulo de inicio permite borrar un bucket que no está vacío. Haz copia de seguridad de los objetos que necesites e inspecciona el plan de destrucción; un workspace de Terraform no es por sí solo ni una copia de seguridad ni una frontera de aislamiento.

Problemas habituales

  • 403 durante la subida: las credenciales de firma o los permisos del bucket están mal
  • fallo de CORS en el navegador: las reglas de CORS del bucket no se aplicaron o son demasiado restrictivas
  • la subida funciona pero la URL pública falla: S3_PUBLIC_BASE_URL está mal o falta
  • las URLs funcionan directamente pero no dentro de la aplicación: comprueba que el publicUrl guardado coincide con la URL base configurada

¿Te resultó útil esta página?

Abre una incidencia de documentación ya rellenada para que el equipo pueda actuar.