/** * POST /api/print-agent/heartbeat (Bearer agent token, every 60 s) * Body: { version, hostname, platform, * printers: [{ queue, state, accepting, deviceUri, makeModel, media:{w,h}|null, jobs }], * queueDepth: { [queue]: n }, health: { uptime, load, diskFreeMb, smartWear, temp, … } } * → { ok, serverTime, queued, commands:[{id,command,payload}], supportUntil } */ import { requireAgent } from '../../utils/printAgentAuth' import { updateAgentHeartbeat, claimCommands, countQueuedJobs, type AgentPrinterInfo } from '../../utils/printSpoolDb' const SAFE_QUEUE = /^[A-Za-z0-9_.-]{1,64}$/ export default defineEventHandler(async (event) => { const agent = requireAgent(event) const body = await readBody(event).catch(() => ({})) || {} const printers: AgentPrinterInfo[] = Array.isArray(body.printers) ? body.printers .filter((p: any) => p && SAFE_QUEUE.test(String(p.queue || ''))) .slice(0, 100) .map((p: any) => ({ queue: String(p.queue), state: p.state ? String(p.state).slice(0, 40) : undefined, accepting: p.accepting === undefined ? undefined : !!p.accepting, deviceUri: p.deviceUri ? String(p.deviceUri).slice(0, 200) : undefined, makeModel: p.makeModel ? String(p.makeModel).slice(0, 120) : undefined, media: (p.media && Number(p.media.w) > 0 && Number(p.media.h) > 0) ? { w: Number(p.media.w), h: Number(p.media.h) } : null, jobs: Number.isFinite(Number(p.jobs)) ? Number(p.jobs) : undefined })) : undefined const queueDepth: Record | undefined = body.queueDepth && typeof body.queueDepth === 'object' ? Object.fromEntries(Object.entries(body.queueDepth).filter(([k]) => SAFE_QUEUE.test(k)).map(([k, v]) => [k, Number(v) || 0])) : undefined updateAgentHeartbeat(agent.id, { version: body.version ? String(body.version).slice(0, 40) : undefined, hostname: body.hostname ? String(body.hostname).slice(0, 120) : undefined, platform: body.platform ? String(body.platform).slice(0, 120) : undefined, printers, queueDepth, health: body.health && typeof body.health === 'object' ? body.health : undefined }) return { ok: true, serverTime: new Date().toISOString(), queued: countQueuedJobs(agent.id), commands: claimCommands(agent.id), supportUntil: agent.support_until } })