Skip to content

Commit

Permalink
Add ranking freeze feature (#21)
Browse files Browse the repository at this point in the history
* This reverts commit 627280a.

* This reverts commit 5284bdb.

* This reverts commit 688eded.
  • Loading branch information
JakubZojdzik authored Apr 19, 2024
1 parent 627280a commit 68b68e9
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ COPY ./competition.yaml /etc/sok/

ENV SOK_CONFIG=/etc/sok/competition.yaml

EXPOSE 8080
EXPOSE 3000

CMD npm start
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ To get started with SOK API, follow these steps:

Docker Compose will run the following containers:

- **api** - The API server on `8080` port.
- **api** - The API server on `3000` port.
- **postgres** - PostgreSQL database server on port specified in `PG_PORT` environment variable.
- **pgadmin** - pgAdmin web interface on port `5050`.

Expand Down
6 changes: 4 additions & 2 deletions competition.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
title: mądrALO
rules: <p>zasady 123123</p><p><br></p><p>123</p><p>zasady</p>
startTime: 2023-09-06T20:55
endTime: 2023-09-27T20:55
startTime: 2024-04-12T20:55
endTime: 2024-04-24T20:55
freezeTime: 2024-04-22T00:00
freeze: 'true'
4 changes: 1 addition & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.9'

services:
api:
restart: on-failure
Expand All @@ -9,7 +7,7 @@ services:
depends_on:
- postgres
ports:
- 8080:8080
- 3000:3000
volumes:
- ./:/app

Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const submitsRouter = require('./src/routes/submits.route');
const competitionRouter = require('./src/routes/competition.route');

const app = express();
const port = 8080;
const port = 3000;

app.use(cors());
app.use(
Expand Down
12 changes: 6 additions & 6 deletions src/controllers/announcements.controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const pool = require('../services/db.service');
const isAdmin = require('../utils/isAdmin');
const isAdminUtil = require('../utils/isAdminUtil');

const getCurrent = async (request, response) => {
const results = await pool.query("SELECT * FROM announcements WHERE added <= now() AT TIME ZONE 'CEST' ORDER BY added DESC");
Expand All @@ -12,7 +12,7 @@ const getInactive = async (request, response) => {
return response.status(403).send('Not permitted!');
}

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('Not permitted');
}
Expand All @@ -30,7 +30,7 @@ const getById = async (request, response) => {
const { annId } = request.query;

let tmp = " AND added <= now() AT TIME ZONE 'CEST'";
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (admin) {
tmp = '';
} else if (new Date(Date.parse(process.env.COMPETITION_START)) >= new Date().fixZone()) {
Expand All @@ -51,7 +51,7 @@ const getById = async (request, response) => {

const add = async (request, response) => {
const { id, title, content, author, added } = request.body;
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand All @@ -70,7 +70,7 @@ const add = async (request, response) => {

const edit = async (request, response) => {
const { id, annId, title, content, author, added } = request.body;
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand All @@ -89,7 +89,7 @@ const edit = async (request, response) => {

const remove = async (request, response) => {
const { id, annId } = request.body;
const admin = isAdmin(id);
const admin = isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand Down
18 changes: 9 additions & 9 deletions src/controllers/challenges.controller.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fs = require('fs');
const yaml = require('js-yaml');
const pool = require('../services/db.service');
const isAdmin = require('../utils/isAdmin');
const isAdminUtil = require('../utils/isAdminUtil');
const logSubmit = require('../utils/logSubmit');

const competitionConf = yaml.load(fs.readFileSync(process.env.SOK_CONFIG, 'utf8'));
Expand Down Expand Up @@ -55,7 +55,7 @@ const compAnswers = async (chall, answer, usrId) => {
const endTime = new Date(Date.parse(competitionConf.endTime));
const currentTime = new Date().fixZone();

const admin = await isAdmin(usrId);
const admin = await isAdminUtil(usrId);
if (admin) {
if (chall.answer === answer) {
return { correct: true, info: '' };
Expand Down Expand Up @@ -93,7 +93,7 @@ const compAnswers = async (chall, answer, usrId) => {

const getCurrent = async (request, response) => {
const { id } = request.body;
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
const startTime = new Date(Date.parse(competitionConf.startTime));
const currentTime = new Date().fixZone();

Expand All @@ -117,7 +117,7 @@ const getInactive = async (request, response) => {
if (!id) {
return response.status(403).send('Not permited!');
}
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('Not permited');
}
Expand All @@ -139,7 +139,7 @@ const getById = async (request, response) => {
const startTime = new Date(Date.parse(competitionConf.startTime));
const currentTime = new Date().fixZone();

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
let tmp = " AND start <= now() AT TIME ZONE 'CEST'";
if (admin) {
tmp = '';
Expand Down Expand Up @@ -190,7 +190,7 @@ const sendAnswer = async (request, response) => {
const correctAnswer = async (request, response) => {
const { id } = request.body;
const { challId } = request.query;
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand All @@ -209,7 +209,7 @@ const correctAnswer = async (request, response) => {
const add = async (request, response) => {
const { id, title, content, author, points, answer, start } = request.body;

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand All @@ -229,7 +229,7 @@ const add = async (request, response) => {
const edit = async (request, response) => {
const { id, title, content, author, points, answer, solves, start, challId } = request.body;

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand All @@ -249,7 +249,7 @@ const edit = async (request, response) => {
const remove = async (request, response) => {
const { id, challId } = request.body;

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You have to be admin');
}
Expand Down
20 changes: 16 additions & 4 deletions src/controllers/competition.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require('fs');
const yaml = require('js-yaml');
const multer = require('multer');
const path = require('path');
const isAdmin = require('../utils/isAdmin');
const isAdminUtil = require('../utils/isAdminUtil');

const competitionConf = yaml.load(fs.readFileSync(process.env.SOK_CONFIG, 'utf8'));

Expand All @@ -14,17 +14,27 @@ const getTimeRange = (request, response) => {
response.status(200).send({ start: competitionConf.startTime, end: competitionConf.endTime });
};

const getFreeze = (request, response) => {
response.status(200).send(competitionConf.freeze);
};

const getFreezeTime = (request, response) => {
response.status(200).send(competitionConf.freezeTime);
};

const edit = async (request, response) => {
const { id, title, rules, start, end } = request.body;
const { id, title, rules, start, end, freeze, freezeTime } = request.body;

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You dont have permissions');
}
competitionConf.title = title;
competitionConf.rules = rules;
competitionConf.startTime = start;
competitionConf.endTime = end;
competitionConf.freeze = freeze;
competitionConf.freezeTime = freezeTime;
fs.writeFileSync(process.env.SOK_CONFIG, yaml.dump(competitionConf));
return response.status(201).send('Competition updated');
};
Expand All @@ -43,7 +53,7 @@ const upload = multer({ storage });
const uploadIcon = async (request, response) => {
const { id } = request.body;

const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You dont have permissions');
}
Expand All @@ -64,6 +74,8 @@ module.exports = {
getTitle,
getRules,
getTimeRange,
getFreeze,
getFreezeTime,
edit,
uploadIcon,
icon,
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/submits.controller.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const pool = require('../services/db.service');
const isAdmin = require('../utils/isAdmin');
const isAdminUtil = require('../utils/isAdminUtil');

const getAll = async (request, response) => {
const { id } = request.body;
const admin = await isAdmin(id);
const admin = await isAdminUtil(id);
if (!admin) {
return response.status(403).send('You dont have permissions');
}
Expand Down
70 changes: 51 additions & 19 deletions src/controllers/users.controller.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
const bcrypt = require('bcryptjs');
const fs = require('fs');
const yaml = require('js-yaml');
const dotenv = require('dotenv');
const jwt = require('jsonwebtoken');
const nodemailer = require('nodemailer');
const pool = require('../services/db.service');
const isAdminUtil = require('../utils/isAdminUtil');

dotenv.config();
const competitionConf = yaml.load(fs.readFileSync(process.env.SOK_CONFIG, 'utf8'));

const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
Expand Down Expand Up @@ -173,25 +177,53 @@ const solves = async (request, response) => {
};

const ranking = async (request, response) => {
const dbRes = await pool.query(
`
SELECT
u.name,
COALESCE(SUM(CASE WHEN s.correct = true THEN c.points ELSE -1 END), 0) AS points
FROM
users u
JOIN
submits s ON u.id = s.usr_id
JOIN
challenges c ON s.chall_id = c.id
WHERE
admin = 0 AND verified = true
GROUP BY
u.id, u.name
ORDER BY
points DESC, MAX(s.sent) ASC, u.name;
`,
);
const { id } = request.body;
const admin = await isAdminUtil(id);
const freeze = competitionConf.endTime;
let dbRes;
if (freeze === 'true' && !admin) {
const freezeTime = new Date(Date.parse(competitionConf.freezeTime));
dbRes = await pool.query(
`
SELECT
u.name,
COALESCE(SUM(CASE WHEN s.correct = true THEN c.points ELSE -1 END), 0) AS points
FROM
users u
JOIN
submits s ON u.id = s.usr_id
JOIN
challenges c ON s.chall_id = c.id
WHERE
admin = 0 AND verified = true AND sent <= $1
GROUP BY
u.id, u.name
ORDER BY
points DESC, MAX(s.sent) ASC, u.name;
`,
[freezeTime.toISOString()],
);
} else {
dbRes = await pool.query(
`
SELECT
u.name,
COALESCE(SUM(CASE WHEN s.correct = true THEN c.points ELSE -1 END), 0) AS points
FROM
users u
JOIN
submits s ON u.id = s.usr_id
JOIN
challenges c ON s.chall_id = c.id
WHERE
admin = 0 AND verified = true
GROUP BY
u.id, u.name
ORDER BY
points DESC, MAX(s.sent) ASC, u.name;
`,
);
}
const dbRows = dbRes.rows;
for (let i = 0; i < dbRes.rows.length; i += 1) {
dbRows[i].position = i + 1;
Expand Down
2 changes: 2 additions & 0 deletions src/routes/competition.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ router.use('/uploadIcon', authenticateToken);
router.get('/title', errorHandler(competitionController.getTitle));
router.get('/rules', errorHandler(competitionController.getRules));
router.get('/timeRange', errorHandler(competitionController.getTimeRange));
router.get('/freeze', errorHandler(competitionController.getFreeze));
router.get('/freezeTime', errorHandler(competitionController.getFreezeTime));
router.get('/icon', errorHandler(competitionController.icon));

router.post('/edit', errorHandler(competitionController.edit));
Expand Down
1 change: 1 addition & 0 deletions src/routes/users.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const errorHandler = require('../middlewares/errorHandler');

router.use('/solves', authenticateToken);
router.use('/islogged', authenticateToken);
router.use('/ranking', authenticateToken);
router.use('/isAdmin', authenticateToken);

router.get('/solves', errorHandler(usersController.solves));
Expand Down
4 changes: 2 additions & 2 deletions src/utils/isAdmin.js → src/utils/isAdminUtil.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const pool = require('../services/db.service');

const isAdmin = async (usrId) => {
const isAdminUtil = async (usrId) => {
const dbRes = await pool.query('SELECT admin FROM users WHERE id=$1 AND verified = true', [usrId]);
if (!dbRes || !dbRes.rows || !dbRes.rows.length) {
return false;
}
return dbRes.rows[0].admin === 2;
};

module.exports = isAdmin;
module.exports = isAdminUtil;

0 comments on commit 68b68e9

Please sign in to comment.