# 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 являются единственным реализованным типом ассетов.