Files
jtlsrv/server.js
2026-06-21 09:09:06 +02:00

92 lines
2.9 KiB
JavaScript

const fs = require('node:fs');
const https = require('node:https');
const path = require('node:path');
const { createStore, createJtlPosServer, createAdminServer } = require('./lib/src');
const actions = require('./lib/src/actions');
const PORT = Number(process.env.PORT) || 8086;
const ADMIN_PORT = Number(process.env.ADMIN_PORT) || 8087;
const AUTH_TOKEN =
process.env.AUTH_TOKEN || '9a2e3036ed9c47e389741d9dbb7590e9';
const PAIRING_CODE = process.env.PAIRING_CODE || '307018';
const certsDir = path.join(__dirname, 'certs');
const keyPath = path.join(certsDir, 'key.pem');
const certPath = path.join(certsDir, 'cert.pem');
if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
console.error('Missing TLS certificate. Run: npm run cert');
process.exit(1);
}
function serverTimestamp() {
const d = new Date();
const pad = (n) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
function formatBody(buffer) {
if (!buffer.length) {
return '(empty)';
}
const text = buffer.toString('utf8');
if (/^[\x09\x0A\x0D\x20-\x7E\u0080-\uFFFF]*$/.test(text)) {
return text;
}
return `[binary ${buffer.length} bytes]\n${buffer.toString('hex')}`;
}
const store = createStore();
// Pre-register the fixed pairing code so the POS can pair out of the box.
store.dispatch(actions.setPairingCode(PAIRING_CODE, 'JTL-POS'));
// Treat the fixed auth token as a pre-paired device for display in the admin UI.
store.dispatch(actions.registerDevice(AUTH_TOKEN, 'JTL-POS'));
const jtlHandler = createJtlPosServer(store, { authToken: AUTH_TOKEN });
const loggedJtlHandler = async (req, res) => {
const started = Date.now();
const originalEnd = res.end.bind(res);
console.log('\n--- incoming POS request ---');
console.log(`${req.method} ${req.url}`);
console.log('remote:', req.socket.remoteAddress, req.socket.remotePort);
console.log('headers:', JSON.stringify(req.headers, null, 2));
await jtlHandler(req, res);
// req.rawBody is attached by jtlHandler's body reader
console.log('body:', formatBody(req.rawBody ?? Buffer.alloc(0)));
console.log(`responded ${res.statusCode} in ${Date.now() - started}ms`);
};
const httpsServer = https.createServer(
{
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath),
},
loggedJtlHandler
);
httpsServer.listen(PORT, '0.0.0.0', () => {
console.log(`HTTPS POS server listening on https://0.0.0.0:${PORT}`);
console.log(`Certificate: ${certPath}`);
console.log('Import cert.pem into JTL POS / Windows trust store if required.');
});
const { server: adminServer } = createAdminServer(store, {
adminPort: ADMIN_PORT,
staticDir: path.join(__dirname, 'web', 'dist'),
});
function shutdown() {
console.log('\nShutting down...');
httpsServer.close();
adminServer.close();
}
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);