#!/usr/bin/env node /** * Generate a pairing QR code for JTL-POS. * * Usage: * node scripts/create-pairing-qr.mjs * npm run qr -- 192.168.1.50 * * Reads PORT / PAIRING_CODE from .env and the TLS cert from certs/cert.pem. */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import dotenv from 'dotenv'; import QRCode from 'qrcode'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); dotenv.config({ path: path.join(root, '.env') }); const host = process.argv[2]; if (!host) { console.error('Usage: node scripts/create-pairing-qr.mjs '); process.exit(1); } const port = String(process.env.PORT || '4443'); const pin = String(process.env.PAIRING_CODE || '307018'); const certPath = path.join(root, 'certs', 'cert.pem'); // Compact: base64 DER only (no PEM headers / line wraps) — client can re-wrap if needed const cert = fs .readFileSync(certPath, 'utf8') .replace(/-----BEGIN CERTIFICATE-----/g, '') .replace(/-----END CERTIFICATE-----/g, '') .replace(/\s+/g, ''); const payload = { v: 1, host, port, pin, cert, }; const content = JSON.stringify(payload); const outDir = path.join(root, 'certs'); const pngPath = path.join(outDir, 'pairing-qr.png'); const jsonPath = path.join(outDir, 'pairing-qr.json'); const qrOpts = { errorCorrectionLevel: 'L', // ~7% recovery — smallest size for the payload margin: 1, }; const qr = QRCode.create(content, qrOpts); // Scale by module count so fewer modules actually looks smaller (not stretched to 512) const modulePx = 4; const width = qr.modules.size * modulePx; await QRCode.toFile(pngPath, content, { ...qrOpts, type: 'png', width, }); fs.writeFileSync(jsonPath, `${JSON.stringify(payload, null, 2)}\n`); console.log(await QRCode.toString(content, { type: 'terminal', small: true, ...qrOpts })); console.log(content); console.log(`host=${host} port=${port} pin=${pin}`); console.log(`QR version=${qr.version} modules=${qr.modules.size} png=${width}px`); console.log(`Wrote ${pngPath}`); console.log(`Wrote ${jsonPath}`);