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:
2026-05-05 09:59:21 +03:00
parent 37592c8b81
commit bcadb85a83
66 changed files with 8698 additions and 213 deletions

View File

@@ -0,0 +1,21 @@
{
"name": "@image-platform/queue",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
},
"types": "./src/index.ts",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"devDependencies": {
"@types/node": "^25.6.0",
"typescript": "^6.0.3"
}
}

View File

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

View File

@@ -0,0 +1,37 @@
export type GenerateVariantJob = {
jobId: string
variantId: string
}
export function parseGenerateVariantJobBuffer(buffer: Buffer): GenerateVariantJob {
const value = JSON.parse(buffer.toString("utf8")) as unknown
return parseGenerateVariantJob(value)
}
export function parseGenerateVariantJob(value: unknown): GenerateVariantJob {
if (!isRecord(value)) {
throw new Error("generate variant job must be a JSON object")
}
if (!isNonEmptyString(value.jobId)) {
throw new Error("generate variant job must include jobId")
}
if (!isNonEmptyString(value.variantId)) {
throw new Error("generate variant job must include variantId")
}
return {
jobId: value.jobId,
variantId: value.variantId,
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
function isNonEmptyString(value: unknown): value is string {
return typeof value === "string" && value.trim().length > 0
}

View File

@@ -0,0 +1,33 @@
export type QueueTopology = {
generateVariantDeadLetterQueue: string
generateVariantDeadLetterRoutingKey: string
generateVariantQueue: string
generateVariantRoutingKey: string
jobsDeadLetterExchange: string
jobsExchange: string
}
export const DEFAULT_QUEUE_TOPOLOGY: QueueTopology = {
generateVariantDeadLetterQueue: "image.generate-variant.dlq",
generateVariantDeadLetterRoutingKey: "image.generate-variant.dlq",
generateVariantQueue: "image.generate-variant",
generateVariantRoutingKey: "image.generate-variant",
jobsDeadLetterExchange: "image-platform.jobs.dlx",
jobsExchange: "image-platform.jobs",
}
export function loadQueueTopologyFromEnv(env: NodeJS.ProcessEnv = process.env): QueueTopology {
const generateVariantQueue = env.RABBITMQ_GENERATE_VARIANT_QUEUE ?? DEFAULT_QUEUE_TOPOLOGY.generateVariantQueue
const generateVariantDeadLetterQueue =
env.RABBITMQ_GENERATE_VARIANT_DLQ ?? DEFAULT_QUEUE_TOPOLOGY.generateVariantDeadLetterQueue
return {
generateVariantDeadLetterQueue,
generateVariantDeadLetterRoutingKey:
env.RABBITMQ_GENERATE_VARIANT_DLQ_ROUTING_KEY ?? generateVariantDeadLetterQueue,
generateVariantQueue,
generateVariantRoutingKey: env.RABBITMQ_GENERATE_VARIANT_ROUTING_KEY ?? generateVariantQueue,
jobsDeadLetterExchange: env.RABBITMQ_GENERATE_VARIANT_DLX ?? DEFAULT_QUEUE_TOPOLOGY.jobsDeadLetterExchange,
jobsExchange: env.RABBITMQ_JOBS_EXCHANGE ?? DEFAULT_QUEUE_TOPOLOGY.jobsExchange,
}
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false
},
"exclude": ["dist", "node_modules", "**/*.spec.ts"]
}

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ES2023"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"target": "ES2023",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules"]
}