feat: добавить базовые сервисы image-platform
- добавлены backend, admin, gateway и worker skeleton - добавлены Drizzle schema, database package и initial migration - добавлены shared packages для RabbitMQ topology и S3 helpers - обновлены dev-инфраструктура, env example, scripts и dependencies - обновлена документация под versioned image URLs и read-through flow
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
# Черновик Data Model
|
||||
# Data Model
|
||||
|
||||
Это черновик для будущих миграций PostgreSQL. Реальные таблицы добавим вместе с API.
|
||||
Текущая PostgreSQL модель описана в `packages/database/src/schema.ts` и миграциях Drizzle в `packages/database/drizzle`.
|
||||
|
||||
## allowed_image_hosts
|
||||
|
||||
```text
|
||||
id
|
||||
hostname
|
||||
enabled
|
||||
description nullable
|
||||
created_at
|
||||
updated_at
|
||||
id uuid pk default gen_random_uuid()
|
||||
hostname text not null unique
|
||||
enabled boolean not null default true
|
||||
description text nullable
|
||||
created_at timestamptz not null default now()
|
||||
updated_at timestamptz not null default now()
|
||||
```
|
||||
|
||||
Правила normalization:
|
||||
@@ -26,53 +26,119 @@ updated_at
|
||||
## image_assets
|
||||
|
||||
```text
|
||||
id
|
||||
source_url
|
||||
source_host
|
||||
source_hash
|
||||
original_s3_key nullable
|
||||
status
|
||||
width nullable
|
||||
height nullable
|
||||
content_type nullable
|
||||
size_bytes nullable
|
||||
created_at
|
||||
updated_at
|
||||
id uuid pk default gen_random_uuid()
|
||||
public_id text not null unique
|
||||
current_version integer not null default 1
|
||||
status asset_status not null default active
|
||||
created_at timestamptz not null default now()
|
||||
updated_at timestamptz not null default now()
|
||||
```
|
||||
|
||||
`public_id` - стабильный идентификатор в public URL. `current_version` указывает активную версию source image и используется для cache invalidation без purge.
|
||||
|
||||
## image_asset_versions
|
||||
|
||||
```text
|
||||
id uuid pk default gen_random_uuid()
|
||||
asset_id uuid not null references image_assets(id) on delete cascade
|
||||
version integer not null
|
||||
source_url text not null
|
||||
source_host text not null
|
||||
source_hash text not null
|
||||
original_s3_key text nullable
|
||||
width integer nullable
|
||||
height integer nullable
|
||||
content_type text nullable
|
||||
size_bytes bigint nullable
|
||||
created_at timestamptz not null default now()
|
||||
```
|
||||
|
||||
Каждое изменение source image создаёт новую версию. Старые versioned URLs остаются immutable, новые клиенты получают URL с новым `v{version}`.
|
||||
|
||||
## image_variants
|
||||
|
||||
```text
|
||||
id
|
||||
asset_id
|
||||
preset
|
||||
variant_hash
|
||||
format
|
||||
width
|
||||
height nullable
|
||||
quality
|
||||
s3_key
|
||||
status: pending | processing | ready | failed
|
||||
size_bytes nullable
|
||||
error nullable
|
||||
created_at
|
||||
updated_at
|
||||
last_accessed_at nullable
|
||||
id uuid pk default gen_random_uuid()
|
||||
asset_id uuid not null references image_assets(id) on delete cascade
|
||||
asset_version_id uuid not null references image_asset_versions(id) on delete cascade
|
||||
asset_version integer not null
|
||||
preset text not null
|
||||
variant_hash text not null unique
|
||||
requested_format requested_format not null default auto
|
||||
format variant_format not null
|
||||
width integer not null
|
||||
height integer nullable
|
||||
quality integer not null
|
||||
s3_key text not null unique
|
||||
content_type text nullable
|
||||
etag text nullable
|
||||
status variant_status not null default pending
|
||||
size_bytes bigint nullable
|
||||
error text nullable
|
||||
attempt_count integer not null default 0
|
||||
last_accessed_at timestamptz nullable
|
||||
created_at timestamptz not null default now()
|
||||
updated_at timestamptz not null default now()
|
||||
```
|
||||
|
||||
`requested_format` хранит то, что запросил клиент (`auto`, `avif`, `webp`, `jpg`, `png`). `format` хранит фактический output format после negotiation по `Accept`.
|
||||
|
||||
## Enums
|
||||
|
||||
```text
|
||||
asset_status: active | disabled | deleted
|
||||
requested_format: auto | avif | webp | jpg | png
|
||||
variant_format: avif | webp | jpg | png
|
||||
variant_status: pending | processing | ready | failed
|
||||
```
|
||||
|
||||
## Unique constraints
|
||||
|
||||
```text
|
||||
allowed_image_hosts(hostname)
|
||||
image_assets(source_hash)
|
||||
image_variants(asset_id, variant_hash, format)
|
||||
image_assets(public_id)
|
||||
image_asset_versions(asset_id, version)
|
||||
image_variants(asset_id, asset_version, preset, width, quality, format)
|
||||
image_variants(s3_key)
|
||||
image_variants(variant_hash)
|
||||
```
|
||||
|
||||
Индексы:
|
||||
|
||||
```text
|
||||
image_asset_versions(source_hash)
|
||||
image_variants(status)
|
||||
```
|
||||
|
||||
## S3 layout
|
||||
|
||||
```text
|
||||
originals/{assetId}/source
|
||||
variants/{assetId}/{variantHash}.{format}
|
||||
originals/{assetId}/v{version}/source
|
||||
variants/{assetId}/v{version}/{variantHash}.{format}
|
||||
```
|
||||
|
||||
`variantHash` должен включать:
|
||||
|
||||
- `assetId`;
|
||||
- `assetVersion`;
|
||||
- `preset`;
|
||||
- normalized width;
|
||||
- normalized quality;
|
||||
- фактический output format;
|
||||
- параметры transform, влияющие на bytes.
|
||||
|
||||
Для `f=auto` в public URL в S3 всё равно пишется фактический формат:
|
||||
|
||||
```text
|
||||
variants/asset_123/v4/card_w640_q80_avif.avif
|
||||
variants/asset_123/v4/card_w640_q80_webp.webp
|
||||
variants/asset_123/v4/card_w640_q80_jpg.jpg
|
||||
```
|
||||
|
||||
Public URL также versioned:
|
||||
|
||||
```text
|
||||
/images/{assetId}/v{version}/{preset}?w={width}&q={quality}&f=auto
|
||||
```
|
||||
|
||||
## Presets
|
||||
|
||||
Reference in New Issue
Block a user