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.