sync
This commit is contained in:
214
docs/assets-delivery-platform.md
Normal file
214
docs/assets-delivery-platform.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# 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 являются единственным реализованным типом ассетов.
|
||||
Reference in New Issue
Block a user