Skip to content

Commit

Permalink
Merge pull request #126 from sliit-foss/development
Browse files Browse the repository at this point in the history
Spectator role addition and performance enhancements
  • Loading branch information
Akalanka47000 authored Sep 15, 2024
2 parents f15a6f8 + b47fe69 commit c9aa63c
Show file tree
Hide file tree
Showing 24 changed files with 135 additions and 85 deletions.
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import context from 'express-http-context';
import rateLimit from 'express-rate-limit';
import httpLogger from '@sliit-foss/http-logger';
import { moduleLogger } from '@sliit-foss/module-logger';
import compression from 'compression';
import cors from 'cors';
import crypto from 'crypto';
import helmet from 'helmet';
import { default as compression } from 'compression';
import { default as cors } from 'cors';
import { default as crypto } from 'crypto';
import { default as helmet } from 'helmet';
import { omit, pick } from 'lodash';
import { default as connectDB } from '@/database';
import { errorHandler, queryMapper, responseInterceptor } from '@/middleware';
Expand Down Expand Up @@ -79,8 +79,6 @@ global.__basedir = __dirname;

const port = process.env.PORT || 3000;

app.listen(port, (err) => {
if (!err) {
logger.info(`Bashaway server successfully started on port ${port}`);
}
app.listen(port, () => {
logger.info(`Bashaway server successfully started on port ${port}`);
});
2 changes: 2 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './question';
export * from './user';
6 changes: 6 additions & 0 deletions src/constants/question.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const DIFFICULTY = {
EASY: 'EASY',
MEDIUM: 'MEDIUM',
HARD: 'HARD',
EXTREME: 'EXTREME'
};
17 changes: 17 additions & 0 deletions src/constants/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const ROLE = {
ADMIN: 'ADMIN',
SPECTATOR: 'SPECTATOR',
GROUP: 'GROUP'
};

export const GENDER = {
MALE: 'M',
FEMALE: 'F',
OTHER: 'O',
NOT_SPECIFIED: '-'
};

export const MEAL_PREFERENCE = {
VEG: 'VEG',
NON_VEG: 'NON_VEG'
};
2 changes: 1 addition & 1 deletion src/controllers/auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import handlebars from 'handlebars';
import { default as createError } from 'http-errors';
import path from 'path';
import { default as path } from 'path';
import { getRegistrationDeadline } from '@/repository/settings';
import { blacklistToken } from '@/repository/token';
import { getOneUser } from '@/repository/user';
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/question.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ROLE } from '@/constants';
import { getDistinctSubmissions } from '@/repository/submission';

export const attachSubmissionAttributesToQuestion = async (question, user) => {
const submissions = await getDistinctSubmissions(question._id);
question.total_submissions = submissions.length;
if (user.role === 'GROUP') question.submitted = submissions.some((s) => s.user.toString() === user._id.toString());
if (user.role === ROLE.GROUP) question.submitted = submissions.some((s) => s.user.toString() === user._id.toString());
return question;
};
10 changes: 6 additions & 4 deletions src/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export const protect = asyncHandler(async (req) => {
req.user = user;
});

export const adminProtect = asyncHandler((req) => {
if (req.headers['x-api-key'] === process.env.API_ACCESS_KEY) return;
if (req.user.role !== 'ADMIN') throw new createError(403, 'You are not permitted to access this resource');
});
export const roleProtect = (roles = []) => {
return asyncHandler((req) => {
if (req.headers['x-api-key'] === process.env.API_ACCESS_KEY) return;
if (!roles.includes(req.user?.role)) throw new createError(403, 'You are not permitted to access this resource');
});
};
12 changes: 8 additions & 4 deletions src/models/question.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import mongoose from 'mongoose';
import mongoosePaginate from 'mongoose-paginate-v2';
import { DIFFICULTY } from '@/constants';

const QuestionSchema = new mongoose.Schema(
{
Expand All @@ -14,8 +15,9 @@ const QuestionSchema = new mongoose.Schema(
},
difficulty: {
type: String,
enum: ['EASY', 'MEDIUM', 'HARD', 'EXTREME'],
required: true
enum: Object.values(DIFFICULTY),
required: true,
index: true
},
constraints: [
{
Expand All @@ -28,12 +30,14 @@ const QuestionSchema = new mongoose.Schema(
},
enabled: {
type: Boolean,
default: true
default: true,
index: true
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
required: true,
index: true
},
creator_lock: {
type: Boolean,
Expand Down
6 changes: 4 additions & 2 deletions src/models/submission.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const SubmissionSchema = mongoose.Schema(
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
required: true,
index: true
},
question: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Question',
required: true
required: true,
index: true
},
link: {
type: String,
Expand Down
14 changes: 6 additions & 8 deletions src/models/user.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import mongoose from 'mongoose';
import aggregatePaginate from 'mongoose-aggregate-paginate-v2';

export const genders = ['M', 'F', 'O', '-'];

export const mealPreferences = ['VEG', 'NON_VEG'];
import { GENDER, MEAL_PREFERENCE, ROLE } from '@/constants';

const UserSchema = new mongoose.Schema(
{
Expand Down Expand Up @@ -45,8 +42,9 @@ const UserSchema = new mongoose.Schema(
},
role: {
type: String,
enum: ['ADMIN', 'GROUP'],
default: 'GROUP'
enum: Object.values(ROLE),
default: ROLE.GROUP,
index: true
},
members: {
type: [
Expand Down Expand Up @@ -75,11 +73,11 @@ const UserSchema = new mongoose.Schema(
},
gender: {
type: String,
enum: genders
enum: Object.values(GENDER)
},
meal_preference: {
type: String,
enum: mealPreferences
enum: Object.values(MEAL_PREFERENCE)
},
student_id_url: {
type: String
Expand Down
7 changes: 4 additions & 3 deletions src/repository/question/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import mongoose from 'mongoose';
import { ROLE } from '@/constants';
import Question from '@/models/question';
import { prefixObjectKeys } from '@/utils';
import { questionFilters } from './util';
Expand Down Expand Up @@ -42,10 +43,10 @@ export const getQuestionById = (id, user) => {
_id: { $eq: new ObjectId(id) },
$or: [{ creator_lock: false }, { creator_lock: true, creator: user._id }]
};
if (user.role !== 'ADMIN') {
if (user.role !== ROLE.ADMIN) {
filters.enabled = true;
}
let query = Question.findOne(filters).lean();
const query = Question.findOne(filters).lean();
return query.exec();
};

Expand Down Expand Up @@ -92,7 +93,7 @@ export const getQuestionSubmissions = (user, teamFilters, submissionFilters) =>
{
$match: {
...prefixObjectKeys(teamFilters, 'user.'),
'user.role': 'GROUP'
'user.role': ROLE.GROUP
}
}
]
Expand Down
14 changes: 5 additions & 9 deletions src/repository/question/util.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { ROLE } from '@/constants';

export const questionFilters = (user, baseFilters = {}) => {
let filter;
if (user.role === 'ADMIN') {
filter = {
$or: [{ creator_lock: false }, { creator_lock: true, creator: user._id }],
$and: [baseFilters]
};
} else {
if (user.role !== ROLE.ADMIN) {
baseFilters.enabled = true;
}
filter = {
const filter = {
$or: [{ creator_lock: false }, { creator_lock: true, creator: user._id }],
$and: [baseFilters]
...baseFilters
};
return filter;
};
6 changes: 3 additions & 3 deletions src/repository/submission.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { moduleLogger } from '@sliit-foss/module-logger';
import { isUndefined } from 'lodash';
import { ROLE } from '@/constants';
import { Submission } from '@/models';
import { prefixObjectKeys } from '@/utils';

Expand All @@ -15,7 +15,7 @@ export const insertSubmission = (userId, question, link) => {

export const getSubmissions = ({ sort = {}, filter = {}, page, limit = 10 }) => {
const populate = ['user', 'graded_by', 'question'];
if (!isUndefined(filter.graded)) {
if (filter.graded !== undefined) {
if (filter.graded) {
filter.$or = [{ graded_by: { $ne: null } }, { automatically_graded: true }];
} else {
Expand Down Expand Up @@ -72,7 +72,7 @@ export const getTeamSubmissions = (filters = {}, userFilters = {}) => {
{
$match: {
...prefixObjectKeys(userFilters, 'user.'),
'user.role': 'GROUP'
'user.role': ROLE.GROUP
}
},
{
Expand Down
5 changes: 3 additions & 2 deletions src/repository/user.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { moduleLogger } from '@sliit-foss/module-logger';
import { ROLE } from '@/constants';
import { User } from '@/models';

const logger = moduleLogger('User-repository');
Expand Down Expand Up @@ -87,7 +88,7 @@ export const getAllUniverstyUserGroups = (filters = {}) => {
{
$match: {
...filters,
role: 'GROUP',
role: ROLE.GROUP,
is_verified: true
}
},
Expand Down Expand Up @@ -119,7 +120,7 @@ export const getLeaderboardData = (filters = {}, submissionFilters = {}) => {
{
$match: {
...filters,
role: 'GROUP',
role: ROLE.GROUP,
is_verified: true,
is_active: true
}
Expand Down
7 changes: 4 additions & 3 deletions src/routes/index.routes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from 'express';
import { adminProtect, protect } from '@/middleware/auth';
import { ROLE } from '@/constants';
import { protect, roleProtect } from '@/middleware/auth';
import authRouter from './auth.routes';
import dashboardRouter from './dashboard.routes';
import leaderboardRouter from './leaderboard.routes';
Expand All @@ -15,9 +16,9 @@ router.use('/auth', authRouter);
router.use('/submissions', protect, submissionRouter);
router.use('/users', protect, userRouter);
router.use('/questions', protect, questionRouter);
router.use('/dashboard', protect, adminProtect, dashboardRouter);
router.use('/dashboard', protect, roleProtect([ROLE.ADMIN, ROLE.SPECTATOR]), dashboardRouter);
router.use('/leaderboard', leaderboardRouter);
router.use('/settings', protect, settingRouter);
router.use('/storage', protect, adminProtect, storageRouter);
router.use('/storage', protect, roleProtect([ROLE.ADMIN]), storageRouter);

export default router;
9 changes: 5 additions & 4 deletions src/routes/question.routes.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import express from 'express';
import { tracedAsyncHandler } from '@sliit-foss/functions';
import { Segments, celebrate } from 'celebrate';
import { ROLE } from '@/constants';
import {
createNewQuestion,
deleteOldQuestion,
getAllQuestions,
getQuestionById,
updateQuestion
} from '@/controllers/question';
import { adminProtect } from '@/middleware/auth';
import { roleProtect } from '@/middleware/auth';
import { addQuestionSchema, questionIdSchema, updateQuestionSchema } from '@/validations/question';

const questions = express.Router();
Expand All @@ -17,20 +18,20 @@ questions.get('/', tracedAsyncHandler(getAllQuestions));
questions.post(
'/',
celebrate({ [Segments.BODY]: addQuestionSchema }),
adminProtect,
roleProtect([ROLE.ADMIN]),
tracedAsyncHandler(createNewQuestion)
);
questions.get('/:question_id', celebrate({ [Segments.PARAMS]: questionIdSchema }), tracedAsyncHandler(getQuestionById));
questions.patch(
'/:question_id',
celebrate({ [Segments.PARAMS]: questionIdSchema, [Segments.BODY]: updateQuestionSchema }),
adminProtect,
roleProtect([ROLE.ADMIN]),
tracedAsyncHandler(updateQuestion)
);
questions.delete(
'/:question_id',
celebrate({ [Segments.PARAMS]: questionIdSchema }),
adminProtect,
roleProtect([ROLE.ADMIN]),
tracedAsyncHandler(deleteOldQuestion)
);

Expand Down
5 changes: 3 additions & 2 deletions src/routes/setting.routes.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import express from 'express';
import { tracedAsyncHandler } from '@sliit-foss/functions';
import { Segments, celebrate } from 'celebrate';
import { ROLE } from '@/constants';
import { getSettings, updateSettings } from '@/controllers/settings';
import { adminProtect } from '@/middleware';
import { roleProtect } from '@/middleware';
import { updateSettingsSchema } from '@/validations/settings';

const settings = express.Router();
Expand All @@ -12,7 +13,7 @@ settings.get('/', tracedAsyncHandler(getSettings));
settings.patch(
'/',
celebrate({ [Segments.PARAMS]: updateSettingsSchema, [Segments.BODY]: updateSettingsSchema }),
adminProtect,
roleProtect([ROLE.ADMIN]),
tracedAsyncHandler(updateSettings)
);

Expand Down
5 changes: 3 additions & 2 deletions src/routes/submission.routes.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import express from 'express';
import { tracedAsyncHandler } from '@sliit-foss/functions';
import { Segments, celebrate } from 'celebrate';
import { ROLE } from '@/constants';
import { create, grade, view } from '@/controllers/submission';
import { adminProtect } from '@/middleware/auth';
import { roleProtect } from '@/middleware/auth';
import {
submissionCreateSchema,
submissionIdSchema,
Expand All @@ -20,7 +21,7 @@ submissions.patch(
[Segments.PARAMS]: submissionIdSchema,
[Segments.BODY]: submissionUpdateSchema
}),
adminProtect,
roleProtect([ROLE.ADMIN]),
tracedAsyncHandler(grade)
);

Expand Down
Loading

0 comments on commit c9aa63c

Please sign in to comment.