Persist image submissions with fixed idempotency keys, leased execution, provider identity, reference snapshots and staged results. Integrate DIY, assisted assembly and non-integrated white-background sessions without holding generation in a browser request. Recover ambiguous submissions through owner-scoped reads and stop a new eight-view batch when restoring an earlier request. Add the NativeImageGenerationJob migration and a dedicated worker to both release entry points. Protect job inputs/results from storage GC. Existing production configuration and business data are not changed by this commit. Validation: 256 focused Jest tests, isolated PostgreSQL migration/lease/transaction checks (8), deployment guard tests (9), type-check, production build and desktop/ mobile browser component scenarios. Live native 2K reference-image request worked; reusing its idempotency key returned the same task. Build uses local placeholders for otherwise absent Drawing System configuration. Three unrelated material tests also fail with the original mainline test file; they remain outside this change. Architecture/visual/root-cause review: passed with warnings. Independent review completed for the integration and shared input/storage paths; worker additionally reviewed by the primary agent. Production merge/deployment requires authorization.
24 lines
1.2 KiB
JavaScript
24 lines
1.2 KiB
JavaScript
// Dedicated process: it resumes persisted jobs after application/host restarts.
|
|
const secret = process.env.AI_IMAGE_WORKER_SECRET?.trim()
|
|
const port = Number(process.env.PORT ?? process.env.APP_PORT ?? 3001)
|
|
if (!secret || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
throw new Error('AI image worker requires AI_IMAGE_WORKER_SECRET and a valid local app port')
|
|
}
|
|
let stopping = false
|
|
process.on('SIGINT', () => { stopping = true })
|
|
process.on('SIGTERM', () => { stopping = true })
|
|
while (!stopping) {
|
|
let idle = false
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${port}/api/internal/ai-image/worker/run`, {
|
|
method: 'POST', redirect: 'error', signal: AbortSignal.timeout(120_000),
|
|
headers: { 'x-ai-image-worker-secret': secret },
|
|
})
|
|
if (!response.ok) throw new Error('worker turn failed')
|
|
const payload = await response.json()
|
|
idle = payload?.data?.worker?.status === 'idle'
|
|
if (payload?.data?.worker?.status === 'blocked') console.error('[AI image worker] a task requires review')
|
|
} catch { console.error('[AI image worker] turn unavailable; persisted tasks will resume') }
|
|
if (!stopping) await new Promise(resolve => setTimeout(resolve, idle ? 5000 : 1000))
|
|
}
|