Saltar a contenido

RF-006 — Almacenamiento y entrega de medios en Cloudflare Images

Estado Implementado — única pieza de lógica, no de configuración
Tipo Colección reescrita + cliente HTTP + hooks de ciclo de vida
Ubicación src/collections/Media.ts · src/collections/Media/hooks.ts · src/utilities/cloudflare-images.ts
Depende de RF-000
Ver también ADR-0002 · Guía de configuración

Requisito

Almacenar y entregar las imágenes del sitio fuera del servidor del CMS, en un servicio con red de distribución propia y generación de variantes por tamaño; mantener el flujo de carga del editor sin cambios respecto del panel estándar; y evitar que el volumen de medios condicione el almacenamiento, el respaldo o el reemplazo del contenedor del CMS.

Solución adoptada

Reescribir la colección media con disableLocalStorage y delegar el ciclo de vida a tres hooks sobre un cliente propio de la API de Cloudflare Images. En base de datos se persiste únicamente el identificador remoto y la metadata del archivo; las URL son campos virtuales que se calculan en cada lectura.

flowchart LR
    E([Editor]) -->|carga| BC[hook beforeChange<br/>uploadToCloudflare]
    BC -->|API v1| CF[Cloudflare Images]
    BC -.persiste cfImageId<br/>+ nombre, tamaño, tipo.-> DB[(PostgreSQL)]
    SF([Storefront]) -->|REST · GraphQL| AR[hook afterRead<br/>populateCloudflareUrls]
    AR -->|sintetiza URLs| SF
    SF -->|GET variante| CDN[imagedelivery.net]
    E -->|elimina| BD[hook beforeDelete<br/>deleteFromCloudflare]
    BD --> CF

Ciclo de vida

Momento Hook Comportamiento
Alta beforeChange · uploadToCloudflare Cargar el binario a Cloudflare y devolver cfImageId, nombre, tamaño y tipo MIME, requeridos para que el panel reconozca la carga
Lectura afterRead · populateCloudflareUrls Sintetizar url, thumbnailURL, baseDeliveryUrl y el grupo sizes a partir del identificador
Baja beforeDelete · deleteFromCloudflare Eliminar la imagen remota y proceder con el borrado local aunque la eliminación remota falle

Variantes publicadas

Siete variantes, declaradas tanto en la colección como en el cliente, que deben existir con el mismo nombre en el panel de Cloudflare:

Variante Destino
thumbnail — 300 px Miniaturas del panel
square — 500 × 500 Piezas cuadradas
small — 600 px Móvil
medium — 900 px Tableta
large — 1400 px Escritorio; es la URL por defecto
xlarge — 1920 px Alta resolución
og — 1200 × 630 Vista previa en redes sociales

Reglas de negocio

  • No persistir ninguna URL: quien consulte la tabla media por SQL encuentra solo el cfImageId. Todo acceso a medios pasa por la API.
  • Restringir la carga a imágenes: PNG, JPEG, GIF, WebP y SVG.
  • Habilitar punto focal y miniatura de administración sobre la imagen remota.
  • Degradar en forma controlada cuando el servicio no está configurado: la carga se saltea con aviso y el CMS sigue operativo.
  • Absorber el error de borrado remoto para que el borrado local siempre proceda.
  • Exponer la colección con lectura pública y reservar la escritura al panel.

Configuración

Variable Uso
CLOUDFLARE_ACCOUNT_ID Cuenta destino de la carga
CLOUDFLARE_IMAGES_TOKEN Token de la API de Cloudflare Images
CLOUDFLARE_ACCOUNT_HASH Componente del dominio de entrega imagedelivery.net

Criterios de aceptación

# Criterio
1 Cargar una imagen desde el panel y verla exhibida sin que quede archivo en el contenedor
2 Recuperar el documento por API y obtener las siete variantes con URL absolutas
3 Eliminar la imagen en el CMS y verificar su baja en Cloudflare
4 Completar la baja local aunque la eliminación remota falle
5 Mantener el CMS operativo, con aviso, si las credenciales no están configuradas

Limitaciones conocidas

  • Dejar imágenes huérfanas en Cloudflare en silencio cuando la baja remota falla.
  • Depender de que las variantes existan con idéntico nombre en el panel de Cloudflare: un nombre faltante devuelve 404 en la entrega, sin error en el CMS.
  • Cargar únicamente en la creación: la sustitución del binario de un documento existente no está contemplada en el hook.