577b13f680
- format.ts: exact px (cm/2.54*dpi), catalog incl. The Frame/portrait, aspect ratio - pipeline.ts: finalizeToFormat (cover/attention crop, dpi metadata), stickerContour (alpha dilation -> white border), heic->png, german filename slug - prompts.ts: task prompts (clean/cutout/format) — reproduce, don't reinterpret - process.ts: per-item chain (load -> model /v1/images -> finalize -> contour -> store) - queue.ts + worker.ts: pg-boss (concurrency, retry, counters, 402 pause, completion) - verified locally: 30x40@300dpi = exact 3543x4724 + dpi meta; sticker white border + alpha
35 lines
1.0 KiB
TypeScript
35 lines
1.0 KiB
TypeScript
import PgBoss from 'pg-boss';
|
|
|
|
export const QUEUE = 'generate';
|
|
export interface GenerateJob { itemId: string; jobId: string; }
|
|
|
|
let boss: PgBoss | null = null;
|
|
|
|
export async function getBoss(): Promise<PgBoss> {
|
|
if (!boss) {
|
|
boss = new PgBoss({ connectionString: process.env.DATABASE_URL });
|
|
boss.on('error', (e) => console.error('[pg-boss]', e));
|
|
await boss.start();
|
|
await boss.createQueue(QUEUE);
|
|
}
|
|
return boss;
|
|
}
|
|
|
|
export async function enqueue(job: GenerateJob): Promise<void> {
|
|
const b = await getBoss();
|
|
await b.send(QUEUE, job, { retryLimit: 2, retryBackoff: true });
|
|
}
|
|
|
|
export async function startWorker(
|
|
concurrency: number,
|
|
handler: (job: GenerateJob) => Promise<void>,
|
|
): Promise<void> {
|
|
const b = await getBoss();
|
|
await b.work<GenerateJob>(
|
|
QUEUE,
|
|
{ batchSize: Math.max(1, concurrency), pollingIntervalSeconds: 2 },
|
|
async (jobs) => { await Promise.all(jobs.map((j) => handler(j.data))); },
|
|
);
|
|
console.log(`[queue] Worker aktiv (Parallelität ${concurrency}).`);
|
|
}
|