Skip to content
Merged
52 changes: 52 additions & 0 deletions api/main_endpoints/routes/OfficeAccessCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const OfficeAccessCard = require('../models/OfficeAccessCard.js');
const logger = require('../../util/logger');
const { officeAccessCard = {} } = require('../../config/config.json');
const { API_KEY = 'NOTHING_REALLY' } = officeAccessCard;
const { decodeTokenFromBodyOrQuery } = require('../util/token-functions.js');

router.use(bodyParser.json());

Expand Down Expand Up @@ -44,6 +45,25 @@ function checkIfCardExists(cardBytes) {
});
}

let clients = [];

const defaultResponse = {
cardWasAdded: false,
message: 'Card authorized!',
endpoint: '/verify',
};

const writeRequestResponse = ({ statusCode, ...rest }) => {
const response = {
statusCode,
...defaultResponse,
...rest,
};
clients.forEach(client => {
client.res.write(`data: ${JSON.stringify(response)}\n\n`);
});
};

router.get('/verify', async (req, res) =>{
const { cardBytes, add = false } = req.query;
const apiKey = req.headers['x-api-key'];
Expand All @@ -55,6 +75,7 @@ router.get('/verify', async (req, res) =>{
const missingValue = required.find(({ value }) => !value);

if (missingValue) {
writeRequestResponse({ statusCode: BAD_REQUEST, message: `${missingValue.title} missing from request` });
return res.status(BAD_REQUEST).send(` ${missingValue.title} missing from request`);
}

Expand All @@ -64,13 +85,15 @@ router.get('/verify', async (req, res) =>{

const cardExists = await checkIfCardExists(cardBytes);
if (cardExists) {
writeRequestResponse({ statusCode: OK });
return res.sendStatus(OK);
}
// if a card doesnt exist and we arent trying
// to add a new one, that means we were trying
// to verify a card, and that card isnt found.
// therefore return a non OK status
if (!add) {
writeRequestResponse({ statusCode: NOT_FOUND, message: 'Card not found' });
return res.sendStatus(NOT_FOUND);
}

Expand All @@ -80,12 +103,41 @@ router.get('/verify', async (req, res) =>{
await new OfficeAccessCard({
cardBytes
}).save();
writeRequestResponse({ statusCode: OK, message: 'Card added!', endpoint: '/verify?add=1' });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to dynamically get the endpoint from req?

return res.sendStatus(OK);
}
} catch (error) {
logger.error('Error creating OfficeAccessCard: ', error);
writeRequestResponse({
statusCode: SERVER_ERROR,
endpoint: '/verify?add=1',
message: `Error creating Office AccessCard: ${error}`
});
return res.sendStatus(SERVER_ERROR);
}
});

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);

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

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

module.exports = router;
9 changes: 9 additions & 0 deletions src/Components/Navbar/AdminNavbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ export default function UserNavBar(props) {
</svg>
),
},
{
title: 'Card Reader',
route: '/card-reader',
icon: (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M14 13h5v-2h-5zm0-3h5V8h-5zm-9 6h8v-.55q0-1.125-1.1-1.787T9 13t-2.9.663T5 15.45zm4-4q.825 0 1.413-.587T11 10t-.587-1.412T9 8t-1.412.588T7 10t.588 1.413T9 12m-5 8q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.587 1.413T20 20z"/>
</svg>
)
},
];

const renderRoutesForNavbar = (navbarLinks) => {
Expand Down
56 changes: 56 additions & 0 deletions src/Pages/CardReader/CardReader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useState, useEffect } from 'react';
import { BASE_API_URL } from '../../Enums';

export default function CardReader(props) {
const [logs, setLogs] = useState([]);
const [error, setError] = useState('');
const token = props.user.token;

const buildLog = (data) => {
let date = new Date().toISOString().padEnd(30, ' ');
let endpoint = data.endpoint.padEnd(20, ' ');
let statusCode = String(data.statusCode).padEnd(8, ' ');
return [date, endpoint, statusCode, data.message].join('');
};

useEffect(() => {
const url = new URL('/api/OfficeAccessCard/listen', BASE_API_URL);
url.searchParams.append('token', token);
const eventSource = new EventSource(url.href);
eventSource.onmessage = (event) => {
try {
let data = JSON.parse(event.data);
let newLog = buildLog(data);
setLogs(currLogs => [newLog, ...currLogs]); // prepend the new log
} catch (error) {
setLogs(
(currLogs) => [
'[error] unable to format response, check browser logs',
...currLogs,
]
);
}
};

eventSource.onerror = () => {
setError('Error connecting to SSE');
};

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

}, []);

return (
<div className='m-4'>
<h1 className='text-4xl font-bold text-white mb-4'>SCE Card Reader Activity</h1>
<pre>
{logs.join('\n')}
</pre>
{error &&
<h2>{error}</h2>
}
</div>
);
}
11 changes: 10 additions & 1 deletion src/Routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import EmailPreferencesPage from './Pages/EmailPreferences/EmailPreferences';
import sendUnsubscribeEmail from './Pages/Profile/admin/SendUnsubscribeEmail';
import Messaging from './Pages/Messaging/Messaging.js';

import CardReader from './Pages/CardReader/CardReader.js';

export default function Routing({ appProps }) {
const userIsAuthenticated = appProps.authenticated;
const userIsMember =
Expand Down Expand Up @@ -138,7 +140,14 @@ export default function Routing({ appProps }) {
allowedIf: userIsOfficerOrAdmin,
redirect: '/',
inAdminNavbar: true
}
},
{
Component: CardReader,
path: '/card-reader',
allowedIf: userIsOfficerOrAdmin,
redirect: '/',
inAdminNavbar: true
},
];
const signedOutRoutes = [
{ Component: Home, path: '/' },
Expand Down