Files
unifi-voucher-site-glenndeh…/server.js
2024-03-23 16:43:00 +01:00

318 lines
9.4 KiB
JavaScript

/**
* Import base packages
*/
const os = require('os');
const express = require('express');
const multer = require('multer');
const cookieParser = require('cookie-parser');
/**
* Import own modules
*/
const logo = require('./modules/logo');
const types = require('./modules/types');
const time = require('./modules/time');
const unifi = require('./modules/unifi');
/**
* Import own middlewares
*/
const authorization = require('./middlewares/authorization');
const flashMessage = require('./middlewares/flashMessage');
const sid = require('./middlewares/sid');
/**
* Setup Express app
*/
const app = express();
/**
* Define global functions and variables
*/
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;
const voucherTypes = types(process.env.VOUCHER_TYPES || '480,0,,,;');
const webService = (process.env.SERVICE_WEB === 'true') || true;
const apiService = (process.env.SERVICE_API === 'true') || false;
const authDisabled = (process.env.DISABLE_AUTH === 'true') || false;
/**
* Output logo
*/
logo();
/**
* Log service status
*/
console.log(`[Service][Web] ${webService ? 'Enabled!' : 'Disabled!'}`);
console.log(`[Service][Api] ${apiService ? 'Enabled!' : 'Disabled!'}`);
/**
* Log voucher types
*/
console.log('[VoucherType] Loaded the following types:');
voucherTypes.forEach((type, key) => {
console.log(`[VoucherType][${key}] ${time(type.expiration)}, ${type.usage === '1' ? 'single-use' : 'multi-use'}${typeof type.upload === "undefined" && typeof type.download === "undefined" && typeof type.megabytes === "undefined" ? ', no limits' : `${typeof type.upload !== "undefined" ? `, upload bandwidth limit: ${type.upload} kb/s` : ''}${typeof type.download !== "undefined" ? `, download bandwidth limit: ${type.download} kb/s` : ''}${typeof type.megabytes !== "undefined" ? `, quota limit: ${type.megabytes} mb` : ''}`}`);
});
/**
* Log auth status
*/
console.log(`[Auth] ${authDisabled ? 'Disabled!' : 'Enabled!'}`);
/**
* Log controller
*/
console.log(`[UniFi] Using Controller on: ${process.env.UNIFI_IP || '192.168.1.1'}:${process.env.UNIFI_PORT || 443} (Site ID: ${process.env.UNIFI_SITE_ID || 'default'})`);
/**
* Trust proxy
*/
app.enable('trust proxy');
/**
* Set template engine
*/
app.set('view engine', 'ejs');
app.set('views', `${__dirname}/template`);
/**
* GET /_health - Health check page
*/
app.get('/_health', (req, res) => {
res.json({
status: 'UP',
host: os.hostname(),
load: process.cpuUsage(),
mem: process.memoryUsage(),
uptime: process.uptime()
});
});
/**
* Enable multer
*/
app.use(multer().none());
/**
* Enable cookie-parser
*/
app.use(cookieParser());
/**
* Enable flash-message
*/
app.use(flashMessage);
/**
* Enable session id
*/
app.use(sid);
/**
* Request logger
*/
app.use((req, res, next) => {
if(req.originalUrl.includes('/images') || req.originalUrl.includes('/dist') || req.originalUrl.includes('/manifest')) {
console.log(`[Web]: ${req.originalUrl}`);
} else {
console.log(`[Web][${req.sid}]: ${req.originalUrl}`);
}
console.log(`[Web][HA Debug] ${JSON.stringify(req.headers)}`);
next();
});
/**
* Serve static public dir
*/
app.use(express.static(`${__dirname}/public`));
/**
* Configure routers
*/
app.get('/', (req, res) => {
if(webService) {
res.redirect(302, '/voucher');
} else {
res.status(501).send();
}
});
// Check if web service is enabled
if(webService) {
app.get('/login', (req, res) => {
// Check if authentication is disabled
if (authDisabled) {
res.redirect(302, '/voucher');
return;
}
const hour = new Date().getHours();
const timeHeader = hour < 12 ? 'Good Morning' : hour < 18 ? 'Good Afternoon' : 'Good Evening';
res.render('login', {
error: req.flashMessage.type === 'error',
error_text: req.flashMessage.message || '',
banner_image: process.env.BANNER_IMAGE || `/images/bg-${random(1, 10)}.jpg`,
app_header: timeHeader,
sid: req.sid
});
});
app.post('/login', async (req, res) => {
if (typeof req.body === "undefined") {
res.status(400).send();
return;
}
const passwordCheck = req.body.password === (process.env.SECURITY_CODE || "0000");
if(!passwordCheck) {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'Password Invalid!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/login');
return;
}
res.cookie('authorization', req.body.password, {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/voucher');
});
app.get('/voucher', [authorization.web], async (req, res) => {
const hour = new Date().getHours();
const timeHeader = hour < 12 ? 'Good Morning' : hour < 18 ? 'Good Afternoon' : 'Good Evening';
res.render('voucher', {
info: req.flashMessage.type === 'info',
info_text: req.flashMessage.message || '',
error: req.flashMessage.type === 'error',
error_text: req.flashMessage.message || '',
banner_image: process.env.BANNER_IMAGE || `/images/bg-${random(1, 10)}.jpg`,
app_header: timeHeader,
sid: req.sid,
timeConvert: time,
voucher_types: voucherTypes,
vouchers_popup: false
});
});
app.post('/voucher', [authorization.web], async (req, res) => {
if (typeof req.body === "undefined") {
res.status(400).send();
return;
}
const typeCheck = (process.env.VOUCHER_TYPES || '480,0,,,;').split(';').includes(req.body['voucher-type']);
if(!typeCheck) {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: 'Unknown Type!'}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/voucher');
return;
}
// Create voucher code
const voucherCode = await unifi(types(req.body['voucher-type'], true)).catch((e) => {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: e}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/voucher');
});
if(voucherCode) {
res.cookie('flashMessage', JSON.stringify({type: 'info', message: `Voucher Created: ${voucherCode}`}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/voucher');
}
});
app.get('/vouchers', [authorization.web], async (req, res) => {
const hour = new Date().getHours();
const timeHeader = hour < 12 ? 'Good Morning' : hour < 18 ? 'Good Afternoon' : 'Good Evening';
const vouchers = await unifi('', false).catch((e) => {
res.cookie('flashMessage', JSON.stringify({type: 'error', message: e}), {httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000)}).redirect(302, '/voucher');
});
if (vouchers) {
res.render('voucher', {
info: req.flashMessage.type === 'info',
info_text: req.flashMessage.message || '',
error: req.flashMessage.type === 'error',
error_text: req.flashMessage.message || '',
banner_image: process.env.BANNER_IMAGE || `/images/bg-${random(1, 10)}.jpg`,
app_header: timeHeader,
sid: req.sid,
timeConvert: time,
voucher_types: voucherTypes,
vouchers_popup: true,
vouchers
});
}
});
}
if(apiService) {
app.get('/api', (req, res) => {
res.json({
error: null,
data: {
message: 'OK',
endpoints: [
'/api',
'/api/types',
'/api/voucher/:type'
]
}
});
});
app.get('/api/types', (req, res) => {
res.json({
error: null,
data: {
message: 'OK',
types: voucherTypes
}
});
});
app.get('/api/voucher/:type', [authorization.api], async (req, res) => {
const typeCheck = (process.env.VOUCHER_TYPES || '480,0,,,;').split(';').includes(req.params.type);
if(!typeCheck) {
res.json({
error: 'Unknown Type!',
data: {}
});
return;
}
// Create voucher code
const voucherCode = await unifi(types(req.params.type, true)).catch((e) => {
res.json({
error: e,
data: {}
});
});
if(voucherCode) {
res.json({
error: null,
data: {
message: 'OK',
voucher: voucherCode
}
});
}
});
}
/**
* Setup default 404 message
*/
app.use((req, res) => {
res.status(404);
res.render('404', {
banner_image: process.env.BANNER_IMAGE || `/images/bg-${random(1, 10)}.jpg`,
sid: req.sid
});
});
/**
* Disable powered by header for security reasons
*/
app.disable('x-powered-by');
/**
* Start listening on port
*/
app.listen(3000, '0.0.0.0', () => {
console.log(`[App] Running on: 0.0.0.0:3000`);
});