uhppoted-db-web/routes/acl.js

260 lines
9.1 KiB
JavaScript

const express = require('express');
const log = require("../logger.js");
const db = global.db;
const router = express.Router();
const expressWs = require('express-ws')(router);
router.use((req, res, next) => {
if (!req.session.user) return res.redirect('/login?err=4');
const perms = req.session.user.perms ? JSON.parse(req.session.user.perms) : [];
if (perms.includes('*') || perms.includes('acl')) {
return next();
}
return res.status(403).render('error', { error: 'Forbidden', button: { text: 'Back', action: 'back' } });
});
router.get('/', async (req, res) => {
const acl = await db.query('SELECT * FROM ACL');
res.render('acl', { acl, user: req.session.user });
});
router.get('/add', async (req, res) => { // Render form to add new ACL entry
doorList = {}
await db.query('SHOW COLUMNS FROM ACL;').then(columns => {
columns.forEach(col => {
if (col.Field !== 'Name' && col.Field !== 'CardNumber' && col.Field !== 'PIN' && col.Field !== 'StartDate' && col.Field !== 'EndDate') {
doorList[col.Field] = false;
}
});
res.render('acl-add', { user: req.session.user, doorList });
}).catch(err => {
log.error(`Database error fetching ACL columns: ${err}`);
return {};
});
});
router.put('/', async (req, res) => { // Attempt to create new ACL entry. Fail if cardNumber already exists
const data = req.body;
const cardNumber = new Number(data.CardNumber);
if (isNaN(cardNumber) || cardNumber <= 0) {
return res.status(400).json({ error: 'Invalid card number' });
}
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (check && check.length > 0) {
return res.status(409).json({ error: 'Card number already exists' });
}
// Name, CardNumber, PIN, StartDate, EndDate, [Doors (will be array of door names from client, for each add that column adn set to 1)]
const fields = ['Name', 'CardNumber', 'PIN', 'StartDate', 'EndDate'];
const placeholders = ['?', '?', '?', '?', '?'];
const values = [
data.Name || "Unknown/Not Set",
cardNumber,
data.PIN || null,
data.StartDate || new Date(),
data.EndDate || new Date(new Date().setFullYear(new Date().getFullYear() + 99))
];
for (const door in data.doors) {
if (data.doors.hasOwnProperty(door)) {
fields.push(door);
placeholders.push('?');
values.push(data.doors[door] ? 1 : 0);
}
}
const sql = `INSERT INTO ACL (${fields.join(', ')}) VALUES (${placeholders.join(', ')})`;
db.query(sql, values).then(result => {
return res.status(201).json({ message: 'ACL entry created' });
}).catch(err => {
log.error(`Database error creating ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
router.get('/edit/:cardNumber', async (req, res) => { // Get ACL entry for editing
const cardNumber = req.params.cardNumber;
if (!/^\d+$/.test(cardNumber)) {
return res.status(400).json({ error: 'Invalid card number' });
}
try {
const entry = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (!entry || entry.length === 0) {
return res.render('error', { error: 'Card number not found', button: { text: 'Back', action: 'back' } });
}
// generate doors object
const doors = {}
Object.keys(entry[0]).forEach(key => {
if (key !== 'id' && key !== 'Name' && key !== 'CardNumber' && key !== 'PIN' && key !== 'StartDate' && key !== 'EndDate') {
doors[key] = entry[0][key] === 1;
}
});
return res.render('acl-edit', { aclEntry: entry[0], doorsList: doors, user: req.session.user });
} catch (err) {
log.error(`Database error fetching ACL entry: ${err}`);
return res.status(500).render('error', { error: 'Internal server error', button: { text: 'Back', action: 'back' } });
}
});
router.patch('/:cardNumber', async (req, res) => { // Update ACL entry. Fail if cardNumber does not exist
const cardNumber = req.params.cardNumber;
if (!/^\d+$/.test(cardNumber)) {
return res.status(400).json({ error: 'Invalid card number' });
}
const data = req.body;
data.cardNumber = cardNumber;
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (!check || check.length === 0) {
return res.status(404).json({ error: 'Card number not found' });
}
// Name, CardNumber, PIN, StartDate, EndDate, [Doors (will be object of door names : true/false from client, for each add that column adn set to 1)]
const fields = [];
const values = [];
if (data.Name) {
fields.push('Name = ?');
values.push(data.Name);
}
if (data.PIN) {
fields.push('PIN = ?');
values.push(data.PIN);
}
if (data.StartDate) {
fields.push('StartDate = ?');
values.push(data.StartDate);
}
if (data.EndDate) {
fields.push('EndDate = ?');
values.push(data.EndDate);
}
for (const door in data.doors) {
if (data.doors.hasOwnProperty(door)) {
fields.push(`${door} = ?`);
values.push(data.doors[door] ? 1 : 0);
}
if (fields.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
}
values.push(cardNumber);
const sql = `UPDATE ACL SET ${fields.join(', ')} WHERE CardNumber = ?`;
db.query(sql, values).then(result => {
return res.status(200).json({ message: 'ACL entry updated' });
}).catch(err => {
log.error(`Database error updating ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
router.get('/delete/:cardNumber', async (req, res) => { // Delete ACL entry. Fail if cardNumber does not exist
const cardNumber = req.params.cardNumber;
if (!/^\d+$/.test(cardNumber)) {
return res.status(400).json({ error: 'Invalid card number' });
}
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (!check || check.length === 0) {
return res.status(404).json({ error: 'Card number not found' });
}
db.query('DELETE FROM ACL WHERE CardNumber = ?', [cardNumber]).then(result => {
return res.redirect('/acl');
}).catch(err => {
log.error(`Database error deleting ACL entry: ${err}`);
return res.status(500).render('error', { error: 'Internal server error', button: { text: 'Back', action: 'back' } });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
router.get('/bulk-add', async (req, res) => { // Render form to bulk add ACL entries
doorList = {}
await db.query('SHOW COLUMNS FROM ACL;').then(columns => {
columns.forEach(col => {
if (col.Field !== 'Name' && col.Field !== 'CardNumber' && col.Field !== 'PIN' && col.Field !== 'StartDate' && col.Field !== 'EndDate') {
doorList[col.Field] = false;
}
});
}).catch(err => {
log.error(`Database error fetching ACL columns: ${err}`);
return {};
});
res.render('acl-bulk-add', { user: req.session.user, doorList: doorList });
});
router.post('/bulk-add', async (req, res) => { // Process bulk add of ACL entries
const data = req.body.credentials;
if (!Array.isArray(data)) {
return res.status(400).json({ error: 'Invalid data format' });
}
const results = [];
for (const entry of data) {
const cardNumber = Number(entry.CardNumber);
if (isNaN(cardNumber) || cardNumber <= 0) {
results.push({ cardNumber: entry.CardNumber, status: 'error', error: 'Invalid card number' });
continue;
}
try {
const exists = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (exists && exists.length > 0) {
results.push({ cardNumber, status: 'error', error: 'Card number already exists' });
continue;
}
const fields = ['Name', 'CardNumber', 'StartDate', 'EndDate'];
const placeholders = ['?', '?', '?', '?'];
const values = [
entry.Name || "Unknown/Not Set",
cardNumber,
entry.StartDate || new Date(),
entry.EndDate || new Date(new Date().setFullYear(new Date().getFullYear() + 99))
];
if (Array.isArray(entry.Doors)) {
for (const door of entry.Doors) {
console.log(door)
fields.push(door);
placeholders.push('?');
values.push(1);
}
}
const sql = `INSERT INTO ACL (${fields.join(', ')}) VALUES (${placeholders.join(', ')})`;
log.debug(`Bulk add SQL: ${sql} Values: ${values}`);
await db.query(sql, values);
results.push({ cardNumber, status: 'success' });
} catch (err) {
log.error(`Bulk add error for card ${cardNumber}: ${err}`);
results.push({ cardNumber, status: 'error', error: 'Internal server error' });
}
}
log.debug(`Bulk add results: ${JSON.stringify(results)}`);
return res.sendStatus(201);
});
router.ws("/bulk-add", (ws, req) => {
log.debug(`Client ${req.sessionID} connected to ACL bulk add WebSocket`);
if (!req.session.user) {
ws.send(JSON.stringify({ error: 'Not authenticated' }))
ws.close();
return;
}
global.dbEvent.on('event', (event) => {
if ( event.Type != 1 || event.Granted != 0 ) return; // Only process 'Swipe' events that were denied
ws.send(JSON.stringify(event));
});
});
module.exports = router;