Files
image-platform/docs/next-image-provider.md
2026-05-12 07:54:32 +03:00

126 lines
4.4 KiB
Markdown
Raw 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.
## Remote source loader config
Remote source mode нужен для сценария, где consumer project уже имеет изображение в `public` или внешний image URL и хочет получить `srcset` без предварительной регистрации asset.
В 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"
import { createImagePlatformNextLoader } from "@image-platform/client"
export default createImagePlatformNextLoader({
baseUrl: process.env.NEXT_PUBLIC_IMAGE_PLATFORM_URL,
preset: "card",
project: process.env.NEXT_PUBLIC_IMAGE_PLATFORM_PROJECT,
sourceBaseUrl: process.env.NEXT_PUBLIC_SITE_ORIGIN,
})
```
Пример использования:
```tsx
import Image from "next/image"
export function ProductCard() {
return <Image src="/images/product.jpg" width={640} height={420} alt="Product" />
}
```
Если `src` относительный, `sourceBaseUrl` превращает его в абсолютный source URL, например `https://site.example.com/images/product.jpg`. Если `src` уже абсолютный, он передаётся как есть.
## Public URL
Remote source URL:
```text
GET /p/{project}/remote/{preset}?src={sourceUrl}&w={width}&q={quality}&f=auto
```
Пример:
```text
https://img.example.com/p/acme/remote/card?src=https%3A%2F%2Fsite.example.com%2Fimages%2Fproduct.jpg&w=640&q=80&f=auto
```
Managed asset 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
```
Для managed asset `src` не должен быть source URL. Это должен быть versioned platform identifier, например `asset_123/v4/card`. Source URL хранится в PostgreSQL и не раскрывается в public image URL.
`v{version}` меняется при обновлении source image. Старые URL можно кэшировать как immutable без purge.
Для remote source `src` является исходным URL. Этот режим не immutable по умолчанию: Gateway отдаёт `GATEWAY_REMOTE_CACHE_CONTROL`, потому что источник может быть mutable.
## 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.