diff --git a/.docs/images/desktop_0.png b/.docs/images/desktop_0.png new file mode 100644 index 0000000..35a2ad3 Binary files /dev/null and b/.docs/images/desktop_0.png differ diff --git a/.docs/images/desktop_1.png b/.docs/images/desktop_1.png new file mode 100644 index 0000000..bb0f98d Binary files /dev/null and b/.docs/images/desktop_1.png differ diff --git a/.docs/images/desktop_2.png b/.docs/images/desktop_2.png new file mode 100644 index 0000000..dd7a3b9 Binary files /dev/null and b/.docs/images/desktop_2.png differ diff --git a/.docs/images/desktop_3.png b/.docs/images/desktop_3.png new file mode 100644 index 0000000..8b681a7 Binary files /dev/null and b/.docs/images/desktop_3.png differ diff --git a/.docs/images/email_example.png b/.docs/images/email_example.png new file mode 100644 index 0000000..00eb93c Binary files /dev/null and b/.docs/images/email_example.png differ diff --git a/.docs/images/escpos_example.jpg b/.docs/images/escpos_example.jpg new file mode 100644 index 0000000..9a897df Binary files /dev/null and b/.docs/images/escpos_example.jpg differ diff --git a/.docs/images/mobile_0.png b/.docs/images/mobile_0.png new file mode 100644 index 0000000..b48ccc0 Binary files /dev/null and b/.docs/images/mobile_0.png differ diff --git a/.docs/images/mobile_1.png b/.docs/images/mobile_1.png new file mode 100644 index 0000000..99481f2 Binary files /dev/null and b/.docs/images/mobile_1.png differ diff --git a/.docs/images/mobile_2.png b/.docs/images/mobile_2.png new file mode 100644 index 0000000..72a26fc Binary files /dev/null and b/.docs/images/mobile_2.png differ diff --git a/.docs/images/mobile_3.png b/.docs/images/mobile_3.png new file mode 100644 index 0000000..f780b8b Binary files /dev/null and b/.docs/images/mobile_3.png differ diff --git a/.docs/images/pdf_example.png b/.docs/images/pdf_example.png new file mode 100644 index 0000000..fc5a905 Binary files /dev/null and b/.docs/images/pdf_example.png differ diff --git a/README.md b/README.md index 4c1b0c0..b4725d2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ UniFi Voucher Site is a web-based platform for generating and managing UniFi net [![Image Size](https://img.shields.io/docker/image-size/glenndehaan/unifi-voucher-site)](https://hub.docker.com/r/glenndehaan/unifi-voucher-site) -![Vouchers Overview - Desktop](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/b0d5c208-2ac7-444e-977d-31287ff19e8b) +![Vouchers Overview - Desktop](.docs/images/desktop_1.png) > Upgrading from 2.x to 3.x? Please take a look at the [migration guide](#migration-from-2x-to-3x) @@ -27,6 +27,7 @@ UniFi Voucher Site is a web-based platform for generating and managing UniFi net - TailwindCSS - NodeMailer - PDFKit +- Node Thermal Printer ## Prerequisites @@ -94,8 +95,10 @@ services: SERVICE_WEB: 'true' # Enable/disable the API SERVICE_API: 'false' - # Enable/disable the printer and set the preferred type, currently supported types: pdf + # Enable/disable the printer and set the preferred type, currently supported types: pdf, escpos PRINTER_TYPE: '' + # IP address to your network enabled ESC/POS compatible printer (Only required when using PRINTER_TYPE: 'escpos') + PRINTER_IP: '192.168.1.1' # SMTP Mail from email address (optional) SMTP_FROM: '' # SMTP Mail server hostname/ip (optional) @@ -273,21 +276,46 @@ To enable the print feature, you need to set the following environment variables ```env PRINTER_TYPE: '' +PRINTER_IP: '' ``` Here’s what each variable represents: -- **`PRINTER_TYPE`**: Sets the printer type used by UniFi Voucher Site. Currently supported options: pdf +- **`PRINTER_TYPE`**: Sets the printer type used by UniFi Voucher Site. Supported options: + - `pdf`: For generating PDF files formatted for 80mm paper width. + - `escpos`: For printing directly to network-enabled ESC/POS compatible printers. + +- **`PRINTER_IP`**: Specifies the IP address of the network-enabled ESC/POS printer. This variable is only required when `PRINTER_TYPE` is set to `escpos`. ### Usage -Once your 80mm receipt printer is configured and connected, you can easily print vouchers directly from the UniFi Voucher Site application. Simply navigate to the voucher within the interface and click on the "Print" button. +#### PDF + +If you're using the PDF option, once your 80mm receipt printer is configured and connected to your local client, you can easily export vouchers to pdf from the UniFi Voucher Site application. Simply navigate to the voucher within the interface and click on the "Print" button. The application will automatically format the voucher for 80mm paper width, ensuring optimal printing results. Depending on your printer settings and preferences, you may adjust print quality, paper type, and other printing parameters to suit your needs. -### Example Print PDF +##### Example PDF + +![Example PDF](.docs/images/pdf_example.png) + +#### ESC/POS + +For network-enabled ESC/POS compatible printers, set the `PRINTER_TYPE` to `escpos` and provide the printer's IP address in the `PRINTER_IP` variable. Once configured, you can print vouchers directly to your network printer from the UniFi Voucher Site application. + +Just like with PDF printing, navigate to the voucher and click on the "Print" button. The application will send the print job directly to the ESC/POS printer over the network, ensuring quick and seamless voucher printing. Make sure your printer supports ESC/POS commands and is correctly configured to accept print jobs over the network. + +##### Tested Printers + +- EPSON TM-T88V +- EPSON TM-T20X +- EPSON TM-T82IIIL +- Posman BTP-R880NP +- NetumScan NT-8360 / 80-V + +##### Example Print -![Example Print PDF](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/e86d0789-47d2-4630-a7fe-291a4fa9502f) +![Example Print](.docs/images/escpos_example.jpg) ## Email Functionality @@ -323,7 +351,7 @@ Once the SMTP environment variables are configured, the email feature will be av ### Example Email -![Example Email](https://github.com/user-attachments/assets/45615db3-df76-48b0-ad30-05236e3754c1) +![Example Email](.docs/images/email_example.png) ## OpenID Connect (OIDC) Authentication @@ -416,28 +444,28 @@ Below is a list of tested Identity Providers (IdPs) with detailed integration in ## Screenshots ### Login (Desktop) -![Login - Desktop](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/5f89ecbd-7e03-4fd0-ae7d-279d16321384) +![Login - Desktop](.docs/images/desktop_0.png) ### Vouchers Overview (Desktop) -![Vouchers Overview - Desktop](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/b0d5c208-2ac7-444e-977d-31287ff19e8b) +![Vouchers Overview - Desktop](.docs/images/desktop_1.png) ### Create Voucher (Desktop) -![Create Voucher - Desktop](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/72f8dcf0-6642-4c89-849f-21cfbcc488ab) +![Create Voucher - Desktop](.docs/images/desktop_2.png) ### Voucher Details (Desktop) -![Voucher Details - Desktop](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/b84ad74c-afaa-4bf1-8bc1-398fb0450ff1) +![Voucher Details - Desktop](.docs/images/desktop_3.png) ### Login (Mobile) -![Login - Mobile](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/d74bc487-5b80-4bb6-8617-da870cdf4cec) +![Login - Mobile](.docs/images/mobile_0.png) ### Vouchers Overview (Mobile) -![Voucher Overview - Mobile](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/c986e03d-5edf-4b04-8903-0b42ff1c4fc9) +![Voucher Overview - Mobile](.docs/images/mobile_1.png) ### Create Voucher (Mobile) -![Create Voucher - Mobile](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/f1cef8c8-a7a5-4238-8a2e-461835375f29) +![Create Voucher - Mobile](.docs/images/mobile_2.png) ### Voucher Details (Mobile) -![Voucher Details - Mobile](https://github.com/glenndehaan/unifi-voucher-site/assets/7496187/28b8f97b-8042-4e6d-b1dc-8386860a1e39) +![Voucher Details - Mobile](.docs/images/mobile_3.png) ## Migration Guide diff --git a/docker-compose.yml b/docker-compose.yml index 54f0628..31c7d1f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ services: SERVICE_WEB: 'true' SERVICE_API: 'false' PRINTER_TYPE: '' + PRINTER_IP: '192.168.1.1' SMTP_FROM: '' SMTP_HOST: '' SMTP_PORT: '' diff --git a/modules/print.js b/modules/print.js new file mode 100644 index 0000000..ff91728 --- /dev/null +++ b/modules/print.js @@ -0,0 +1,236 @@ +/** + * Import base packages + */ +const PDFDocument = require('pdfkit'); +const ThermalPrinter = require('node-thermal-printer').printer; +const PrinterTypes = require('node-thermal-printer').types; + +/** + * Import own modules + */ +const log = require('./log'); +const config = require('./config'); +const time = require('../utils/time'); +const bytes = require('../utils/bytes'); +const size = require('../utils/size'); + +/** + * Define global variables + */ +const printerIp = config('printer_ip') || process.env.PRINTER_IP || '192.168.1.1'; + +/** + * Exports the printer module + */ +module.exports = { + /** + * Generates a voucher as a PDF + * + * @param voucher + * @return {Promise} + */ + pdf: (voucher) => { + return new Promise((resolve) => { + const doc = new PDFDocument({ + bufferPages: true, + size: [226.77165354330398, size(voucher)], + margins : { + top: 20, + bottom: 20, + left: 20, + right: 20 + } + }); + + const buffers = []; + doc.on('data', buffers.push.bind(buffers)); + doc.on('end', () => { + log.info('[Printer] PDF generation completed!'); + resolve(buffers); + }); + + doc.image('public/images/logo_grayscale_dark.png', 75, 15, {fit: [75, 75], align: 'center', valign: 'center'}); + + doc.moveDown(6); + + doc.font('Helvetica-Bold') + .fontSize(20) + .text(`WiFi Voucher Code`, { + align: 'center' + }); + doc.font('Helvetica-Bold') + .fontSize(15) + .text(`${voucher.code.slice(0, 5)}-${voucher.code.slice(5)}`, { + align: 'center' + }); + + doc.moveDown(2); + + doc.font('Helvetica-Bold') + .fontSize(12) + .text(`Voucher Details`); + + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`--------------------------------------------------------`); + + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`Type: `, { + continued: true + }); + doc.font('Helvetica') + .fontSize(10) + .text(voucher.quota === 0 ? 'Multi-use' : 'Single-use'); + + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`Duration: `, { + continued: true + }); + doc.font('Helvetica') + .fontSize(10) + .text(time(voucher.duration)); + + if(voucher.qos_usage_quota) { + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`Data Limit: `, { + continued: true + }); + doc.font('Helvetica') + .fontSize(10) + .text(`${bytes(voucher.qos_usage_quota, 2)}`); + } + + if(voucher.qos_rate_max_down) { + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`Download Limit: `, { + continued: true + }); + doc.font('Helvetica') + .fontSize(10) + .text(`${bytes(voucher.qos_rate_max_down, 1, true)}`); + } + + if(voucher.qos_rate_max_up) { + doc.font('Helvetica-Bold') + .fontSize(10) + .text(`Upload Limit: `, { + continued: true + }); + doc.font('Helvetica') + .fontSize(10) + .text(`${bytes(voucher.qos_rate_max_up, 1, true)}`); + } + + doc.end(); + }); + }, + + /** + * Sends a print job to an ESC/POS compatible network printer + * + * @param voucher + * @return {Promise} + */ + escpos: (voucher) => { + return new Promise(async (resolve, reject) => { + const printer = new ThermalPrinter({ + type: PrinterTypes.EPSON, + interface: `tcp://${printerIp}` + }); + + const status = await printer.isPrinterConnected(); + + if(!status) { + reject('Unable to connect to printer!'); + return; + } + + printer.setTypeFontB(); + printer.alignCenter(); + printer.newLine(); + await printer.printImage(`${process.cwd()}/public/images/logo_grayscale_dark.png`); + printer.newLine(); + + printer.alignCenter(); + printer.newLine(); + printer.setTextSize(2, 2); + printer.println('WiFi Voucher Code'); + printer.setTextSize(1, 1); + printer.println(`${voucher.code.slice(0, 5)}-${voucher.code.slice(5)}`); + printer.setTextNormal(); + + printer.newLine(); + printer.newLine(); + printer.newLine(); + printer.newLine(); + printer.newLine(); + + printer.alignLeft(); + printer.setTypeFontB(); + printer.setTextSize(1, 1); + printer.println('Voucher Details'); + printer.setTextNormal(); + printer.drawLine(); + + printer.setTextDoubleHeight(); + printer.invert(true); + printer.print('Type:'); + printer.invert(false); + printer.print(voucher.quota === 0 ? ' Multi-use' : ' Single-use'); + printer.newLine(); + + printer.setTextDoubleHeight(); + printer.invert(true); + printer.print('Duration:'); + printer.invert(false); + printer.print(` ${time(voucher.duration)}`); + printer.newLine(); + + if(voucher.qos_usage_quota) { + printer.setTextDoubleHeight(); + printer.invert(true); + printer.print('Data Limit:'); + printer.invert(false); + printer.print(` ${bytes(voucher.qos_usage_quota, 2)}`); + printer.newLine(); + } + + if(voucher.qos_rate_max_down) { + printer.setTextDoubleHeight(); + printer.invert(true); + printer.print('Download Limit:'); + printer.invert(false); + printer.print(` ${bytes(voucher.qos_rate_max_down, 1, true)}`); + printer.newLine(); + } + + if(voucher.qos_rate_max_up) { + printer.setTextDoubleHeight(); + printer.invert(true); + printer.print('Upload Limit:'); + printer.invert(false); + printer.print(` ${bytes(voucher.qos_rate_max_up, 1, true)}`); + printer.newLine(); + } + + printer.newLine(); + printer.newLine(); + printer.newLine(); + printer.newLine(); + printer.cut(); + printer.beep(2, 2); + + try { + await printer.execute(); + log.info('[Printer] Data send to printer!'); + resolve(true); + } catch (error) { + reject(error); + } + }); + } +}; diff --git a/package-lock.json b/package-lock.json index f7b5cc8..cc758b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "js-logger": "^1.6.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", + "node-thermal-printer": "^4.4.3", "node-unifi": "^2.5.1", "nodemailer": "^6.9.14", "pdfkit": "^0.15.0" @@ -931,6 +932,12 @@ "node": ">=4" } }, + "node_modules/dank-do-while": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dank-do-while/-/dank-do-while-0.1.2.tgz", + "integrity": "sha512-pyMrgXfugjvdernLCcXNixYqGijUZzYxrb+hAeED2EBpNbctCCxdA/O6NIQXn6vIjwzY1IMmV29d2Y6vaayrwQ==", + "license": "MIT" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2555,6 +2562,30 @@ "node": ">= 0.6" } }, + "node_modules/node-thermal-printer": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/node-thermal-printer/-/node-thermal-printer-4.4.3.tgz", + "integrity": "sha512-avpfLcNRxn+rhhDcqho1Q+0SUZo7ZCq3IGXAqXeVf0NBEZJUV7bzjIqU/qDCl+LgOBZIQtsmEg8oCFxzA+ojDA==", + "license": "ISC", + "dependencies": { + "iconv-lite": "0.5.0", + "pngjs": "3.3.3", + "unorm": "1.4.1", + "write-file-queue": "0.0.1" + } + }, + "node_modules/node-thermal-printer/node_modules/iconv-lite": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", + "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/node-unifi": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/node-unifi/-/node-unifi-2.5.1.tgz", @@ -2926,6 +2957,15 @@ "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" }, + "node_modules/pngjs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", + "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -3822,6 +3862,15 @@ "node": ">= 4.0.0" } }, + "node_modules/unorm": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", + "integrity": "sha512-ZKduSr7qaZXq/R784+M3vudsguIahTX9aVtlfJdMaQj/yQw7A8LhkaS76/a6d0TzeBtiMDl+clvrLxq6HG/nAA==", + "license": "MIT or GPL-2.0", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3994,6 +4043,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/write-file-queue": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/write-file-queue/-/write-file-queue-0.0.1.tgz", + "integrity": "sha512-uUInKYzJjhvZ5UJA27tsqbw6s9EEpwe6r4mdpN95t2E4aKzJEJbZg2EvIleTGaja+xtZYx376Jd2WWu5LEQP0w==", + "license": "MIT", + "dependencies": { + "dank-do-while": "^0.1.2" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", diff --git a/package.json b/package.json index 4928320..7c8746e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "js-logger": "^1.6.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", + "node-thermal-printer": "^4.4.3", "node-unifi": "^2.5.1", "nodemailer": "^6.9.14", "pdfkit": "^0.15.0" diff --git a/public/images/logo_grayscale_dark.png b/public/images/logo_grayscale_dark.png new file mode 100644 index 0000000..491a4aa Binary files /dev/null and b/public/images/logo_grayscale_dark.png differ diff --git a/server.js b/server.js index 5adb7f6..0ca7d25 100644 --- a/server.js +++ b/server.js @@ -6,7 +6,6 @@ const os = require('os'); const express = require('express'); const multer = require('multer'); const cookieParser = require('cookie-parser'); -const PDFDocument = require('pdfkit'); /** * Import own modules @@ -20,6 +19,7 @@ const types = require('./utils/types'); const time = require('./utils/time'); const bytes = require('./utils/bytes'); const unifi = require('./modules/unifi'); +const print = require('./modules/print'); const mail = require('./modules/mail'); const oidc = require('./modules/oidc'); @@ -48,6 +48,7 @@ const webService = process.env.SERVICE_WEB ? process.env.SERVICE_WEB !== 'false' const apiService = config('service_api') || (process.env.SERVICE_API === 'true') || false; const authDisabled = (process.env.AUTH_DISABLE === 'true') || false; const printerType = config('printer_type') || process.env.PRINTER_TYPE || ''; +const printerIp = config('printer_ip') || process.env.PRINTER_IP || '192.168.1.1'; const smtpFrom = config('smtp_from') || process.env.SMTP_FROM || ''; const smtpHost = config('smtp_host') || process.env.SMTP_HOST || ''; const smtpPort = config('smtp_port') || process.env.SMTP_PORT || 25; @@ -97,12 +98,12 @@ log.info(`[Voucher][Custom] ${voucherCustom ? 'Enabled!' : 'Disabled!'}`); /** * Log auth status */ -log.info(`[Auth] ${authDisabled ? 'Disabled!' : 'Enabled!'}`); +log.info(`[Auth] ${authDisabled ? 'Disabled!' : `Enabled! Type: ${(oidcIssuerBaseUrl !== '' && oidcAppBaseUrl !== '' && oidcClientId !== '') ? 'OIDC' : 'Internal'}`}`); /** * Log printer status */ -log.info(`[Printer] ${printerType !== '' ? `Enabled! Type: ${printerType}` : 'Disabled!'}`); +log.info(`[Printer] ${printerType !== '' ? `Enabled! Type: ${printerType}${printerType === 'escpos' ? `, IP: ${printerIp}` : ''}` : 'Disabled!'}`); /** * Log email status @@ -297,105 +298,25 @@ if(webService) { }); if(voucher) { - const doc = new PDFDocument({ - bufferPages: true, - size: [226.77165354330398, 290], - margins : { - top: 20, - bottom: 20, - left: 20, - right: 20 - } - }); - - const buffers = []; - doc.on('data', buffers.push.bind(buffers)); - doc.on('end', () => { - let pdfData = Buffer.concat(buffers); + if(printerType === 'pdf') { + const buffers = await print.pdf(voucher); + const pdfData = Buffer.concat(buffers); res.writeHead(200, { 'Content-Length': Buffer.byteLength(pdfData), 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment;filename=voucher_${req.params.id}.pdf` }).end(pdfData); - }); - - doc.image('public/images/logo_grayscale.png', 75, 15, {fit: [75, 75], align: 'center', valign: 'center'}); - - doc.moveDown(6); - - doc.font('Helvetica-Bold') - .fontSize(20) - .text(`WiFi Voucher Code`, { - align: 'center' - }); - doc.font('Helvetica-Bold') - .fontSize(15) - .text(`${voucher.code.slice(0, 5)}-${voucher.code.slice(5)}`, { - align: 'center' - }); - - doc.moveDown(2); - - doc.font('Helvetica-Bold') - .fontSize(12) - .text(`Voucher Details`); - - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`--------------------------------------------------------`); - - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`Type: `, { - continued: true - }); - doc.font('Helvetica') - .fontSize(10) - .text(voucher.quota === 0 ? 'Multi-use' : 'Single-use'); - - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`Duration: `, { - continued: true - }); - doc.font('Helvetica') - .fontSize(10) - .text(time(voucher.duration)); - - if(voucher.qos_usage_quota) { - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`Data Limit: `, { - continued: true - }); - doc.font('Helvetica') - .fontSize(10) - .text(`${bytes(voucher.qos_usage_quota, 2)}`); } - if(voucher.qos_rate_max_down) { - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`Download Limit: `, { - continued: true - }); - doc.font('Helvetica') - .fontSize(10) - .text(`${bytes(voucher.qos_rate_max_down, 1, true)}`); - } + if(printerType === 'escpos') { + const printResult = await print.escpos(voucher).catch((e) => { + res.cookie('flashMessage', JSON.stringify({type: 'error', message: e}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, `${req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : ''}/vouchers`); + }); - if(voucher.qos_rate_max_up) { - doc.font('Helvetica-Bold') - .fontSize(10) - .text(`Upload Limit: `, { - continued: true - }); - doc.font('Helvetica') - .fontSize(10) - .text(`${bytes(voucher.qos_rate_max_up, 1, true)}`); + if(printResult) { + res.cookie('flashMessage', JSON.stringify({type: 'info', message: `Voucher send to printer!`}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, `${req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : ''}/vouchers`); + } } - - doc.end(); } else { res.status(404); res.render('404', { diff --git a/utils/size.js b/utils/size.js new file mode 100644 index 0000000..436a24e --- /dev/null +++ b/utils/size.js @@ -0,0 +1,20 @@ +/** + * Util function to calculate paper size based on voucher data + */ +module.exports = (voucher) => { + let base = 260; + + if(voucher.qos_usage_quota) { + base += 10; + } + + if(voucher.qos_rate_max_down) { + base += 10; + } + + if(voucher.qos_rate_max_up) { + base += 10; + } + + return base; +}