125 lines
2.5 KiB
Markdown
125 lines
2.5 KiB
Markdown
|
|
# Черновик API Контракта
|
|||
|
|
|
|||
|
|
Это не реализация API, а фиксация будущего контракта для NestJS backend.
|
|||
|
|
|
|||
|
|
Backend отдаёт JSON, metadata, statuses и URLs. Он не должен проксировать image bytes на каждый обычный запрос.
|
|||
|
|
|
|||
|
|
## Allowed Hosts
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /allowed-hosts
|
|||
|
|
POST /allowed-hosts
|
|||
|
|
PATCH /allowed-hosts/:id
|
|||
|
|
DELETE /allowed-hosts/:id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Assets
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /assets
|
|||
|
|
POST /assets
|
|||
|
|
GET /assets/:id
|
|||
|
|
DELETE /assets/:id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`POST /assets` request:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"sourceUrl": "https://example.com/photo.jpg"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Responsibilities:
|
|||
|
|
|
|||
|
|
- validate source URL;
|
|||
|
|
- check `allowed_image_hosts`;
|
|||
|
|
- create or reuse `image_assets` row;
|
|||
|
|
- optionally save original to S3 later.
|
|||
|
|
|
|||
|
|
## Variants
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /assets/:id/variants
|
|||
|
|
POST /assets/:id/variants
|
|||
|
|
POST /variants/:id/regenerate
|
|||
|
|
DELETE /variants/:id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`POST /assets/:id/variants` request:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"preset": "card",
|
|||
|
|
"format": "webp",
|
|||
|
|
"width": 640
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Response if ready:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "variant_123",
|
|||
|
|
"status": "ready",
|
|||
|
|
"url": "http://localhost:8888/images/asset_123/w640_q80_card.webp"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Response if generation is async:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "variant_123",
|
|||
|
|
"status": "pending",
|
|||
|
|
"url": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Image URLs For UI
|
|||
|
|
|
|||
|
|
Для UI нужен endpoint, который возвращает готовый набор URLs для `<picture>`/`srcset`.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /assets/:id/picture?preset=card
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Example response:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"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 может выступить первой очередью:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SELECT * FROM image_variants
|
|||
|
|
WHERE status = 'pending'
|
|||
|
|
FOR UPDATE SKIP LOCKED
|
|||
|
|
LIMIT 1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Позже можно добавить Redis/Valkey или отдельную queue, если PostgreSQL станет узким местом.
|