diff --git a/eventWatcher.js b/eventWatcher.js index 0821372..8865314 100644 --- a/eventWatcher.js +++ b/eventWatcher.js @@ -105,7 +105,7 @@ if (process.env.EVENT_TESTING == true) { await getLastEventIndex(); await fetchEvents(); })(); -test = true +test = false if (test == true) { log.warn("EVENT_TESTING is enabled - generating test events"); setInterval(() => { diff --git a/routes/acl.js b/routes/acl.js new file mode 100644 index 0000000..05d400a --- /dev/null +++ b/routes/acl.js @@ -0,0 +1,139 @@ +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.put('/:cardNumber', async (req, res) => { // Attempt to create new ACL entry. Fail if cardNumber already exists + const cardNumber = parseInt(req.params.cardNumber); + if (isNaN(cardNumber) || cardNumber <= 0) { + 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(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", + data.cardNumber, + data.PIN || null, + data.StartDate || new Date(), + data.EndDate || new Date(new Date().setFullYear(new Date().getFullYear() + 99)) + ]; + data.doors.forEach(door => { + fields.push(door); + placeholders.push('?'); + values.push(1); + }); + 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.patch('/:cardNumber', async (req, res) => { // Update ACL entry. Fail if cardNumber does not exist + const cardNumber = parseInt(req.params.cardNumber); + if (isNaN(cardNumber) || cardNumber <= 0) { + 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.delete('/:cardNumber', async (req, res) => { // Delete ACL entry. Fail if cardNumber does not exist + const cardNumber = parseInt(req.params.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(404).json({ error: 'Card number not found' }); + } + db.query('DELETE FROM ACL WHERE CardNumber = ?', [cardNumber]).then(result => { + return res.status(200).json({ message: 'ACL entry deleted' }); + }).catch(err => { + log.error(`Database error deleting 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' }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/event-logs.js b/routes/event-logs.js index 8e41c06..ab538fc 100644 --- a/routes/event-logs.js +++ b/routes/event-logs.js @@ -62,6 +62,15 @@ dataTypes = { } } +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('eventLog')) { + return next(); + } + return res.status(403).render('error', { error: 'Forbidden', button: { text: 'Back', action: 'back' } }); +}); + router.get('/', async (req, res) => { const logs = await db.query('SELECT * FROM Events ORDER BY EventIndex DESC LIMIT 100'); res.render('event-logs', { logs, user: req.session.user, dataTypes }); diff --git a/views/acl.ejs b/views/acl.ejs new file mode 100644 index 0000000..ff4804e --- /dev/null +++ b/views/acl.ejs @@ -0,0 +1,82 @@ + + +
+ +Name | +CardNumber | +PIN | +StartDate | +EndDate | + <% + // Get door columns by filtering keys not in known columns + const knownCols = ['Name', 'CardNumber', 'PIN', 'StartDate', 'EndDate']; + const doors = acl.length > 0 + ? Object.keys(acl[0]).filter(col => !knownCols.includes(col)) + : []; + doors.forEach(function(door) { + %> +<%= door %> | + <% }); %> +
---|---|---|---|---|---|
<%= row.Name %> | +<%= row.CardNumber %> | +<%= row.PIN %> | +<%= row.StartDate %> | +<%= row.EndDate %> | + <% doors.forEach(function(door) { %> ++ <% if (row[door]) { %> + ✅ + <% } else { %> + ❌ + <% } %> + | + <% }); %> +