Public Access
Replaces the brother_ql subprocess call in printBadge with printPng, and swaps available() for a real reachability check. Uses brace matching rather than literal body matching so it survives formatting differences, refuses to run if the anchors are missing, is idempotent, and writes a .bak first. The badge layout is untouched.
172 lines
5.8 KiB
JavaScript
172 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* One-shot patch: point printer.js at the native QL protocol module instead of
|
|
* shelling out to the Python brother_ql CLI.
|
|
*
|
|
* node scripts/wire-native-printing.mjs # patch
|
|
* node scripts/wire-native-printing.mjs --check # report only, change nothing
|
|
*
|
|
* Safe to run twice — it detects an already-patched file and stops.
|
|
* Writes printer.js.bak next to the original before touching anything.
|
|
*
|
|
* Only the imports and the two functions at the bottom change. The badge
|
|
* layout (drawBadge / renderBadgePng) is left exactly as it is.
|
|
*/
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const target = path.resolve(here, '..', 'src', 'printer.js');
|
|
const checkOnly = process.argv.includes('--check');
|
|
|
|
function fail(message) {
|
|
console.error(`\n FAILED: ${message}\n`);
|
|
console.error(' Nothing was changed. Patch printer.js by hand instead.\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
/**
|
|
* Find a top-level function and return its full text by matching braces.
|
|
* Formatting-independent, unlike matching the whole body literally.
|
|
*/
|
|
function extractFunction(source, signature) {
|
|
const start = source.indexOf(signature);
|
|
if (start === -1) return null;
|
|
|
|
let depth = 0;
|
|
let i = source.indexOf('{', start);
|
|
if (i === -1) return null;
|
|
|
|
for (; i < source.length; i++) {
|
|
if (source[i] === '{') depth++;
|
|
else if (source[i] === '}') {
|
|
depth--;
|
|
if (depth === 0) {
|
|
return { start, end: i + 1, text: source.slice(start, i + 1) };
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (!fs.existsSync(target)) fail(`${target} does not exist. Run this from the repo root.`);
|
|
|
|
let source = fs.readFileSync(target, 'utf8');
|
|
const original = source;
|
|
|
|
if (source.includes("from './printing/ql-print.js'")) {
|
|
console.log('\n Already patched — printer.js imports the native module. Nothing to do.\n');
|
|
process.exit(0);
|
|
}
|
|
|
|
const changes = [];
|
|
|
|
/* -- 1. Import the native module ---------------------------------- */
|
|
|
|
const importAnchor = "import { photoAbsolutePath } from './photos.js';";
|
|
if (!source.includes(importAnchor)) fail('could not find the photos.js import');
|
|
|
|
source = source.replace(
|
|
importAnchor,
|
|
`${importAnchor}\nimport { printPng, printerHealth } from './printing/ql-print.js';`
|
|
);
|
|
changes.push('added the ql-print import');
|
|
|
|
/* -- 2. Replace printBadge ---------------------------------------- */
|
|
|
|
const printBadge = extractFunction(source, 'export async function printBadge(visit, site)');
|
|
if (!printBadge) fail('could not locate printBadge');
|
|
if (!printBadge.text.includes('runBrotherQl')) {
|
|
fail('printBadge does not call runBrotherQl — it may already have been changed');
|
|
}
|
|
|
|
const newPrintBadge = `export async function printBadge(visit, site) {
|
|
if (!isConfigured(site)) throw new Error('Server printing is not turned on for this site.');
|
|
|
|
const png = await renderBadgePng(visit, site);
|
|
const port = Number(site.printer_port) || 9100;
|
|
const target = \`tcp://\${site.printer_host}:\${port}\`;
|
|
|
|
// The roll decides whether the job is two-colour, not the accent setting.
|
|
// A DK-22251 roll refuses a monochrome job even when the badge has no red
|
|
// on it, so labelFor() is passed straight through and ql-print maps it.
|
|
const result = await printPng(png, {
|
|
host: site.printer_host,
|
|
port,
|
|
label: labelFor(site),
|
|
});
|
|
|
|
note(site.id, result.ok, result.message);
|
|
if (!result.ok) throw new Error(result.message);
|
|
|
|
// confirmed is false when the printer accepted the bytes but never said the
|
|
// label came out. Over the network that is the normal case, not a fault.
|
|
return { ok: true, confirmed: Boolean(result.confirmed), target };
|
|
}`;
|
|
|
|
source =
|
|
source.slice(0, printBadge.start) + newPrintBadge + source.slice(printBadge.end);
|
|
changes.push('printBadge now uses printPng');
|
|
|
|
/* -- 3. Replace available ----------------------------------------- */
|
|
|
|
const available = extractFunction(source, 'export function available()');
|
|
if (available) {
|
|
const newAvailable = `export function available() {
|
|
// Kept for callers that only ask "can this server print at all?". The
|
|
// external brother_ql binary is no longer involved, so the answer is always
|
|
// yes; use health(site) to ask about a specific printer.
|
|
return Promise.resolve(true);
|
|
}
|
|
|
|
/**
|
|
* Reachability and, where the printer will say, roll state.
|
|
*
|
|
* ready is tri-state: true, false, or null meaning "reachable but it won't
|
|
* tell us". Null is the normal answer over the network — show it as unknown
|
|
* in the admin console rather than green or red.
|
|
*/
|
|
export function health(site) {
|
|
return printerHealth({
|
|
host: site?.printer_host,
|
|
port: Number(site?.printer_port) || 9100,
|
|
label: labelFor(site),
|
|
});
|
|
}`;
|
|
|
|
source =
|
|
source.slice(0, available.start) + newAvailable + source.slice(available.end);
|
|
changes.push('available() no longer probes for a binary; added health(site)');
|
|
} else {
|
|
console.warn(' note: available() not found, skipping that step');
|
|
}
|
|
|
|
/* -- 4. Flag the now-dead subprocess code ------------------------- */
|
|
|
|
for (const name of ['function explainPrintError', 'function runBrotherQl']) {
|
|
if (source.includes(name)) {
|
|
changes.push(`${name.split(' ')[1]}() is now unused and can be deleted`);
|
|
}
|
|
}
|
|
|
|
/* -- Report / write ----------------------------------------------- */
|
|
|
|
console.log('\n Changes to src/printer.js:');
|
|
for (const c of changes) console.log(` - ${c}`);
|
|
|
|
if (checkOnly) {
|
|
console.log('\n --check given, nothing written.\n');
|
|
process.exit(0);
|
|
}
|
|
|
|
if (source === original) fail('nothing actually changed');
|
|
|
|
fs.writeFileSync(`${target}.bak`, original);
|
|
fs.writeFileSync(target, source);
|
|
|
|
console.log(`\n Wrote ${target}`);
|
|
console.log(` Backup at ${target}.bak`);
|
|
console.log('\n Next: docker compose build && docker compose up -d\n');
|