# Next/image Provider `image-platform` должен работать как custom image provider для `next/image`. ## Next.js contract Next.js custom loader получает только: - `src`; - `width`; - `quality`. Поэтому public image URL должен принимать эти параметры напрямую и сам запускать read-through генерацию при miss. ## Loader config В Next.js приложении используется `loaderFile`: ```js // next.config.js module.exports = { images: { loader: "custom", loaderFile: "./src/image-platform-loader.js", qualities: [75, 80], }, } ``` Пример loader: ```js "use client" const imageBaseUrl = process.env.NEXT_PUBLIC_IMAGE_PLATFORM_URL export default function imagePlatformLoader({ src, width, quality }) { const normalizedSrc = src.startsWith("/") ? src.slice(1) : src const q = quality || 80 return `${imageBaseUrl}/images/${normalizedSrc}?w=${width}&q=${q}&f=auto` } ``` Пример использования: ```tsx import Image from "next/image" export function ProductCard() { return Product } ``` ## Public URL ```text GET /images/{assetId}/v{version}/{preset}?w={width}&q={quality}&f=auto ``` Route реализован в Fastify Gateway. Для `card` ширина должна входить в static preset allowlist: `320`, `640`, `960`. Пример: ```text https://img.example.com/images/asset_123/v4/card?w=640&q=80&f=auto ``` `src` не должен быть source URL. Это должен быть versioned platform identifier, например `asset_123/v4/card`. Source URL хранится в PostgreSQL и не раскрывается в public image URL. `v{version}` меняется при обновлении source image. Старые URL можно кэшировать как immutable без purge. ## Format auto `f=auto` выбирает output format по `Accept` header: 1. `image/avif`, если клиент поддерживает AVIF и preset разрешает AVIF. 2. `image/webp`, если клиент поддерживает WebP и preset разрешает WebP. 3. `image/jpeg` или original fallback. Для auto format обязательны headers: ```http Vary: Accept Cache-Control: public, max-age=31536000, immutable Content-Type: image/avif | image/webp | image/jpeg ``` CDN и Gateway L1 cache должны учитывать `Vary: Accept`, иначе можно отдать AVIF клиенту без AVIF support. ## Read-through behavior ```text client -> CDN -> Fastify gateway -> L1 memory -> Backend -> RabbitMQ -> Worker -> imgproxy -> S3 ``` Поведение: - CDN HIT: backend не вызывается. - Gateway L1 HIT: backend не вызывается. - Gateway L1 MISS: Gateway вызывает Backend internal ensure endpoint. - S3 HIT: Backend отдаёт bytes Gateway, Gateway кладёт result в L1. - S3 MISS: Backend ставит RabbitMQ job, Worker генерирует variant через external imgproxy, сохраняет в S3, обновляет PostgreSQL, Backend возвращает bytes Gateway. Так достигается Cloudinary-like поведение: первый запрос создаёт derived asset, следующие запросы отдаются из cache/storage.