Skip to content

Commit

Permalink
Migrated README.md images to .docs folder. Moved print logic to own m…
Browse files Browse the repository at this point in the history
…odule. 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
  • Loading branch information
glenndehaan committed Aug 30, 2024
1 parent d6b2071 commit 3c21553
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 108 deletions.
Binary file added .docs/images/desktop_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/desktop_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/desktop_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/desktop_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/email_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/escpos_example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/mobile_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/mobile_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/mobile_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/mobile_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .docs/images/pdf_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: ''
Expand Down
236 changes: 236 additions & 0 deletions modules/print.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
}
};
Loading

0 comments on commit 3c21553

Please sign in to comment.