feat: image core — format math, sharp pipeline, sticker contour, prompts, pg-boss queue+worker
- 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
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
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}).`);
|
||||
}
|
||||
Reference in New Issue
Block a user