- добавлены 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
4.5 KiB
Data Model
Текущая PostgreSQL модель описана в packages/database/src/schema.ts и миграциях Drizzle в packages/database/drizzle.
allowed_image_hosts
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:
- lowercase;
- без protocol;
- без path;
- без trailing slash;
- без wildcard на первом этапе;
- source URL должен быть
httpилиhttps; - запрещены localhost, private IP, loopback, link-local.
image_assets
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
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
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
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
allowed_image_hosts(hostname)
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)
Индексы:
image_asset_versions(source_hash)
image_variants(status)
S3 layout
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 всё равно пишется фактический формат:
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:
/images/{assetId}/v{version}/{preset}?w={width}&q={quality}&f=auto
Presets
Клиент не должен передавать произвольные трансформации. Сначала нужны ограниченные presets.
Пример:
avatar:
widths: 128, 256, 512
formats: avif, webp, jpg
quality: 80
resize: fill
card:
widths: 320, 640, 960
formats: avif, webp, jpg
quality: 80
resize: fit
hero:
widths: 1280, 1920
formats: avif, webp, jpg
quality: 80
resize: fit