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

215 lines
9.8 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.

# Assets Delivery Platform
## Концепция
`image-platform` эволюционирует из платформы для изображений в Assets Delivery Platform.
Платформа должна быть control plane и delivery layer для загрузки, обработки, версионирования и доставки ассетов. Первый поддерживаемый тип ассетов - изображения. Текущие наработки по image pipeline остаются основой первого production-ready vertical slice.
## Продуктовая цель
Пользователь должен иметь возможность управлять ассетами через кабинет и программно через API.
Клиентский backend должен уметь без участия UI:
- загрузить изображение;
- выбрать preset обработки;
- запустить build;
- получить статус обработки;
- получить готовые delivery URL;
- использовать публичные URL в приложении, CMS, магазине или любом другом сервисе.
Платформа должна быть не только оптимизатором изображений, а headless-сервисом доставки ассетов с UI для управления.
## Первый vertical slice: Images
Первый модуль платформы - изображения.
В рамках images сохраняются текущие архитектурные решения:
- `Gateway` как публичный image origin;
- read-through delivery flow;
- `Backend` как orchestration/control plane;
- `PostgreSQL` как источник правды для metadata, statuses и variants;
- `S3/MinIO` как хранилище originals и generated variants;
- `RabbitMQ` как очередь задач;
- `Worker` как исполнитель image processing;
- внешний `imgproxy` как CPU-heavy image processor;
- versioned immutable public URLs;
- presets и variants;
- `f=auto` с negotiation по `Accept` header.
Текущий публичный URL для managed images остаётся базовым delivery contract:
```text
GET /images/{assetId}/v{version}/{preset}?w={width}&q={quality}&f=auto
```
## Кабинет пользователя
На первом этапе кабинет показывает только раздел работы с изображениями.
Пользователь должен иметь возможность:
- загружать изображения;
- создавать и редактировать image presets;
- смотреть список загруженных изображений;
- смотреть версии, variants, статусы обработки и готовые URL;
- выпускать API-ключи для проекта.
В будущем кабинет расширяется разделами:
- Images;
- Videos;
- Sprites;
- Fonts;
- другие типы ассетов при появлении продуктовой необходимости.
## Projects
`Project` становится основной областью изоляции.
К проекту должны относиться:
- assets;
- presets;
- builds;
- variants/results;
- API keys;
- лимиты;
- настройки delivery;
- allowlist источников;
- usage/billing metrics, если они появятся.
На первом этапе можно развивать images внутри проекта, не создавая преждевременно универсальную модель для всех типов ассетов.
## API Keys
Для каждого проекта пользователь может выпускать API-ключи.
API-ключ нужен для server-side интеграций, где backend клиента программно добавляет файлы, запускает обработку и получает результаты.
Ключ должен храниться безопасно:
- secret показывается пользователю только при создании;
- в базе хранится hash секрета;
- в UI отображается только prefix/identifier;
- ключ можно отозвать;
- ключ может иметь scopes;
- желательно хранить дату последнего использования.
Базовые scopes:
```text
assets:read
assets:write
assets:delete
presets:read
presets:write
builds:read
builds:write
```
Delivery URLs остаются публичными и не требуют `Authorization`, если конкретный проект не включает приватный delivery mode.
## Headless API
Платформа должна предоставлять публичный management API для backend-клиентов.
Минимальный image API первого этапа:
```text
POST /api/v1/images
GET /api/v1/images
GET /api/v1/images/{id}
DELETE /api/v1/images/{id}
POST /api/v1/images/{id}/builds
GET /api/v1/images/{id}/builds
GET /api/v1/images/{id}/results
GET /api/v1/image-presets
POST /api/v1/project-api-keys
GET /api/v1/project-api-keys
DELETE /api/v1/project-api-keys/{id}
```
API должен поддерживать загрузку файла напрямую, а не только регистрацию внешнего `sourceUrl`.
Пример server-side сценария:
```text
1. Backend клиента отправляет изображение в API платформы с project API key.
2. Платформа сохраняет original, создаёт asset и version.
3. Backend клиента указывает preset или запускает build.
4. Worker генерирует variants/results.
5. Backend клиента получает готовые public delivery URL.
```
## Builds и Results
`Build` описывает запуск обработки ассета по preset или transform config.
`Result` или `Variant` описывает готовый артефакт, который можно доставлять через public URL.
Для images текущая сущность `image_variants` уже выполняет роль результата обработки. При развитии API можно добавить explicit build layer, не ломая текущий read-through delivery flow.
## Realtime transforms и cropping
Платформа должна поддерживать два режима обработки изображений:
- preset builds - заранее заданные и ограниченные variants;
- realtime transforms - динамические resize/crop/format/quality операции через delivery URL.
Realtime crop должен быть ограничен правилами проекта и preset/custom transform config, чтобы пользователь не мог бесконтрольно создавать произвольные дорогие трансформации.
Первый запрос на dynamic transform может генерировать результат через worker/imgproxy и сохранять его в storage/cache. Следующие запросы должны отдавать уже готовый артефакт.
Пример будущего dynamic transform URL:
```text
GET /images/{assetId}/v{version}/custom?w=800&h=600&fit=fill&crop=center&f=auto&q=80
```
Параметры, влияющие на bytes, должны входить в deterministic variant hash и S3 key.
## Workers
Для каждого типа ассетов предусматривается специализированный worker.
Общий orchestration остаётся в backend, database, queue и storage. Worker конкретного типа отвечает за инструменты обработки этого типа.
Планируемая модель:
```text
image-worker -> imgproxy / sharp / imagemagick
video-worker -> ffmpeg
font-worker -> fonttools / subset tools
sprite-worker -> svg/css sprite builder
```
На первом этапе реализуется и развивается `image-worker`. Остальные worker'ы добавляются только при появлении соответствующих продуктовых задач.
## Архитектурный принцип
Не нужно преждевременно строить универсальный engine для всех возможных ассетов.
Правильное направление:
- делать images как первый полноценный модуль;
- общие сущности называть так, чтобы они не блокировали будущие типы ассетов;
- выносить в общий слой только реально общие части: projects, API keys, queue orchestration, storage contract, statuses, delivery concepts;
- типоспецифичную обработку держать внутри конкретного модуля.
Примеры naming direction:
```text
Project вместо ImageProject
ProjectApiKey вместо ImageApiKey
ProcessingJob вместо ImageWorkerJob
AssetBuild вместо ImageBuild, если build станет общим понятием
```
При этом текущие `image_assets`, `image_asset_versions` и `image_variants` могут оставаться конкретными image-таблицами, пока images являются единственным реализованным типом ассетов.