Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/main_endpoints/models/AuditLog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const AuditLogActions = require('../util/auditLogActions');
const { writeLogToClient } = require('../util/AuditLog');

const AuditLogSchema = new Schema(
{
Expand All @@ -27,4 +28,9 @@ const AuditLogSchema = new Schema(

AuditLogSchema.index({ _id: 1, action: 1 });

AuditLogSchema.post('save', async function(doc) {
const newDoc = await doc.constructor.findById(doc._id).populate('userId', 'firstName lastName email');
writeLogToClient({message: newDoc});
});

module.exports = mongoose.model('AuditLog', AuditLogSchema);
3 changes: 2 additions & 1 deletion api/main_endpoints/models/ChatMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const ChatMessageSchema = new Schema(
},
expiresAt: {
type: Date,
default: ()=> Date.now() + 24 * 3600 * 1000, // expires in 24 hours
default: ()=> new Date(Date.now() + 24 * 3600 * 1000), // expires in 24 hours
index: {expires: 0},
},
chatroomId: {
type: String,
Expand Down
27 changes: 26 additions & 1 deletion api/main_endpoints/routes/AuditLog.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ const AuditLog = require('../models/AuditLog');
const { OK, UNAUTHORIZED, SERVER_ERROR } = require('../../util/constants').STATUS_CODES;
const { OFFICER } = require('../../util/constants.js').MEMBERSHIP_STATE;

const { checkIfTokenSent, checkIfTokenValid } = require('../util/token-functions.js');
const { checkIfTokenSent, checkIfTokenValid, decodeTokenFromBodyOrQuery } = require('../util/token-functions.js');

const logger = require('../../util/logger');
const User = require('../models/User.js');
let { clients } = require('../util/AuditLog.js');

router.get('/getAuditLogs', async (req, res) => {
if (!checkIfTokenSent(req)) {
Expand Down Expand Up @@ -71,4 +72,28 @@ router.get('/getAuditLogs', async (req, res) => {
}
});

router.get('/listen', async (req, res) => {
if (!await decodeTokenFromBodyOrQuery(req)) {
return res.sendStatus(UNAUTHORIZED);
}

const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
};

res.writeHead(OK, headers);
res.flushHeaders();

const newClient = { res };
clients.push(newClient);

req.on('close', () => {
clients = clients.filter(c => c !== newClient);
res.end();
});
});

module.exports = router;
10 changes: 10 additions & 0 deletions api/main_endpoints/util/AuditLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let clients = [];

function writeLogToClient(response) {
clients.forEach(client => {
client.res.write(`data: ${JSON.stringify(response)}\n\n`);
});

}

module.exports = { writeLogToClient, clients };
11 changes: 11 additions & 0 deletions src/APIFunctions/AuditLog.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,14 @@ export async function getAllLogs(page, actionFilter, searchQuery, token) {

return status;
}

export function createAuditLogEventSource(token, onMessage, onError) {
const url = new URL('/api/AuditLog/listen', BASE_API_URL);
url.searchParams.append('token', token);

const eventSource = new EventSource(url.href);
eventSource.onmessage = event => onMessage(JSON.parse(event.data));
eventSource.onerror = onError;

return eventSource;
}
14 changes: 13 additions & 1 deletion src/Pages/AuditLog/AuditLog.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { getAllLogs } from '../../APIFunctions/AuditLog';
import { getAllLogs, createAuditLogEventSource } from '../../APIFunctions/AuditLog';
import Pagination from './Components/Pagination';
import { useSCE } from '../../Components/context/SceContext';
import AuditLogCard from './Components/AuditLogCard';
Expand All @@ -18,6 +18,7 @@ export default function AuditLogPage() {
const [applyingFilters, setApplyingFilters] = useState(false);

const user = useSCE();
const token = user.token;

const toggleActivityFilter = activity => {
setActivityFilters(prev => (prev.includes(activity) ? prev.filter(a => a !== activity) : [...prev, activity]));
Expand Down Expand Up @@ -63,6 +64,17 @@ export default function AuditLogPage() {
};

fetchData();

const eventSource = createAuditLogEventSource(
token,
data => setAuditLogsData(prev => ({ items: [data.message, ...prev.items], totalLogs: prev.totalLogs + 1 })),
() => setError('Failed to load audit logs')
);

return () => {
eventSource.close();
};

}, [currentPage, applyingFilters]);

const applyFilters = async () => {
Expand Down