Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c0ce851
sign2 has arrived
adarshm11 Dec 31, 2025
5e453fa
ts lint frying me
adarshm11 Dec 31, 2025
8a96dd1
sign2 api tests
adarshm11 Jan 1, 2026
62a4397
bug fix attempt 1
adarshm11 Jan 1, 2026
cb8934b
bug fix attempt 2
adarshm11 Jan 1, 2026
352614b
attempt 3
adarshm11 Jan 1, 2026
f6326a4
pmo
adarshm11 Jan 1, 2026
3421e8e
should fix failing test plz
adarshm11 Jan 4, 2026
aca3b13
please
adarshm11 Jan 4, 2026
75b6e34
PLZ
adarshm11 Jan 4, 2026
8a7a6b7
error msg color
adarshm11 Jan 29, 2026
e84e266
sign2 url for prod
adarshm11 Jan 29, 2026
3fbc842
test the 409 error thing before merge
adarshm11 Jan 30, 2026
667a72a
fix tests when 409 tested
adarshm11 Jan 30, 2026
84e2223
make sure to fix tests that fail because of sign2 disabled returning 200
adarshm11 Jan 30, 2026
7e2ee2c
sign2 returns 409 when adding existing user
adarshm11 Jan 30, 2026
d972b78
sign2 url from env
adarshm11 Jan 30, 2026
001bd4a
todo: add sign2 url to frontend env in docker compose
adarshm11 Jan 30, 2026
2ab660b
update sign2 url
adarshm11 Apr 25, 2026
a35c2d2
axios to fetch from sign2 server
adarshm11 Apr 25, 2026
7fe93e7
add nginx proxying for emulator
adarshm11 Apr 25, 2026
dc1ff80
add websocket support for emulator iframe
adarshm11 Apr 25, 2026
1dcbaa2
upgrade websocket connection
adarshm11 Apr 25, 2026
d27146e
fix tornado/nginx casing mismatch
adarshm11 Apr 25, 2026
14db141
static img instead of iframe and websocket
adarshm11 Apr 25, 2026
71bbf6f
remove unused member from config.json
adarshm11 Apr 26, 2026
e0fd01c
remove api call based unit tests
adarshm11 Apr 26, 2026
1bed3e0
simplify api
adarshm11 Apr 27, 2026
42589f4
lowkirk don't need unit tests rn
adarshm11 Apr 27, 2026
bf2b408
simplify api setup
adarshm11 Apr 27, 2026
1c160a8
fix lint
adarshm11 Apr 27, 2026
2c2743c
fix clark route to be /leetcode
adarshm11 Apr 27, 2026
58fb5a6
extend interval of re-querying emulator
adarshm11 Apr 27, 2026
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
3 changes: 3 additions & 0 deletions api/config/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
"LED_SIGN": {
"ENABLED": false
},
"LEETCODE_LED_SIGN": {
"ENABLED": false
},
"membershipPayment": {
"API_KEY": "GO_AWAY_LOL"
},
Expand Down
86 changes: 86 additions & 0 deletions api/main_endpoints/routes/LeetCodeLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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');
Comment thread
adarshm11 marked this conversation as resolved.
const AuditLogActions = require('../util/auditLogActions.js');
const AuditLog = require('../models/AuditLog.js');
const membershipState = require('../../util/constants').MEMBERSHIP_STATE;
const { LEETCODE_LED_SIGN = {} } = require('../../config/config.json');
const axios = require('axios');
const LEETCODE_LED_SIGN_URL = process.env.LEETCODE_LED_SIGN_URL || 'http://localhost:12121';

// middleware to abstract token decoding and enabled check
router.use(async (req, res, next) => {
const decoded = await decodeToken(req, membershipState.OFFICER);
if (decoded.status !== OK) {
return res.sendStatus(decoded.status);
}

if (!LEETCODE_LED_SIGN.ENABLED) {
logger.warn('LeetCode Leaderboard is disabled, returning 200 to mock the service');
return res.sendStatus(OK);
}

res.locals.userId = decoded.token._id;
next();
});

// request handler for clark -> sign2 requests
const handleLeetCodeRequest = async (res, method, endpoint, data = null) => {
try {
const url = new URL(endpoint, LEETCODE_LED_SIGN_URL);
const response = await axios({ method, url: url.href, data });

if (response.data && 'error' in response.data) {
throw new Error(response.data.error);
}

return response.data;
} catch (err) {
logger.error(`Error with LeetCode Leaderboard at ${endpoint}: `, err.message || err);
res.status(SERVER_ERROR).send('Error communicating with LeetCode Leaderboard');
return null;
}
};

router.get('/', async (req, res) => {
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.

do we need this code still, if we have the router.use

does router.use take care of all this stuff now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

image we can do it like this but it doesn't seem aesthetically pleasing to me, thoughts

const data = await handleLeetCodeRequest(res, 'GET', '/getAllUsers');
if (data) {
return res.status(OK).json({ users: data.users });
}
});

router.post('/addUser', async (req, res) => {
const { username, firstName, lastName } = req.body;
const data = await handleLeetCodeRequest(res, 'POST', '/user/add', { username, firstName, lastName });

if (data) {
AuditLog.create({
userId: res.locals.userId,
action: AuditLogActions.ADD_LEETCODE_USER,
details: { username },
});
return res.sendStatus(OK);
}
});

router.post('/deleteUser', async (req, res) => {
const { username } = req.body;
const data = await handleLeetCodeRequest(res, 'POST', '/user/remove', { username });

if (data) {
AuditLog.create({
userId: res.locals.userId,
action: AuditLogActions.DELETE_LEETCODE_USER,
details: { username },
});
return res.sendStatus(OK);
}
});

module.exports = router;
2 changes: 2 additions & 0 deletions api/main_endpoints/util/auditLogActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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;
2 changes: 2 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ services:
- CLEEZY_URL=http://host.docker.internal:8000
- PRINTER_URL=http://host.docker.internal:14000
- LED_SIGN_URL=http://host.docker.internal:10000
- LEETCODE_LED_SIGN_URL=http://host.docker.internal:12121
- DISCORD_REDIRECT_URI=http://localhost/api/user/callback
- MAILER_API_URL=http://sce-cloud-api-dev:8082/cloudapi
- DATABASE_HOST=sce-mongodb-dev
Expand Down Expand Up @@ -79,6 +80,7 @@ services:
environment:
- NODE_ENV=dev
- REACT_APP_BASE_API_URL=http://localhost
- REACT_APP_LEETCODE_LED_SIGN_URL=/sign2/
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=${CHOKIDAR_USEPOLLING}
- WATCHPACK_POLLING=${WATCHPACK_POLLING}
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ services:
- CLEEZY_URL=http://cleezy-app.sce:8000
- PRINTER_URL=http://host.docker.internal:14000
- LED_SIGN_URL=http://host.docker.internal:10000
- LEETCODE_LED_SIGN_URL=http://host.docker.internal:12121
- DISCORD_REDIRECT_URI=https://sce.sjsu.edu/api/user/callback
- MAILER_API_URL=http://sce-cloud-api:8082/cloudapi
- DATABASE_HOST=sce-mongodb
Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ COPY ./package-lock.json /frontend/package-lock.json
# and instead define them inline here. We do this as multi-stage
# builds and environment variables from docker-compose.yml weren't working.
ENV REACT_APP_BASE_API_URL https://sce.sjsu.edu
ENV REACT_APP_SIGN2_URL /sign2/

RUN npm install

Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ COPY ./package-lock.json /frontend/package-lock.json
ARG NODE_ENV

ARG REACT_APP_BASE_API_URL
ARG REACT_APP_SIGN2_URL

# see https://stackoverflow.com/a/61215597
ENV DANGEROUSLY_DISABLE_HOST_CHECK true
Expand Down
8 changes: 8 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ http {
return 404 "oops! we couldnt load your interview question lol, i guess this ones free!";
}

location /sign2/ {
Comment thread
adarshm11 marked this conversation as resolved.
proxy_pass http://172.17.0.1:12122/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

#Load balancer
location /api/scevents/ {
resolver 127.0.0.11 valid=15s;
Expand Down
8 changes: 8 additions & 0 deletions nginx.dev.conf
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ http {
server {
listen 80;

location /sign2/ {
proxy_pass http://host.docker.internal:12122/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

#Load balancer
location /api {
proxy_pass http://main_endpoints;
Expand Down
69 changes: 69 additions & 0 deletions src/APIFunctions/LeetCodeLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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/', 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) {
status.error = false;
} else {
status.error = true;
status.statusCode = res.status;
}
} 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 }),
});
status.error = !res.ok;
} 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 @@ -144,6 +144,15 @@ export default function UserNavBar(props) {
</svg>
)
},
{
title: 'LeetCode Leaderboard',
route: '/leetcode',
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