Migrated README.md images to .docs folder. Moved print logic to own module. Implemented ESC/POS printer support. Added logo_grayscale_dark.png. Replaced PDF logo. Implemented size.js util for dynamic PDF page length. Updated README.md. Updated auth output log with type. Updated print output log with printer ip
BIN
.docs/images/desktop_0.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
.docs/images/desktop_1.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
.docs/images/desktop_2.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
.docs/images/desktop_3.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
.docs/images/email_example.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
.docs/images/escpos_example.jpg
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
.docs/images/mobile_0.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
.docs/images/mobile_1.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
.docs/images/mobile_2.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
.docs/images/mobile_3.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
.docs/images/pdf_example.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
58
README.md
@@ -4,7 +4,7 @@ UniFi Voucher Site is a web-based platform for generating and managing UniFi net
|
||||
|
||||
[](https://hub.docker.com/r/glenndehaan/unifi-voucher-site)
|
||||
|
||||

|
||||

|
||||
|
||||
> 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
|
||||
|
||||

|
||||

|
||||
|
||||
#### 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
|
||||
|
||||

|
||||
|
||||
## Email Functionality
|
||||
|
||||
@@ -323,7 +351,7 @@ Once the SMTP environment variables are configured, the email feature will be av
|
||||
|
||||
### Example Email
|
||||
|
||||

|
||||

|
||||
|
||||
## OpenID Connect (OIDC) Authentication
|
||||
|
||||
@@ -416,28 +444,28 @@ Below is a list of tested Identity Providers (IdPs) with detailed integration in
|
||||
## Screenshots
|
||||
|
||||
### Login (Desktop)
|
||||

|
||||

|
||||
|
||||
### Vouchers Overview (Desktop)
|
||||

|
||||

|
||||
|
||||
### Create Voucher (Desktop)
|
||||

|
||||

|
||||
|
||||
### Voucher Details (Desktop)
|
||||

|
||||

|
||||
|
||||
### Login (Mobile)
|
||||

|
||||

|
||||
|
||||
### Vouchers Overview (Mobile)
|
||||

|
||||

|
||||
|
||||
### Create Voucher (Mobile)
|
||||

|
||||

|
||||
|
||||
### Voucher Details (Mobile)
|
||||

|
||||

|
||||
|
||||
## Migration Guide
|
||||
|
||||
|
||||
@@ -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: ''
|
||||
|
||||
236
modules/print.js
Normal file
@@ -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<unknown>}
|
||||
*/
|
||||
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<unknown>}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
58
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
BIN
public/images/logo_grayscale_dark.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
107
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', {
|
||||
|
||||
20
utils/size.js
Normal file
@@ -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;
|
||||
}
|
||||