/** * POST /api/print-agent/jobs/ack (Bearer agent token) * Body: { id, status:'spooled'|'completed'|'failed', cupsJobId?, output?, error? } * or { acks: [ …same… ] } (bulk, e.g. after an lpstat sweep) * * 'spooled' = lp accepted the file (CUPS has it) → the UI toast fires on this * 'completed' = CUPS reports the job done * 'failed' = lp rejected it / CUPS aborted (printer offline, paper out, …) * * Every ack is pushed to the creator's browser through the in-app SSE bus (role-scoped; * the client keeps only events whose userId matches — see plugins/05.print-jobs.client.ts). */ import { requireAgent } from '../../../utils/printAgentAuth' import { ackJob, type AckInput, type PrintJobRow } from '../../../utils/printSpoolDb' import { publishNotification } from '../../../utils/notificationBus' const STATUSES = new Set(['spooled', 'completed', 'failed']) const notify = (job: PrintJobRow, agentName: string) => { if (!job.created_by_role) return const label = job.status === 'failed' ? 'Druck fehlgeschlagen' : job.status === 'completed' ? 'Druck abgeschlossen' : 'An Drucker gesendet' publishNotification({ recipientRoleIds: [Number(job.created_by_role)], payload: { type: 'print-job', title: label, body: `${job.queue}${job.error ? ' – ' + job.error : ''}`, jobId: job.id, batchId: job.batch_id, status: job.status, queue: job.queue, source: job.source, error: job.error, userId: job.created_by_user, agentName, timestamp: Date.now() } }) } export default defineEventHandler(async (event) => { const agent = requireAgent(event) const body = await readBody(event).catch(() => ({})) || {} const acks: any[] = Array.isArray(body.acks) ? body.acks : [body] const results: any[] = [] for (const a of acks.slice(0, 500)) { const id = Number(a?.id) const status = String(a?.status || '') if (!Number.isFinite(id) || !STATUSES.has(status)) { results.push({ id: a?.id, ok: false, message: 'invalid ack' }); continue } const input: AckInput = { id, status: status as any, cupsJobId: a.cupsJobId ? String(a.cupsJobId).slice(0, 80) : null, output: a.output ?? null, error: a.error ?? null } const job = ackJob(agent.id, input) if (!job) { results.push({ id, ok: false, message: 'not your job' }); continue } results.push({ id, ok: true, status: job.status }) notify(job, agent.name) } return { ok: true, results } })