UNPKG

parallel-port-printer

Version:

A Node.js application for using parallel port printers with ECS/POS commands.

216 lines (188 loc) 7.39 kB
const express = require('express'); const { exec } = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); const iconv = require('iconv-lite'); const updateChecker = require('./check-update'); // 設定 .env 檔案路徑到 AppData 目錄 const envFilePath = path.join( os.homedir(), 'AppData', 'Local', 'ParallelPortPrinter', '.env' ); // 載入 .env 檔案 require('dotenv').config({ path: envFilePath }); const app = express(); const LISTEN_PORT = process.env.LISTEN_PORT || 3000; const escResetStyle = Buffer.from([0x1B, 0x45, 0x00, 0x1D, 0x21, 0x00]); const escInit = Buffer.from([0x1B, 0x40]); // 初始化 const escBoldOn = Buffer.from([0x1B, 0x45, 0x01]); // 加粗開啟 const escBoldOff = Buffer.from([0x1B, 0x45, 0x00]); // 加粗關閉 const escFontA = Buffer.from([0x1B, 0x4D, 0x00]); // 字型 A const escFontB = Buffer.from([0x1B, 0x4D, 0x01]); // 字型 B const escDouble = Buffer.from([0x1D, 0x21, 0x11]); // 雙倍高寬 const escDouble_off = Buffer.from([0x1D, 0x21, 0x00]); // 雙倍高寬關閉 const escAlignRight = Buffer.from([0x1B, 0x61, 0x02]); // 置右對齊 const escAlignJustify = Buffer.from([0x1B, 0x61, 0x03]); // 兩端對齊 const escAlignCenter = Buffer.from([0x1B, 0x61, 0x01]); // 置中對齊 const escAlignLeft = Buffer.from([0x1B, 0x61, 0x00]); // 左對齊 const escCut = Buffer.from([0x1D, 0x56, 0x00]); // 全切紙 const escCutPartial = Buffer.from([0x1D, 0x56, 0x01]); // 半切紙 const escLineFeed = Buffer.from([0x0A]); // 換行 // CORS configuration app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); }); app.use(express.json()); app.use(express.static(__dirname)); /*app.use(express.json({ limit: '1mb' })); // 限制請求大小 app.use(express.static(__dirname, { dotfiles: 'ignore', etag: true, index: 'index.html', // 指定預設檔案為 index.html maxAge: '1h' }));*/ // Input validation middleware const validatePort = (req, res, next) => { const { port } = req.body; if (!port || !['LPT1', 'LPT2'].includes(port)) { return res.status(400).json({ error: 'Invalid printer port' }); } next(); }; // API Routes // Read settings app.get('/settings', (req, res) => { res.json({ PRINTER_PORT: process.env.PRINTER_PORT || 'LPT1' }); }); // Save settings app.post('/settings', validatePort, (req, res) => { const { port } = req.body; const envContent = `PRINTER_PORT=${port}`; try { fs.writeFileSync(envFilePath, envContent, { encoding: 'utf8' }); process.env.PRINTER_PORT = port; res.json({ message: 'Settings saved successfully' }); } catch (error) { console.error('Error saving settings:', error); res.status(500).json({ error: 'Failed to save settings' }); } }); // Handle AirControl Agent requests app.post('/inform', (req, res) => { res.status(200).send('OK'); }); // Command validation middleware const validateCommand = (req, res, next) => { const { type, text, count, port } = req.body; if (!type || !['printLine', 'newline', 'cut'].includes(type)) { return res.status(400).json({ error: 'Invalid command type' }); } if (type === 'printLine' && (!text || typeof text !== 'string' || text.length > 1000)) { return res.status(400).json({ error: 'Invalid text content' }); } if (type === 'newline' && (count && (isNaN(count) || count < 1 || count > 50))) { return res.status(400).json({ error: 'Invalid line count' }); } if (port && !['LPT1', 'LPT2'].includes(port)) { return res.status(400).json({ error: 'Invalid port' }); } next(); }; // 檢查印表機狀態 function checkPrinterStatus(port) { return new Promise((resolve) => { const command = `mode ${port}`; exec(command, { encoding: 'buffer' }, (error, stdout, stderr) => { if (error) { console.error('Error querying printer status:', error); console.error('Error output:', stderr); const errorMessage = iconv.decode(stderr, 'cp950'); console.error('Printer status check error:', errorMessage); resolve(false); } else { resolve(true); } }); }); } app.post('/command', validateCommand, async (req, res) => { const { type, text, count, port } = req.body; const targetPort = port || process.env.PRINTER_PORT || 'LPT1'; const realPort = '\\\\.\\' + targetPort; // 檢查印表機狀態 const isPrinterReady = await checkPrinterStatus(targetPort); if (!isPrinterReady) { return res.json({ status: 'error', message: 'Printer is not ready. Please check if the printer is turned on and connected.' }); } let buffer; try { switch (type) { case 'printLine': buffer = iconv.encode(text + '\n', 'big5'); break; case 'newline': const n = Math.max(1, Math.min(parseInt(count) || 1, 50)); buffer = Buffer.from('\n'.repeat(n)); break; case 'cut': buffer = Buffer.from([0x1D, 0x56, 0x00]); // ESC/POS Full cut break; } } catch (error) { console.error('Error preparing buffer:', error); return res.status(500).json({ error: 'Failed to prepare print data' }); } const tryWrite = () => { // 將 buffer 直接寫入 LPT 埠 try { const lptStream = fs.createWriteStream(realPort, { flags: 'w' }); lptStream.write(buffer); lptStream.end(); lptStream.on('finish', () => { res.json({ message: `Sent to ${targetPort}, command type: ${type}` }); }); lptStream.on('error', (error) => { console.error('Error writing to LPT port:', error); res.status(500).json({ error: 'Transmission failed: Please check LPT Port settings' }); }); } catch (error) { console.error('Error writing to LPT port:', error); res.status(500).json({ error: 'Transmission failed: Please check LPT Port settings' }); } }; tryWrite(); }); app.post('/printer-status', async (req, res) => { const { port } = req.body; const targetPort = port || process.env.PRINTER_PORT || 'LPT1'; const isPrinterReady = await checkPrinterStatus(targetPort); if (!isPrinterReady) { return res.json({ status: 'error', message: 'Printer is not ready. Please check if the printer is turned on and connected.' }); } res.json({ status: 'success', message: 'Printer is ready' }); }); // 確保所有其他路由都返回 index.html app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); // Error handling middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ error: 'Something went wrong!' }); }); app.listen(LISTEN_PORT, () => { console.log(`\x1b[31mParallel Port Printer Server\x1b[0m running at: http://localhost:${LISTEN_PORT}`); console.log('\x1b[33m請不要關閉這個視窗! 如果您想停止伺服器,請使用 Ctrl+C。\x1b[0m'); console.log(`\x1b[32mLoaded PRINTER_PORT from .env: ${process.env.PRINTER_PORT}\x1b[0m`); // Start update checker updateChecker.startUpdateCheck(); });