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