feat(setup): print a real test slip from the printer "Test connection" modal
healthCheck only opens the transport (TCP connect / USB open) — it proves the printer is REACHABLE, not that paper feeds and the head fires. Add a "Print test slip" action so the admin can physically confirm a printer is live (the new host-net USB /dev/usb/lpN path, or a network printer). - server: POST /api/setup/test-print — printer-only, re-merges stored secrets like /test (so an edited network printer authenticates), creates the device, and pushes a short slip via the device-agnostic printReport(). Fail-soft: a print error (paper out, head fault, transport drop) is reported, never a 500. Mirrors the test-anpr pattern. - web: testPrint() client + PrintTestResult; a button in the device modal shown for category=printer, with ok/fail rendering. i18n keys in sq + en (parity holds). Server 168 tests pass; web + server typecheck clean. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
isCamera,
|
||||
isDiscoverable,
|
||||
isHardenable,
|
||||
isPrinter,
|
||||
registerBuiltinDrivers,
|
||||
registry,
|
||||
setDeviceLogSink,
|
||||
@@ -391,6 +392,70 @@ export async function setupRoutes(
|
||||
},
|
||||
);
|
||||
|
||||
// Print a TEST SLIP on a printer config WITHOUT saving. healthCheck only opens the
|
||||
// transport (TCP connect / USB open) — it proves reachability, NOT that paper feeds
|
||||
// and the head fires. This pushes a real short slip through the device-agnostic
|
||||
// printReport(), so the admin can physically confirm the printer is live (the USB
|
||||
// /dev/usb/lpN path or the network printer). Fail-soft like test-anpr: a print error
|
||||
// is reported, never a 500. Mirrors /test's stored-secret re-merge so an edited
|
||||
// network printer still authenticates.
|
||||
app.post<{ Body: TestBody }>(
|
||||
"/api/setup/test-print",
|
||||
{ preHandler: adminGuard },
|
||||
async (req, reply) => {
|
||||
const { driverId, config, id } = req.body;
|
||||
const driver = registry.get(driverId);
|
||||
if (!driver) return reply.code(400).send({ error: `unknown driver: ${driverId}` });
|
||||
if (driver.category !== "printer") {
|
||||
return reply.code(400).send({ error: `driver ${driverId} is not a printer` });
|
||||
}
|
||||
|
||||
const merged: Record<string, string | number | boolean | undefined> = { ...config };
|
||||
if (id) {
|
||||
for (const [k, v] of Object.entries(storedSecrets(db, id, driverId, config))) {
|
||||
const sent = merged[k];
|
||||
if (sent === undefined || sent === "" || sent === 0) merged[k] = v as string | number;
|
||||
}
|
||||
}
|
||||
|
||||
let device;
|
||||
try {
|
||||
device = registry.create(driverId, merged as Record<string, string | number | boolean>);
|
||||
} catch (err) {
|
||||
return reply.code(400).send({ error: (err as Error).message });
|
||||
}
|
||||
if (!isPrinter(device)) {
|
||||
return reply.code(400).send({ error: `driver ${driverId} cannot print` });
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
await device.printReport({
|
||||
title: "TEST PRINT",
|
||||
lines: [
|
||||
"Parking System",
|
||||
"Printer test slip",
|
||||
new Date().toLocaleString("sv"), // YYYY-MM-DD HH:MM:SS, locale-stable
|
||||
"",
|
||||
"If you can read this, the",
|
||||
"printer is connected and",
|
||||
"printing correctly.",
|
||||
],
|
||||
});
|
||||
} catch (err) {
|
||||
// The failure we're testing for (paper out, head fault, transport drop) —
|
||||
// report it, don't 500.
|
||||
return reply.send({
|
||||
ok: false,
|
||||
reason: "print-failed",
|
||||
detail: (err as Error).message,
|
||||
tookMs: Date.now() - startedAt,
|
||||
});
|
||||
}
|
||||
return reply.send({ ok: true, tookMs: Date.now() - startedAt });
|
||||
},
|
||||
);
|
||||
|
||||
// Candidate backend IPs the device can push to, for a given device host. The
|
||||
// wizard pre-fills with the on-subnet one and lets the admin override (matters
|
||||
// on multi-NIC hosts). See net.ts / wiki/concepts/device-input-flow.md.
|
||||
|
||||
Reference in New Issue
Block a user