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
108 changes: 108 additions & 0 deletions api/main_endpoints/routes/LeetCodeLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const express = require('express');
const router = express.Router();
const {
OK,
SERVER_ERROR,
BAD_REQUEST
} = require('../../util/constants.js').STATUS_CODES;
const { decodeToken } = require('../util/token-functions.js');
const logger = require('../../util/logger.js');
const {
getAllUsers,
addUserToLeaderboard,
deleteUserFromLeaderboard,
checkIfUserExists,
} = require('../util/LeetCodeLeaderboard.js');
const AuditLogActions = require('../util/auditLogActions.js');
const AuditLog = require('../models/AuditLog.js');
const membershipState = require('../../util/constants').MEMBERSHIP_STATE;

router.get('/getAllUsers', async (req, res) => {
const decoded = await decodeToken(req, membershipState.OFFICER);
if (decoded.status !== OK) {
return res.sendStatus(decoded.status);
}

const users = await getAllUsers();
if (!users) {
return res.status(SERVER_ERROR);
}
return res.status(OK).send({ users });
});

router.post('/addUser', async (req, res) => {
const decoded = await decodeToken(req, membershipState.OFFICER);
if (decoded.status !== OK) {
return res.sendStatus(decoded.status);
}

const { username, firstName, lastName } = req.body;
const required = [
{ value: username, title: 'LeetCode username', },
{ value: firstName, title: 'User\'s first name', },
{ value: lastName, title: 'User\'s last name', }
];

const missingValue = required.find(({ value }) => !value);

if (missingValue) {
return res.status(BAD_REQUEST).send(`${missingValue.title} missing from request`);
}

if (!await addUserToLeaderboard({
username,
firstName,
lastName
})) {
return res.sendStatus(SERVER_ERROR);
}

AuditLog.create({
userId: decoded.token._id,
action: AuditLogActions.ADD_LEETCODE_USER,
details: { username },
});
return res.sendStatus(OK);
});

router.post('/deleteUser', async (req, res) => {
const decoded = await decodeToken(req, membershipState.OFFICER);
if (decoded.status !== OK) {
return res.sendStatus(decoded.status);
}

const { username } = req.body;
if (!username) {
return res.status(BAD_REQUEST).send('Username field missing');
}

if (!await deleteUserFromLeaderboard(username)) {
return res.sendStatus(SERVER_ERROR);
}

AuditLog.create({
userId: decoded.token._id,
action: AuditLogActions.DELETE_LEETCODE_USER,
details: { username },
});
return res.sendStatus(OK);
});

router.post('/checkIfUserExists', async (req, res) => {
const decoded = await decodeToken(req, membershipState.OFFICER);
if (decoded.status !== OK) {
return res.sendStatus(decoded.status);
}

const { username } = req.body;
if (!username) {
return res.status(BAD_REQUEST).send('Username field missing');
}
const response = await checkIfUserExists(username);
if (response.error) {
return res.status(response.status).send(response.message);
}
return res.status(OK).send(response.exists);
});

module.exports = router;
103 changes: 103 additions & 0 deletions api/main_endpoints/util/LeetCodeLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const logger = require('../../util/logger');
const LEETCODE_URL = 'http://192.168.69.180:8080';

async function getAllUsers() {
try {
const url = new URL('/getAllUsers', LEETCODE_URL);
const res = await fetch(url.href);
const data = await res.json();
if ('error' in data) {
logger.error('Error from LeetCode Leaderboard: ', data.error);
return null;
}
return data.users;
} catch (err) {
logger.error('getAllUsers encountered an error: ', err);
return null;
}
}

async function addUserToLeaderboard(userData) {
try {
const url = new URL('/user/add', LEETCODE_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
const data = await res.json();
if ('error' in data) {
logger.error('Error from LeetCode Leaderboard: ', data.error);
return false;
}
return true;
} catch (err) {
logger.error('addUserToLeaderboard encountered an error: ', err);
return false;
}
}

async function deleteUserFromLeaderboard(username) {
try {
const url = new URL('/user/remove', LEETCODE_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
});
const data = await res.json();
if ('error' in data) {
logger.error('Error from LeetCode Leaderboard: ', data.error);
return false;
}
return true;
} catch (err) {
logger.error('deleteUserFromLeaderboard encountered an error: ', err);
return false;
}
}

async function checkIfUserExists(username) {
try {
const url = new URL('/checkIfUserExists', LEETCODE_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
});
const data = await res.json();
if ('error' in data) {
logger.error('Error from LeetCode Leaderboard: ', data.error);
return {
error: true,
message: data.error,
status: data.status_code,
};
}
return {
error: false,
exists: data.exists,
};

} catch (err) {
logger.error('checkIfUserExists encountered an error: ', err);
return {
error: true,
message: err,
};
}
}

module.exports = {
getAllUsers,
addUserToLeaderboard,
deleteUserFromLeaderboard,
checkIfUserExists,
};

2 changes: 2 additions & 0 deletions api/main_endpoints/util/auditLogActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const AuditLogActions = {
ADD_CARD: 'ADD_CARD',
DELETE_CARD: 'DELETE_CARD',
EDIT_CARD: 'EDIT_CARD',
ADD_LEETCODE_USER: 'ADD_LEETCODE_USER',
DELETE_LEETCODE_USER: 'DELETE_LEETCODE_USER',
};

module.exports = AuditLogActions;
100 changes: 100 additions & 0 deletions src/APIFunctions/LeetCodeLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { ApiResponse } from './ApiResponses';
import { BASE_API_URL } from '../Enums';

export async function getAllUsers(token) {
let status = new ApiResponse();
try {
const url = new URL('/api/LeetCodeLeaderboard/getAllUsers', BASE_API_URL);
const res = await fetch(url.href, {
headers: {
'Authorization': `Bearer ${token}`,
}
});
if (res.ok) {
const result = await res.json();
status.responseData = result;
} else {
status.error = true;
}
} catch (err) {
status.error = true;
status.responseData = err;
}
return status;
}

export async function addUser(userData, token) {
let status = new ApiResponse();
try {
const url = new URL('/api/LeetCodeLeaderboard/addUser', BASE_API_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
if (res.ok) {
const result = await res.json();
status.responseData = result;
} else {
status.error = true;
}
} catch (err) {
status.error = true;
status.responseData = err;
}
return status;
}

export async function deleteUser(username, token) {
let status = new ApiResponse();
try {
const url = new URL('/api/LeetCodeLeaderboard/deleteUser', BASE_API_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
});
if (res.ok) {
const result = await res.json();
status.responseData = result;
} else {
status.error = true;
}
} catch (err) {
status.error = true;
status.responseData = err;
}
return status;
}

export async function checkIfUserExists(username, token) {
let status = new ApiResponse();
try {
const url = new URL('/api/LeetCodeLeaderboard/checkIfUserExists', BASE_API_URL);
const res = await fetch(url.href, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ username }),
});
if (res.ok) {
const result = await res.json();
status.responseData = result;
} else {
status.error = true;
}
} catch (err) {
status.error = true;
status.responseData = err;
}
return status;
}

9 changes: 9 additions & 0 deletions src/Components/Navbar/AdminNavbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ export default function UserNavBar(props) {
</svg>
)
},
{
title: 'LeetCode Leaderboard',
route: '/leetcode-leaderboard',
icon: (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="size-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.242 5.992h12m-12 6.003H20.24m-12 5.999h12M4.117 7.495v-3.75H2.99m1.125 3.75H2.99m1.125 0H5.24m-1.92 2.577a1.125 1.125 0 1 1 1.591 1.59l-1.83 1.83h2.16M2.99 15.745h1.125a1.125 1.125 0 0 1 0 2.25H3.74m0-.002h.375a1.125 1.125 0 0 1 0 2.25H2.99" />
</svg>
),
},
];

const renderRoutesForNavbar = (navbarLinks) => {
Expand Down
Loading
Loading