feat: добавить генерацию image variants

- добавлен shared config presets, custom transforms и allowlist hosts
- реализованы Backend endpoints для assets, presets и variants
- добавлена orchestration через PostgreSQL, RabbitMQ, S3 и worker
- обновлён Gateway read-through flow с L1 cache и корректным Vary: Accept
- добавлена миграция resize_mode для variants lookup
- обновлены dev scripts, env template, lockfile и документация
This commit is contained in:
2026-05-05 13:25:28 +03:00
parent bcadb85a83
commit 1c0e8277a3
59 changed files with 3526 additions and 143 deletions

View File

@@ -2,10 +2,11 @@
"name": "@image-platform/queue",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"require": "./dist/index.js",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
@@ -15,7 +16,11 @@
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"devDependencies": {
"@types/amqplib": "^0.10.8",
"@types/node": "^25.6.0",
"typescript": "^6.0.3"
},
"dependencies": {
"amqplib": "^1.0.4"
}
}

View File

@@ -0,0 +1,29 @@
import type { Channel } from "amqplib"
import type { GenerateVariantJob } from "./jobs.js"
import type { QueueTopology } from "./topology.js"
export async function assertQueueTopology(channel: Channel, topology: QueueTopology) {
await channel.assertExchange(topology.jobsExchange, "direct", { durable: true })
await channel.assertExchange(topology.jobsDeadLetterExchange, "direct", { durable: true })
await channel.assertQueue(topology.generateVariantQueue, {
deadLetterExchange: topology.jobsDeadLetterExchange,
deadLetterRoutingKey: topology.generateVariantDeadLetterRoutingKey,
durable: true,
})
await channel.assertQueue(topology.generateVariantDeadLetterQueue, { durable: true })
await channel.bindQueue(topology.generateVariantQueue, topology.jobsExchange, topology.generateVariantRoutingKey)
await channel.bindQueue(
topology.generateVariantDeadLetterQueue,
topology.jobsDeadLetterExchange,
topology.generateVariantDeadLetterRoutingKey,
)
}
export function publishGenerateVariantJob(channel: Channel, topology: QueueTopology, job: GenerateVariantJob) {
return channel.publish(topology.jobsExchange, topology.generateVariantRoutingKey, Buffer.from(JSON.stringify(job)), {
contentType: "application/json",
deliveryMode: 2,
persistent: true,
})
}

View File

@@ -1,2 +1,3 @@
export * from "./amqp.js"
export * from "./jobs.js"
export * from "./topology.js"

View File

@@ -5,8 +5,8 @@
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ES2023"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"module": "Node16",
"moduleResolution": "Node16",
"noUncheckedIndexedAccess": true,
"outDir": "./dist",
"rootDir": "./src",