- добавлен базовый pnpm workspace для будущих приложений - добавлена dev-инфраструктура PostgreSQL и MinIO - добавлены env-пример и базовые правила репозитория - зафиксированы архитектура, data model и API-контракт - описан контракт с внешним imgproxy
2.5 KiB
2.5 KiB
Черновик API Контракта
Это не реализация API, а фиксация будущего контракта для NestJS backend.
Backend отдаёт JSON, metadata, statuses и URLs. Он не должен проксировать image bytes на каждый обычный запрос.
Allowed Hosts
GET /allowed-hosts
POST /allowed-hosts
PATCH /allowed-hosts/:id
DELETE /allowed-hosts/:id
Assets
GET /assets
POST /assets
GET /assets/:id
DELETE /assets/:id
POST /assets request:
{
"sourceUrl": "https://example.com/photo.jpg"
}
Responsibilities:
- validate source URL;
- check
allowed_image_hosts; - create or reuse
image_assetsrow; - optionally save original to S3 later.
Variants
GET /assets/:id/variants
POST /assets/:id/variants
POST /variants/:id/regenerate
DELETE /variants/:id
POST /assets/:id/variants request:
{
"preset": "card",
"format": "webp",
"width": 640
}
Response if ready:
{
"id": "variant_123",
"status": "ready",
"url": "http://localhost:8888/images/asset_123/w640_q80_card.webp"
}
Response if generation is async:
{
"id": "variant_123",
"status": "pending",
"url": null
}
Image URLs For UI
Для UI нужен endpoint, который возвращает готовый набор URLs для <picture>/srcset.
GET /assets/:id/picture?preset=card
Example response:
{
"assetId": "asset_123",
"preset": "card",
"sources": [
{
"type": "image/avif",
"srcset": "http://localhost:8888/images/asset_123/w320_card.avif 320w, http://localhost:8888/images/asset_123/w640_card.avif 640w"
},
{
"type": "image/webp",
"srcset": "http://localhost:8888/images/asset_123/w320_card.webp 320w, http://localhost:8888/images/asset_123/w640_card.webp 640w"
}
],
"fallback": {
"src": "http://localhost:8888/images/asset_123/w640_card.jpg",
"width": 640,
"height": null
}
}
Worker Lifecycle
Первый MVP может генерировать sync на request. Если генерация тяжёлая, variant создаётся как pending, а worker обрабатывает job.
PostgreSQL может выступить первой очередью:
SELECT * FROM image_variants
WHERE status = 'pending'
FOR UPDATE SKIP LOCKED
LIMIT 1
Позже можно добавить Redis/Valkey или отдельную queue, если PostgreSQL станет узким местом.