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 и документация
This commit is contained in:
2026-05-05 13:25:28 +03:00
parent bcadb85a83
commit 1c0e8277a3
59 changed files with 3526 additions and 143 deletions

View File

@@ -68,6 +68,7 @@ requested_format requested_format not null default auto
format variant_format not null
width integer not null
height integer nullable
resize_mode resize_mode not null default fit
quality integer not null
s3_key text not null unique
content_type text nullable
@@ -89,6 +90,7 @@ updated_at timestamptz not null default now()
asset_status: active | disabled | deleted
requested_format: auto | avif | webp | jpg | png
variant_format: avif | webp | jpg | png
resize_mode: fit | fill
variant_status: pending | processing | ready | failed
```
@@ -98,7 +100,7 @@ variant_status: pending | processing | ready | failed
allowed_image_hosts(hostname)
image_assets(public_id)
image_asset_versions(asset_id, version)
image_variants(asset_id, asset_version, preset, width, quality, format)
image_variants(asset_id, asset_version, preset, width, height, resize_mode, quality, format)
image_variants(s3_key)
image_variants(variant_hash)
```
@@ -123,6 +125,8 @@ variants/{assetId}/v{version}/{variantHash}.{format}
- `assetVersion`;
- `preset`;
- normalized width;
- normalized height, где `0` означает auto height;
- normalized resize mode;
- normalized quality;
- фактический output format;
- параметры transform, влияющие на bytes.
@@ -135,34 +139,45 @@ variants/asset_123/v4/card_w640_q80_webp.webp
variants/asset_123/v4/card_w640_q80_jpg.jpg
```
Public URL также versioned:
Public URL также versioned. Для fixed preset `w` и `q` можно не передавать, для responsive preset `w` обязателен:
```text
/images/{assetId}/v{version}/{preset}?w={width}&q={quality}&f=auto
/images/{assetId}/v{version}/avatar?f=auto
```
## Presets
Клиент не должен передавать произвольные трансформации. Сначала нужны ограниченные presets.
Клиент не должен бесконтрольно создавать произвольные трансформации. Сейчас есть статический config в `packages/image-config`.
Режимы:
- `fixed` - preset задаёт один размер, например `avatar`.
- `responsive` - preset задаёт allowlist ширин, например `card` и `hero`.
- `custom` - произвольный single image, только если включён `IMAGE_ALLOW_CUSTOM_TRANSFORMS=true`.
Пример:
```text
avatar:
widths: 128, 256, 512
mode: fixed
width: 256
height: 256
formats: avif, webp, jpg
quality: 80
resize: fill
card:
mode: responsive
widths: 320, 640, 960
formats: avif, webp, jpg
quality: 80
qualities: 75, 80
resize: fit
hero:
mode: responsive
widths: 1280, 1920
formats: avif, webp, jpg
quality: 80
qualities: 75, 80
resize: fit
```