260 lines
9.1 KiB
JavaScript
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; |