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:
24
apps/worker/package.json
Normal file
24
apps/worker/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@image-platform/worker",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"dev": "tsx watch src/main.ts",
|
||||
"start": "node dist/main.js",
|
||||
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@image-platform/database": "workspace:*",
|
||||
"@image-platform/queue": "workspace:*",
|
||||
"@image-platform/storage": "workspace:*",
|
||||
"amqplib": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/amqplib": "^0.10.8",
|
||||
"@types/node": "^25.6.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
25
apps/worker/src/config.ts
Normal file
25
apps/worker/src/config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { loadQueueTopologyFromEnv, type QueueTopology } from "@image-platform/queue"
|
||||
|
||||
export type WorkerConfig = {
|
||||
prefetch: number
|
||||
queueTopology: QueueTopology
|
||||
rabbitmqUrl: string
|
||||
}
|
||||
|
||||
export function loadWorkerConfig(env: NodeJS.ProcessEnv = process.env): WorkerConfig {
|
||||
return {
|
||||
prefetch: parsePositiveInteger(env.WORKER_PREFETCH, 2),
|
||||
queueTopology: loadQueueTopologyFromEnv(env),
|
||||
rabbitmqUrl: env.RABBITMQ_URL ?? "amqp://image:image-password@localhost:5672/image_platform",
|
||||
}
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value: string | undefined, fallback: number) {
|
||||
if (!value) {
|
||||
return fallback
|
||||
}
|
||||
|
||||
const parsed = Number.parseInt(value, 10)
|
||||
|
||||
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback
|
||||
}
|
||||
67
apps/worker/src/main.ts
Normal file
67
apps/worker/src/main.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import amqp, { type Channel, type ConsumeMessage } from "amqplib"
|
||||
import { parseGenerateVariantJobBuffer, type QueueTopology } from "@image-platform/queue"
|
||||
|
||||
import { loadWorkerConfig } from "./config.js"
|
||||
|
||||
async function bootstrap() {
|
||||
const config = loadWorkerConfig()
|
||||
const connection = await amqp.connect(config.rabbitmqUrl)
|
||||
const channel = await connection.createChannel()
|
||||
|
||||
await assertQueueTopology(channel, config.queueTopology)
|
||||
await channel.prefetch(config.prefetch)
|
||||
await channel.consume(
|
||||
config.queueTopology.generateVariantQueue,
|
||||
(message) => void handleGenerateVariantMessage(channel, message),
|
||||
{ noAck: false },
|
||||
)
|
||||
|
||||
console.log(`worker consuming ${config.queueTopology.generateVariantQueue}`)
|
||||
|
||||
const shutdown = async () => {
|
||||
console.log("worker shutting down")
|
||||
await channel.close().catch((error: unknown) => console.error("failed to close RabbitMQ channel", error))
|
||||
await connection.close().catch((error: unknown) => console.error("failed to close RabbitMQ connection", error))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
process.once("SIGINT", () => void shutdown())
|
||||
process.once("SIGTERM", () => void shutdown())
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
async function handleGenerateVariantMessage(channel: Channel, message: ConsumeMessage | null) {
|
||||
if (message === null) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const job = parseGenerateVariantJobBuffer(message.content)
|
||||
console.log("generate variant job received, handler not implemented yet", job)
|
||||
channel.nack(message, false, false)
|
||||
} catch (error) {
|
||||
console.error("invalid generate variant job", error)
|
||||
channel.nack(message, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
void bootstrap().catch((error: unknown) => {
|
||||
console.error("worker failed to start", error)
|
||||
process.exit(1)
|
||||
})
|
||||
7
apps/worker/tsconfig.build.json
Normal file
7
apps/worker/tsconfig.build.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false
|
||||
},
|
||||
"exclude": ["dist", "node_modules", "**/*.spec.ts"]
|
||||
}
|
||||
21
apps/worker/tsconfig.json
Normal file
21
apps/worker/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Reference in New Issue
Block a user