mirror of
https://github.com/glenndehaan/unifi-voucher-site.git
synced 2026-04-05 08:54:17 -04:00
Updated info.js for new printers key. Updated print.js to forward ESC/POS printer IPs. Updated keys within variables.js. Added 'UI_BACK_BUTTON', 'PRINTER_TYPE' and 'PRINTER_IP' to the deprecated strings array.js. Updated status.js with new printing module. Updated docker-compose.yml environment variables. Updated dependencies. Hotfix body type for express v5. Updated README.md. Removed back button from navigation.ejs. Updated print.ejs and bulk-print.ejs with printer selection. Updated print logic within server.js to allow printer selection by user
This commit is contained in:
56
README.md
56
README.md
@@ -6,7 +6,7 @@ UniFi Voucher Site is a web-based platform for generating and managing UniFi net
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
> Upgrading from 4.x to 5.x? Please take a look at the [migration guide](#migration-from-4x-to-5x)
|
> Upgrading from 5.x to 6.x? Please take a look at the [migration guide](#migration-from-5x-to-6x)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -108,10 +108,8 @@ services:
|
|||||||
SERVICE_WEB: 'true'
|
SERVICE_WEB: 'true'
|
||||||
# Enable/disable the API
|
# Enable/disable the API
|
||||||
SERVICE_API: 'false'
|
SERVICE_API: 'false'
|
||||||
# Enable/disable the printer and set the preferred type, currently supported types: pdf, escpos
|
# Enable/disable the printers and setup available printers, currently supported: pdf,escpos ip (Example: pdf,192.168.1.10)
|
||||||
PRINTER_TYPE: ''
|
PRINTERS: ''
|
||||||
# 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 Mail from email address (optional)
|
||||||
SMTP_FROM: ''
|
SMTP_FROM: ''
|
||||||
# SMTP Mail server hostname/ip (optional)
|
# SMTP Mail server hostname/ip (optional)
|
||||||
@@ -135,8 +133,6 @@ services:
|
|||||||
TRANSLATION_DEFAULT: 'en'
|
TRANSLATION_DEFAULT: 'en'
|
||||||
# Enables/disables translation debugging, when enabled only translation keys are shown
|
# Enables/disables translation debugging, when enabled only translation keys are shown
|
||||||
TRANSLATION_DEBUG: 'false'
|
TRANSLATION_DEBUG: 'false'
|
||||||
# Enables/disables a back-button next to the logo to go back 1 page in history (Could be used with multi-page kiosks)
|
|
||||||
UI_BACK_BUTTON: 'false'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Home Assistant Add-on
|
### Home Assistant Add-on
|
||||||
@@ -468,17 +464,20 @@ The print functionality is compatible with most 80mm thermal receipt printers co
|
|||||||
To enable the print feature, you need to set the following environment variables:
|
To enable the print feature, you need to set the following environment variables:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
PRINTER_TYPE: ''
|
PRINTERS: ''
|
||||||
PRINTER_IP: ''
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Here’s what each variable represents:
|
Here’s what each variable represents:
|
||||||
|
|
||||||
- **`PRINTER_TYPE`**: Sets the printer type used by UniFi Voucher Site. Supported options:
|
- **`PRINTERS`**: Sets the printer type used by UniFi Voucher Site. Supported options:
|
||||||
- `pdf`: For generating PDF files formatted for 80mm paper width.
|
- `pdf`: For generating PDF files formatted for 80mm paper width.
|
||||||
- `escpos`: For printing directly to network-enabled ESC/POS compatible printers.
|
- `escpos`: For printing directly to network-enabled ESC/POS compatible printers. Specify the IP address of the network-enabled ESC/POS printer
|
||||||
|
|
||||||
- **`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`.
|
> You can have multiple printers available at the same time.
|
||||||
|
> Let's say you have 2 ESC/POS network printers and want to print via pdf, then define:
|
||||||
|
> ```env
|
||||||
|
> PRINTERS: 'pdf,192.168.1.10,192.168.1.11'
|
||||||
|
> ```
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
@@ -494,7 +493,7 @@ The application will automatically format the voucher for 80mm paper width, ensu
|
|||||||
|
|
||||||
#### ESC/POS
|
#### 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.
|
For network-enabled ESC/POS compatible printers, provide the printer's IP address in the `PRINTERS` 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.
|
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.
|
||||||
|
|
||||||
@@ -647,6 +646,37 @@ Detailed information on the changes in each release can be found on the [GitHub
|
|||||||
|
|
||||||
## Migration Guide
|
## Migration Guide
|
||||||
|
|
||||||
|
### Migration from 5.x to 6.x
|
||||||
|
|
||||||
|
When upgrading from 5.x to 6.x, the following changes need to be made:
|
||||||
|
|
||||||
|
1. **`UI_BACK_BUTTON` Removed**
|
||||||
|
|
||||||
|
- The `UI_BACK_BUTTON` configuration option has been **removed** in 6.x.
|
||||||
|
- This setting is no longer used and can be safely **removed from your environment configuration**.
|
||||||
|
|
||||||
|
2. **Printer Configuration Changes**
|
||||||
|
|
||||||
|
- The legacy printer configuration options **`PRINTER_TYPE`** and **`PRINTER_IP`** have been replaced by a single setting: **`PRINTERS`**.
|
||||||
|
- The new format is a **comma-separated string** combining the printer type and IP address.
|
||||||
|
|
||||||
|
**Before (5.x):**
|
||||||
|
|
||||||
|
```env
|
||||||
|
PRINTER_TYPE='pdf'
|
||||||
|
PRINTER_IP='192.168.1.10'
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (6.x):**
|
||||||
|
|
||||||
|
```env
|
||||||
|
PRINTERS='pdf,192.168.1.10'
|
||||||
|
```
|
||||||
|
|
||||||
|
- Update your configuration to use `PRINTERS` and remove the old `PRINTER_TYPE` and `PRINTER_IP` variables.
|
||||||
|
|
||||||
|
> Make sure to clean up any deprecated variables and update your printer configuration to ensure compatibility with 6.x.
|
||||||
|
|
||||||
### Migration from 4.x to 5.x
|
### Migration from 4.x to 5.x
|
||||||
|
|
||||||
When upgrading from 4.x to 5.x, the following changes need to be made:
|
When upgrading from 4.x to 5.x, the following changes need to be made:
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ services:
|
|||||||
VOUCHER_CUSTOM: 'true'
|
VOUCHER_CUSTOM: 'true'
|
||||||
SERVICE_WEB: 'true'
|
SERVICE_WEB: 'true'
|
||||||
SERVICE_API: 'false'
|
SERVICE_API: 'false'
|
||||||
PRINTER_TYPE: ''
|
PRINTERS: ''
|
||||||
PRINTER_IP: '192.168.1.1'
|
|
||||||
SMTP_FROM: ''
|
SMTP_FROM: ''
|
||||||
SMTP_HOST: ''
|
SMTP_HOST: ''
|
||||||
SMTP_PORT: ''
|
SMTP_PORT: ''
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ module.exports = () => {
|
|||||||
/**
|
/**
|
||||||
* Log printer status
|
* Log printer status
|
||||||
*/
|
*/
|
||||||
log.info(`[Printer] ${variables.printerType !== '' ? `Enabled! Type: ${variables.printerType}${variables.printerType === 'escpos' ? `, IP: ${variables.printerIp}` : ''}` : 'Disabled!'}`);
|
log.info(`[Printers] ${variables.printers !== '' ? `Enabled! Available: ${variables.printers.split(',').join(', ')}` : 'Disabled!'}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log email status
|
* Log email status
|
||||||
|
|||||||
@@ -225,16 +225,17 @@ module.exports = {
|
|||||||
*
|
*
|
||||||
* @param voucher
|
* @param voucher
|
||||||
* @param language
|
* @param language
|
||||||
|
* @param ip
|
||||||
* @return {Promise<unknown>}
|
* @return {Promise<unknown>}
|
||||||
*/
|
*/
|
||||||
escpos: (voucher, language) => {
|
escpos: (voucher, language, ip) => {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
// Create new translator
|
// Create new translator
|
||||||
const t = translation('print', language);
|
const t = translation('print', language);
|
||||||
|
|
||||||
const printer = new ThermalPrinter({
|
const printer = new ThermalPrinter({
|
||||||
type: PrinterTypes.EPSON,
|
type: PrinterTypes.EPSON,
|
||||||
interface: `tcp://${variables.printerIp}`
|
interface: `tcp://${ip}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const status = await printer.isPrinterConnected();
|
const status = await printer.isPrinterConnected();
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ module.exports = {
|
|||||||
authOidcClientId: config('auth_oidc_client_id') || process.env.AUTH_OIDC_CLIENT_ID || '',
|
authOidcClientId: config('auth_oidc_client_id') || process.env.AUTH_OIDC_CLIENT_ID || '',
|
||||||
authOidcClientSecret: config('auth_oidc_client_secret') || process.env.AUTH_OIDC_CLIENT_SECRET || '',
|
authOidcClientSecret: config('auth_oidc_client_secret') || process.env.AUTH_OIDC_CLIENT_SECRET || '',
|
||||||
authDisabled: config('auth_disable') || (process.env.AUTH_DISABLE === 'true') || false,
|
authDisabled: config('auth_disable') || (process.env.AUTH_DISABLE === 'true') || false,
|
||||||
printerType: config('printer_type') || process.env.PRINTER_TYPE || '',
|
printers: config('printers') || process.env.PRINTERS || '',
|
||||||
printerIp: config('printer_ip') || process.env.PRINTER_IP || '192.168.1.1',
|
|
||||||
smtpFrom: config('smtp_from') || process.env.SMTP_FROM || '',
|
smtpFrom: config('smtp_from') || process.env.SMTP_FROM || '',
|
||||||
smtpHost: config('smtp_host') || process.env.SMTP_HOST || '',
|
smtpHost: config('smtp_host') || process.env.SMTP_HOST || '',
|
||||||
smtpPort: config('smtp_port') || process.env.SMTP_PORT || 25,
|
smtpPort: config('smtp_port') || process.env.SMTP_PORT || 25,
|
||||||
@@ -45,7 +44,6 @@ module.exports = {
|
|||||||
logLevel: config('log_level') || process.env.LOG_LEVEL || 'info',
|
logLevel: config('log_level') || process.env.LOG_LEVEL || 'info',
|
||||||
translationDefault: config('translation_default') || process.env.TRANSLATION_DEFAULT || 'en',
|
translationDefault: config('translation_default') || process.env.TRANSLATION_DEFAULT || 'en',
|
||||||
translationDebug: config('translation_debug') || (process.env.TRANSLATION_DEBUG === 'true') || false,
|
translationDebug: config('translation_debug') || (process.env.TRANSLATION_DEBUG === 'true') || false,
|
||||||
uiBackButton: config('ui_back_button') || (process.env.UI_BACK_BUTTON === 'true') || false,
|
|
||||||
gitTag: process.env.GIT_TAG || 'master',
|
gitTag: process.env.GIT_TAG || 'master',
|
||||||
gitBuild: fs.existsSync('/etc/unifi_voucher_site_build') ? fs.readFileSync('/etc/unifi_voucher_site_build', 'utf-8') : 'Development'
|
gitBuild: fs.existsSync('/etc/unifi_voucher_site_build') ? fs.readFileSync('/etc/unifi_voucher_site_build', 'utf-8') : 'Development'
|
||||||
};
|
};
|
||||||
|
|||||||
903
package-lock.json
generated
903
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.21.2",
|
"express": "^5.1.0",
|
||||||
"express-locale": "^2.0.2",
|
"express-locale": "^2.0.2",
|
||||||
"express-openid-connect": "^2.18.1",
|
"express-openid-connect": "^2.18.1",
|
||||||
"js-logger": "^1.6.1",
|
"js-logger": "^1.6.1",
|
||||||
@@ -36,8 +36,8 @@
|
|||||||
"qrcode": "^1.5.4"
|
"qrcode": "^1.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/cli": "^4.1.5",
|
"@tailwindcss/cli": "^4.1.7",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"tailwindcss": "^4.1.5"
|
"tailwindcss": "^4.1.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
server.js
42
server.js
@@ -165,7 +165,7 @@ if(variables.serviceWeb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to generate a voucher or send an email with an existing voucher
|
// Check if we need to generate a voucher or send an email with an existing voucher
|
||||||
if(req.body.id && req.body.code && req.body.email) {
|
if(req.body && req.body.id && req.body.code && req.body.email) {
|
||||||
// Check if email functions are enabled
|
// Check if email functions are enabled
|
||||||
if(variables.smtpFrom === '' || variables.smtpHost === '' || variables.smtpPort === '') {
|
if(variables.smtpFrom === '' || variables.smtpHost === '' || variables.smtpPort === '') {
|
||||||
res.status(501).send();
|
res.status(501).send();
|
||||||
@@ -363,7 +363,7 @@ if(variables.serviceWeb) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.get('/voucher/:id/print', [authorization.web], async (req, res) => {
|
app.get('/voucher/:id/print', [authorization.web], async (req, res) => {
|
||||||
if(variables.printerType === '') {
|
if(variables.printers === '') {
|
||||||
res.status(501).send();
|
res.status(501).send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -377,6 +377,7 @@ if(variables.serviceWeb) {
|
|||||||
baseUrl: req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : '',
|
baseUrl: req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : '',
|
||||||
languages,
|
languages,
|
||||||
defaultLanguage: variables.translationDefault,
|
defaultLanguage: variables.translationDefault,
|
||||||
|
printers: variables.printers.split(','),
|
||||||
voucher,
|
voucher,
|
||||||
updated: cache.updated
|
updated: cache.updated
|
||||||
});
|
});
|
||||||
@@ -388,17 +389,22 @@ if(variables.serviceWeb) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.post('/voucher/:id/print', [authorization.web], async (req, res) => {
|
app.post('/voucher/:id/print', [authorization.web], async (req, res) => {
|
||||||
if(variables.printerType === '') {
|
if(variables.printers === '') {
|
||||||
res.status(501).send();
|
res.status(501).send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!variables.printers.includes(req.body.printer)) {
|
||||||
|
res.status(400).send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const voucher = cache.vouchers.find((e) => {
|
const voucher = cache.vouchers.find((e) => {
|
||||||
return e._id === req.params.id;
|
return e._id === req.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(voucher) {
|
if(voucher) {
|
||||||
if(variables.printerType === 'pdf') {
|
if(req.body.printer === 'pdf') {
|
||||||
const buffers = await print.pdf(voucher, req.body.language);
|
const buffers = await print.pdf(voucher, req.body.language);
|
||||||
const pdfData = Buffer.concat(buffers);
|
const pdfData = Buffer.concat(buffers);
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
@@ -406,10 +412,8 @@ if(variables.serviceWeb) {
|
|||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
'Content-Disposition': `attachment;filename=voucher_${req.params.id}.pdf`
|
'Content-Disposition': `attachment;filename=voucher_${req.params.id}.pdf`
|
||||||
}).end(pdfData);
|
}).end(pdfData);
|
||||||
}
|
} else {
|
||||||
|
const printResult = await print.escpos(voucher, req.body.language, req.body.printer).catch((e) => {
|
||||||
if(variables.printerType === 'escpos') {
|
|
||||||
const printResult = await print.escpos(voucher, req.body.language).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`);
|
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`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -525,12 +529,11 @@ if(variables.serviceWeb) {
|
|||||||
info_text: req.flashMessage.message || '',
|
info_text: req.flashMessage.message || '',
|
||||||
error: req.flashMessage.type === 'error',
|
error: req.flashMessage.type === 'error',
|
||||||
error_text: req.flashMessage.message || '',
|
error_text: req.flashMessage.message || '',
|
||||||
uiBackButton: variables.uiBackButton,
|
|
||||||
kioskEnabled: variables.kioskEnabled,
|
kioskEnabled: variables.kioskEnabled,
|
||||||
timeConvert: time,
|
timeConvert: time,
|
||||||
bytesConvert: bytes,
|
bytesConvert: bytes,
|
||||||
email_enabled: variables.smtpFrom !== '' && variables.smtpHost !== '' && variables.smtpPort !== '',
|
email_enabled: variables.smtpFrom !== '' && variables.smtpHost !== '' && variables.smtpPort !== '',
|
||||||
printer_enabled: variables.printerType !== '',
|
printer_enabled: variables.printers !== '',
|
||||||
voucher_types: types(variables.voucherTypes),
|
voucher_types: types(variables.voucherTypes),
|
||||||
voucher_custom: variables.voucherCustom,
|
voucher_custom: variables.voucherCustom,
|
||||||
vouchers: cache.vouchers.filter((item) => {
|
vouchers: cache.vouchers.filter((item) => {
|
||||||
@@ -617,7 +620,6 @@ if(variables.serviceWeb) {
|
|||||||
baseUrl: req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : '',
|
baseUrl: req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : '',
|
||||||
gitTag: variables.gitTag,
|
gitTag: variables.gitTag,
|
||||||
gitBuild: variables.gitBuild,
|
gitBuild: variables.gitBuild,
|
||||||
uiBackButton: variables.uiBackButton,
|
|
||||||
kioskEnabled: variables.kioskEnabled,
|
kioskEnabled: variables.kioskEnabled,
|
||||||
user: user,
|
user: user,
|
||||||
userIcon: req.oidc ? crypto.createHash('sha256').update(user.email).digest('hex') : '',
|
userIcon: req.oidc ? crypto.createHash('sha256').update(user.email).digest('hex') : '',
|
||||||
@@ -626,7 +628,7 @@ if(variables.serviceWeb) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.get('/bulk/print', [authorization.web], async (req, res) => {
|
app.get('/bulk/print', [authorization.web], async (req, res) => {
|
||||||
if(variables.printerType === '') {
|
if(variables.printers === '') {
|
||||||
res.status(501).send();
|
res.status(501).send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -637,16 +639,22 @@ if(variables.serviceWeb) {
|
|||||||
bytesConvert: bytes,
|
bytesConvert: bytes,
|
||||||
languages,
|
languages,
|
||||||
defaultLanguage: variables.translationDefault,
|
defaultLanguage: variables.translationDefault,
|
||||||
|
printers: variables.printers.split(','),
|
||||||
vouchers: cache.vouchers,
|
vouchers: cache.vouchers,
|
||||||
updated: cache.updated
|
updated: cache.updated
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.post('/bulk/print', [authorization.web], async (req, res) => {
|
app.post('/bulk/print', [authorization.web], async (req, res) => {
|
||||||
if(variables.printerType === '') {
|
if(variables.printers === '') {
|
||||||
res.status(501).send();
|
res.status(501).send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!variables.printers.includes(req.body.printer)) {
|
||||||
|
res.status(400).send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!req.body.vouchers) {
|
if(!req.body.vouchers) {
|
||||||
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'No selected vouchers to print!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, `${req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : ''}/vouchers`);
|
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'No selected vouchers to print!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, `${req.headers['x-ingress-path'] ? req.headers['x-ingress-path'] : ''}/vouchers`);
|
||||||
return;
|
return;
|
||||||
@@ -664,7 +672,7 @@ if(variables.serviceWeb) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(!vouchers.includes(undefined)) {
|
if(!vouchers.includes(undefined)) {
|
||||||
if(variables.printerType === 'pdf') {
|
if(req.body.printer === 'pdf') {
|
||||||
const buffers = await print.pdf(vouchers, req.body.language, true);
|
const buffers = await print.pdf(vouchers, req.body.language, true);
|
||||||
const pdfData = Buffer.concat(buffers);
|
const pdfData = Buffer.concat(buffers);
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
@@ -672,13 +680,11 @@ if(variables.serviceWeb) {
|
|||||||
'Content-Type': 'application/pdf',
|
'Content-Type': 'application/pdf',
|
||||||
'Content-Disposition': `attachment;filename=bulk_vouchers_${new Date().getTime()}.pdf`
|
'Content-Disposition': `attachment;filename=bulk_vouchers_${new Date().getTime()}.pdf`
|
||||||
}).end(pdfData);
|
}).end(pdfData);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if(variables.printerType === 'escpos') {
|
|
||||||
let printSuccess = true;
|
let printSuccess = true;
|
||||||
|
|
||||||
for(let voucher = 0; voucher < vouchers.length; voucher++) {
|
for(let voucher = 0; voucher < vouchers.length; voucher++) {
|
||||||
const printResult = await print.escpos(vouchers[voucher], req.body.language).catch((e) => {
|
const printResult = await print.escpos(vouchers[voucher], req.body.language, req.body.printer).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`);
|
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`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,16 @@
|
|||||||
<div class="flex flex-1 flex-col justify-between">
|
<div class="flex flex-1 flex-col justify-between">
|
||||||
<div class="divide-y divide-black/5 dark:divide-white/5 px-4 sm:px-6">
|
<div class="divide-y divide-black/5 dark:divide-white/5 px-4 sm:px-6">
|
||||||
<div class="space-y-6 pb-5 pt-6">
|
<div class="space-y-6 pb-5 pt-6">
|
||||||
|
<div>
|
||||||
|
<label for="printer" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Printer</label>
|
||||||
|
<div class="mt-2">
|
||||||
|
<select id="printer" name="printer" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
|
||||||
|
<% printers.forEach((printer) => { %>
|
||||||
|
<option value="<%= printer %>"><%= printer %> (<%= printer === 'pdf' ? 'PDF' : 'ESC/POS' %>)</option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
|
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|||||||
@@ -18,6 +18,16 @@
|
|||||||
<div class="flex flex-1 flex-col justify-between">
|
<div class="flex flex-1 flex-col justify-between">
|
||||||
<div class="divide-y divide-black/5 dark:divide-white/5 px-4 sm:px-6">
|
<div class="divide-y divide-black/5 dark:divide-white/5 px-4 sm:px-6">
|
||||||
<div class="space-y-6 pb-5 pt-6">
|
<div class="space-y-6 pb-5 pt-6">
|
||||||
|
<div>
|
||||||
|
<label for="printer" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Printer</label>
|
||||||
|
<div class="mt-2">
|
||||||
|
<select id="printer" name="printer" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
|
||||||
|
<% printers.forEach((printer) => { %>
|
||||||
|
<option value="<%= printer %>"><%= printer %> (<%= printer === 'pdf' ? 'PDF' : 'ESC/POS' %>)</option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
|
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|||||||
@@ -2,13 +2,6 @@
|
|||||||
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex h-16 justify-between">
|
<div class="flex h-16 justify-between">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<% if(uiBackButton) { %>
|
|
||||||
<button aria-label="Back to Previous Page" onclick="window.history.go(-1);">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-100 w-10 h-10 mr-4" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<% } %>
|
|
||||||
<a href="<%= baseUrl %>/vouchers" class="flex shrink-0 items-center">
|
<a href="<%= baseUrl %>/vouchers" class="flex shrink-0 items-center">
|
||||||
<img class="h-12 w-auto" width="48" height="48" alt="UniFi Voucher Site Logo" src="<%= baseUrl %>/images/logo.png">
|
<img class="h-12 w-auto" width="48" height="48" alt="UniFi Voucher Site Logo" src="<%= baseUrl %>/images/logo.png">
|
||||||
<div class="hidden sm:block ml-4 text-2xl font-semibold leading-7 text-gray-900 dark:text-white">
|
<div class="hidden sm:block ml-4 text-2xl font-semibold leading-7 text-gray-900 dark:text-white">
|
||||||
|
|||||||
@@ -128,35 +128,6 @@
|
|||||||
<%= status.printing.details %> <a href="<%= status.printing.info %>" class="italic text-xs underline" aria-label="More Info Link" target="_blank" rel="noreferrer noopener">More Info</a>
|
<%= status.printing.details %> <a href="<%= status.printing.info %>" class="italic text-xs underline" aria-label="More Info Link" target="_blank" rel="noreferrer noopener">More Info</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="border-b border-gray-300 dark:border-white/10 bg-white dark:bg-white/5">
|
|
||||||
<td class="p-4 align-middle font-medium flex items-center gap-2 pl-6">
|
|
||||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625Z" />
|
|
||||||
<path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
|
|
||||||
</svg>
|
|
||||||
PDF
|
|
||||||
</td>
|
|
||||||
<td class="p-4 align-middle">
|
|
||||||
<%- include('partials/tag', {status: status.printing.modules.pdf.status}) %>
|
|
||||||
</td>
|
|
||||||
<td class="p-4 align-middle">
|
|
||||||
<%= status.printing.modules.pdf.details %> <a href="<%= status.printing.modules.pdf.info %>" class="italic text-xs underline" aria-label="More Info Link" target="_blank" rel="noreferrer noopener">More Info</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="border-b border-gray-300 dark:border-white/10 bg-white dark:bg-white/5">
|
|
||||||
<td class="p-4 align-middle font-medium flex items-center gap-2 pl-6">
|
|
||||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path fill-rule="evenodd" d="M7.875 1.5C6.839 1.5 6 2.34 6 3.375v2.99c-.426.053-.851.11-1.274.174-1.454.218-2.476 1.483-2.476 2.917v6.294a3 3 0 0 0 3 3h.27l-.155 1.705A1.875 1.875 0 0 0 7.232 22.5h9.536a1.875 1.875 0 0 0 1.867-2.045l-.155-1.705h.27a3 3 0 0 0 3-3V9.456c0-1.434-1.022-2.7-2.476-2.917A48.716 48.716 0 0 0 18 6.366V3.375c0-1.036-.84-1.875-1.875-1.875h-8.25ZM16.5 6.205v-2.83A.375.375 0 0 0 16.125 3h-8.25a.375.375 0 0 0-.375.375v2.83a49.353 49.353 0 0 1 9 0Zm-.217 8.265c.178.018.317.16.333.337l.526 5.784a.375.375 0 0 1-.374.409H7.232a.375.375 0 0 1-.374-.409l.526-5.784a.373.373 0 0 1 .333-.337 41.741 41.741 0 0 1 8.566 0Zm.967-3.97a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75H18a.75.75 0 0 1-.75-.75V10.5ZM15 9.75a.75.75 0 0 0-.75.75v.008c0 .414.336.75.75.75h.008a.75.75 0 0 0 .75-.75V10.5a.75.75 0 0 0-.75-.75H15Z" clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
ESC/POS
|
|
||||||
</td>
|
|
||||||
<td class="p-4 align-middle">
|
|
||||||
<%- include('partials/tag', {status: status.printing.modules.escpos.status}) %>
|
|
||||||
</td>
|
|
||||||
<td class="p-4 align-middle">
|
|
||||||
<%= status.printing.modules.escpos.details %> <a href="<%= status.printing.modules.escpos.info %>" class="italic text-xs underline" aria-label="More Info Link" target="_blank" rel="noreferrer noopener">More Info</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="border-b border-gray-300 dark:border-white/10 bg-white dark:bg-white/5">
|
<tr class="border-b border-gray-300 dark:border-white/10 bg-white dark:bg-white/5">
|
||||||
<td class="p-4 align-middle font-medium flex items-center gap-2">
|
<td class="p-4 align-middle font-medium flex items-center gap-2">
|
||||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ module.exports = {
|
|||||||
'DISABLE_AUTH',
|
'DISABLE_AUTH',
|
||||||
'AUTH_OIDC_CLIENT_TYPE',
|
'AUTH_OIDC_CLIENT_TYPE',
|
||||||
'AUTH_PASSWORD',
|
'AUTH_PASSWORD',
|
||||||
'AUTH_TOKEN'
|
'AUTH_TOKEN',
|
||||||
|
'UI_BACK_BUTTON',
|
||||||
|
'PRINTER_TYPE',
|
||||||
|
'PRINTER_IP'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,29 +47,12 @@ module.exports = () => {
|
|||||||
},
|
},
|
||||||
printing: {
|
printing: {
|
||||||
status: {
|
status: {
|
||||||
text: variables.printerType !== '' ? 'Enabled' : 'Disabled',
|
text: variables.printers !== '' ? 'Enabled' : 'Disabled',
|
||||||
state: variables.printerType !== '' ? 'green' : 'red'
|
state: variables.printers !== '' ? 'green' : 'red'
|
||||||
},
|
},
|
||||||
details: variables.printerType !== '' ? 'Printing service has been configured.' : 'No printing service enabled.',
|
details: variables.printers !== '' ? `Printing service has been configured. Available printers: ${variables.printers.split(',').join(', ')}` : 'No printing service enabled.',
|
||||||
info: 'https://github.com/glenndehaan/unifi-voucher-site#print-functionality',
|
info: 'https://github.com/glenndehaan/unifi-voucher-site#print-functionality',
|
||||||
modules: {
|
modules: {}
|
||||||
pdf: {
|
|
||||||
status: {
|
|
||||||
text: variables.printerType === 'pdf' ? 'Enabled' : 'Disabled',
|
|
||||||
state: variables.printerType === 'pdf' ? 'green' : 'red'
|
|
||||||
},
|
|
||||||
details: variables.printerType === 'pdf' ? 'PDF Service enabled.' : 'PDF Service not enabled.',
|
|
||||||
info: 'https://github.com/glenndehaan/unifi-voucher-site#pdf'
|
|
||||||
},
|
|
||||||
escpos: {
|
|
||||||
status: {
|
|
||||||
text: variables.printerType === 'escpos' ? 'Enabled' : 'Disabled',
|
|
||||||
state: variables.printerType === 'escpos' ? 'green' : 'red'
|
|
||||||
},
|
|
||||||
details: variables.printerType === 'escpos' ? `ESC/POS Printing on ${variables.printerIp}.` : 'ESC/POS Service not enabled.',
|
|
||||||
info: 'https://github.com/glenndehaan/unifi-voucher-site#escpos'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
status: {
|
status: {
|
||||||
|
|||||||
Reference in New Issue
Block a user