diff --git a/client/app/Test/page.tsx b/client/app/Test/page.tsx new file mode 100644 index 0000000..fb0091b --- /dev/null +++ b/client/app/Test/page.tsx @@ -0,0 +1,19 @@ +"use client"; +import { useEffect, useState } from "react"; + +export default function Test() { + const ws = new WebSocket("ws://localhost:3001"); + const [data, setData] = useState(null); + useEffect(() => { + ws.onmessage = (e) => { + // a message was received + + setData(e.data); + }; + return () => { + ws.close(); + }; + }, []); + + return
{data}
; +} diff --git a/server/app.ts b/server/app.ts index 7b8c139..b670265 100644 --- a/server/app.ts +++ b/server/app.ts @@ -1,5 +1,4 @@ -import { loadEnvVariables } from "./util/functions"; - +import { loadEnvVariables, updateLeaderboard } from "./util/functions"; loadEnvVariables(__dirname); // keep the above imports always at the top they are used to load the environment variables @@ -9,7 +8,6 @@ import WebSocket from "ws"; import redisClient from "./util/redis"; var app = express(); - app.use("/", async function (req: Request, res: Response, next) { const count = await redisClient.incr("count"); @@ -17,14 +15,17 @@ app.use("/", async function (req: Request, res: Response, next) { }); function webSocketHandler(ws: WebSocket) { - ws.on("message", function (message: string) { - console.log("received: %s", message); - }); - let i = 0; - let interval = setInterval(() => { - ws.send(i); - i += 10; - }, 10000); + let interval = setInterval(async () => { + const leaderboard = await redisClient.get("leaderboard"); + if (!leaderboard) + return ws.send( + JSON.stringify({ + leaderboard: [], + }) + ); + + ws.send(leaderboard); + }, 1000); ws.send("something"); ws.on("close", function () { @@ -32,5 +33,8 @@ function webSocketHandler(ws: WebSocket) { clearInterval(interval); }); } + +setInterval(() => updateLeaderboard(redisClient as any, 468732), 4000); export default app; + export { webSocketHandler }; diff --git a/server/package-lock.json b/server/package-lock.json index 7b2678a..1fa6c1d 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,6 +10,9 @@ "dependencies": { "@types/express": "^4.17.17", "cookie-parser": "^1.4.6", + "cross-fetch": "^4.0.0", + "crypto": "^1.0.1", + "cryptos": "^1.0.0", "dotenv": "^16.3.1", "express": "^4.18.2", "morgan": "^1.10.0", @@ -512,6 +515,44 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/cryptos": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cryptos/-/cryptos-1.0.0.tgz", + "integrity": "sha512-f3QBXSYMhpDmy68vFp7nu6IH0Dd+okUJ9AMu9H0pDP8deUdjRWgG7bzkPbqzRo5INRdin38s8tI+cgo9AhvTCA==" + }, "node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -1342,6 +1383,11 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -1444,6 +1490,20 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", @@ -1891,6 +1951,34 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, + "cryptos": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cryptos/-/cryptos-1.0.0.tgz", + "integrity": "sha512-f3QBXSYMhpDmy68vFp7nu6IH0Dd+okUJ9AMu9H0pDP8deUdjRWgG7bzkPbqzRo5INRdin38s8tI+cgo9AhvTCA==" + }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -2504,6 +2592,11 @@ "nopt": "~1.0.10" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -2565,6 +2658,20 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", diff --git a/server/package.json b/server/package.json index 26452a4..8147ccd 100644 --- a/server/package.json +++ b/server/package.json @@ -16,6 +16,9 @@ "dependencies": { "@types/express": "^4.17.17", "cookie-parser": "^1.4.6", + "cross-fetch": "^4.0.0", + "crypto": "^1.0.1", + "cryptos": "^1.0.0", "dotenv": "^16.3.1", "express": "^4.18.2", "morgan": "^1.10.0", diff --git a/server/server.ts b/server/server.ts index d2c2a57..da237fa 100644 --- a/server/server.ts +++ b/server/server.ts @@ -30,7 +30,7 @@ app.use(function ( res.render("error"); }); -var port = normalizePort(process.env.PORT || "3000"); +var port = normalizePort(process.env.PORT || "3001"); app.set("port", port); /** diff --git a/server/util/functions.ts b/server/util/functions.ts index 295d984..6f8729b 100644 --- a/server/util/functions.ts +++ b/server/util/functions.ts @@ -1,7 +1,15 @@ import fs from "fs"; import dotenv from "dotenv"; import path from "path"; -const requiredEnvVariables = ["REDIS_PASS", "REDIS_HOST"]; +import { RedisClientType } from "redis"; +import crypto from "crypto"; +import fetch from "cross-fetch"; +const requiredEnvVariables = [ + "REDIS_PASS", + "REDIS_HOST", + "CF_SECRET", + "CF_API_KEY", +]; export function loadEnvVariables(dirname: string): void { try { const envFilePath = path.resolve(dirname, ".env"); @@ -42,3 +50,28 @@ export function normalizePort(val: string) { return false; } + +export async function updateLeaderboard( + redisClient: RedisClientType, + contestId: number +) { + const curr_time = Math.floor(new Date().getTime() / 1000); + const apiKey = process.env.CF_API_KEY; + const secret = process.env.CF_SECRET; + if (!apiKey || !secret) { + throw new Error("API key or secret is missing"); + } + + const signature = crypto + .createHash("sha512") + .update( + `123456/contest.standings?apiKey=${apiKey}&contestId=${contestId}&time=${curr_time}#${secret}` + ) + .digest("hex"); + + const requestUrl = `https://codeforces.com/api/contest.standings?contestId=${contestId}&apiKey=${apiKey}&time=${curr_time}&apiSig=123456${signature}`; + + const resp = await fetch(requestUrl); + const data = await resp.json(); + await redisClient.set("leaderboard", JSON.stringify(data)); +}