Files
image-platform/docs/next-image-provider.md
S.Gromov 1c0e8277a3 feat: добавить генерацию image variants
- добавлен shared config presets, custom transforms и allowlist hosts
- реализованы Backend endpoints для assets, presets и variants
- добавлена orchestration через PostgreSQL, RabbitMQ, S3 и worker
- обновлён Gateway read-through flow с L1 cache и корректным Vary: Accept
- добавлена миграция resize_mode для variants lookup
- обновлены dev scripts, env template, lockfile и документация
2026-05-05 13:25:28 +03:00

106 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 <Image src="asset_123/v4/card" width={640} height={420} alt="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.