Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions config/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
module.exports = {
discordUnverifiedRoleId: "1103047289330745386",
discordDeveloperRoleId: "915490782939582485",
discordNewComersChannelId: "709080951824842783",
discordMavenRoleId: "875564640438997043",
discordMissedUpdatesRoleId: "1183553844811153458",
userToken: {
Expand Down
1 change: 1 addition & 0 deletions config/staging.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
discordDeveloperRoleId: "1121445071213056071",
discordMavenRoleId: "1152361736456896586",
discordMissedUpdatesRoleId: "1184201657404362772",
discordNewComersChannelId: "896184507080769559",
enableFileLogs: false,
enableConsoleLogs: true,

Expand Down
8 changes: 8 additions & 0 deletions constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ const daysOfWeek = {
sat: 6,
};

//This is the headers used in server sent events APIs
const HEADERS_FOR_SSE = {
"Content-Type": "text/event-stream",
Connection: "keep-alive",
"Cache-Control": "no-cache",
};

module.exports = {
DOCUMENT_WRITE_SIZE,
daysOfWeek,
HEADERS_FOR_SSE,
};
8 changes: 7 additions & 1 deletion controllers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const githubAuthCallback = (req, res, next) => {
let isMobileApp = false;
const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl"));
let authRedirectionUrl = rdsUiUrl;
let devMode = false;
if ("state" in req.query) {
try {
const redirectUrl = new URL(req.query.state);
Expand All @@ -54,6 +55,7 @@ const githubAuthCallback = (req, res, next) => {
if (`.${redirectUrl.hostname}`.endsWith(`.${rdsUiUrl.hostname}`)) {
// Matching *.realdevsquad.com
authRedirectionUrl = redirectUrl;
devMode = Boolean(redirectUrl.searchParams.get("dev"));
} else {
logger.error(`Malicious redirect URL provided URL: ${redirectUrl}, Will redirect to RDS`);
}
Expand Down Expand Up @@ -88,7 +90,11 @@ const githubAuthCallback = (req, res, next) => {
sameSite: "lax",
});

if (incompleteUserDetails) authRedirectionUrl = "https://my.realdevsquad.com/new-signup";
if (!devMode) {
// TODO: Revisit incompleteUserDetails redirect condition
if (incompleteUserDetails) authRedirectionUrl = "https://my.realdevsquad.com/new-signup";
}

if (isMobileApp) {
const newUrl = new URL(authRedirectionUrl);
newUrl.searchParams.set("token", token);
Expand Down
4 changes: 2 additions & 2 deletions controllers/issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ const githubService = require("../services/githubService");

const getIssues = async (req, res) => {
try {
const { q: queryString, dev } = req.query;
const { q: queryString } = req.query;
let issues = {};
const githubOrg = config.get("githubApi.org");
const githubIssuerUrlPattern = new RegExp(`^https://github.com/${githubOrg}/.+/issues/\\d+$`);

if (githubIssuerUrlPattern.test(queryString) && dev === "true") {
if (githubIssuerUrlPattern.test(queryString)) {
const url = new URL(queryString);
const issueUrlPaths = url.pathname.split("/");
const repositoryName = issueUrlPaths[2];
Expand Down
63 changes: 63 additions & 0 deletions controllers/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const crypto = require("crypto");
import { Request } from "express";

import { Client, Question } from "../types/questions";
import { CustomRequest, CustomResponse } from "../types/global";

const { HEADERS_FOR_SSE } = require("../constants/constants");
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");

const questionQuery = require("../models/questions");

/* Refer to limitation of this clients array here(in the limitations section of doc) - https://github.com/Real-Dev-Squad/website-www/wiki/%5BFeature%5D-%E2%80%90-Realtime-Word-Cloud-Questions-Answers-Feature*/
let clients: Client[] = [];

function sendQuestionToAll(newQuestion: Question, res: CustomResponse) {
clients.forEach((client) => client.res.write(`data: ${JSON.stringify(newQuestion)}\n\n`));

res.status(201).send({
message: "Question created and sent successfully to connected peers",
data: newQuestion,
});
}

const createQuestion = async (req: CustomRequest, res: CustomResponse) => {
try {
const questionId = crypto.randomUUID({ disableEntropyCache: true });
const question = await questionQuery.createQuestion({ ...req.body, id: questionId });
return sendQuestionToAll(question, res);
} catch (error) {
logger.error(`Error while creating question: ${error}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

// eslint-disable-next-line consistent-return
const getQuestions = async (req: Request, res: CustomResponse) => {
try {
res.writeHead(200, HEADERS_FOR_SSE);

// for initial sse(server sent event) connection sending null data
const data = `data: null\n\n`;
res.write(data);

const clientId = crypto.randomUUID({ disableEntropyCache: true });

const newClient = {
id: clientId,
res,
};

clients.push(newClient);

req.on("close", () => {
logger.info(`${clientId} Connection closed`);
clients = clients.filter((client) => client.id !== clientId);
});
} catch (error) {
logger.error(`Error while getting question: ${error}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

module.exports = { createQuestion, getQuestions };
27 changes: 15 additions & 12 deletions controllers/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ const { OLD_ACTIVE, OLD_BLOCKED, OLD_PENDING } = TASK_STATUS_OLD;
const { IN_PROGRESS, BLOCKED, SMOKE_TESTING, ASSIGNED } = TASK_STATUS;
const { INTERNAL_SERVER_ERROR, SOMETHING_WENT_WRONG } = require("../constants/errorMessages");
const dependencyModel = require("../models/tasks");
const { transformQuery } = require("../utils/tasks");
const { transformQuery, transformTasksUsersQuery } = require("../utils/tasks");
const { getPaginatedLink } = require("../utils/helper");
const { updateUserStatusOnTaskUpdate, updateStatusOnTaskCompletion } = require("../models/userStatus");
const dataAccess = require("../services/dataAccessLayer");
const { parseSearchQuery } = require("../utils/tasks");
const { addTaskCreatedAtAndUpdatedAtFields } = require("../services/tasks");
const { RQLQueryParser } = require("../utils/RQLParser");
const { getMissedProgressUpdatesUsers } = require("../models/discordactions");
const { daysOfWeek } = require("../constants/constants");
const { logType } = require("../constants/logs");

/**
Expand Down Expand Up @@ -484,17 +483,21 @@ const getUsersHandler = async (req, res) => {
try {
const { size, cursor, q: queryString } = req.query;
const rqlParser = new RQLQueryParser(queryString);
const { "days-count": daysGap, weekday, date, status } = rqlParser.getFilterQueries();
if (!!status && status.length === 1 && status[0].value === tasksUsersStatus.MISSED_UPDATES) {
if (daysGap && daysGap > 1) {
return res.boom.badRequest("number of days gap provided cannot be greater than 1");
}
const filterQueries = rqlParser.getFilterQueries();
const {
dateGap,
weekdayList,
dateList,
status,
size: transformedSize,
} = transformTasksUsersQuery({ ...filterQueries, size });
if (status === tasksUsersStatus.MISSED_UPDATES) {
const response = await getMissedProgressUpdatesUsers({
cursor: cursor,
size: size && Number.parseInt(size),
excludedDates: date?.map((date) => Number.parseInt(date.value)),
excludedDays: weekday?.map((day) => daysOfWeek[day.value]),
dateGap: !!daysGap && daysGap.length === 1 ? Number.parseInt(daysGap[0].value) : null,
size: transformedSize,
excludedDates: dateList,
excludedDays: weekdayList,
dateGap: dateGap,
});

if (response.error) {
Expand All @@ -504,7 +507,7 @@ const getUsersHandler = async (req, res) => {
.status(200)
.json({ message: "Discord details of users with status missed updates fetched successfully", data: response });
} else {
return res.boom.badRequest("Unknown type and query");
return res.boom.badRequest(`Invalid status: ${status}`);
}
} catch (error) {
const taskRequestLog = {
Expand Down
13 changes: 3 additions & 10 deletions controllers/tasksRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,9 @@ const usersUtils = require("../utils/users");

const fetchTaskRequests = async (_, res) => {
try {
const { dev } = _.query;
let data;
if (dev === "true") {
data = await taskRequestsModel.fetchPaginatedTaskRequests(_.query);
if (data.error) {
return res.status(data.statusCode).json(data);
}
} else {
data = {};
data.data = await taskRequestsModel.fetchTaskRequests(true);
const data = await taskRequestsModel.fetchPaginatedTaskRequests(_.query);
if (data.error) {
return res.status(data.statusCode).json(data);
}

return res.status(200).json({
Expand Down
22 changes: 22 additions & 0 deletions middlewares/validators/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NextFunction } from "express";
import { CustomRequest, CustomResponse } from "../../types/global";
const joi = require("joi");

const createQuestion = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
const schema = joi.object({
question: joi.string().required(),
createdBy: joi.string().required(),
eventId: joi.string().required(),
maxCharacters: joi.optional(),
});

try {
await schema.validateAsync(req.body);
next();
} catch (error) {
logger.error(`Error validating question: ${error}`);
res.boom.badRequest(error.details[0].message);
}
};

module.exports = { createQuestion };
5 changes: 4 additions & 1 deletion middlewares/validators/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ const getUsersValidator = async (req, res, next) => {
const queryParamsSchema = joi.object().keys({
cursor: joi.string().optional(),
q: joi.string().optional(),
size: joi.number().integer().min(1).max(2013),
size: joi.number().integer().min(1).max(2000),
});
const filtersSchema = joi.object().keys({
status: joi
Expand All @@ -225,6 +225,7 @@ const getUsersValidator = async (req, res, next) => {
operator: joi.string().valid(Operators.EXCLUDE),
})
)
.max(1)
.optional(),
weekday: joi
.array()
Expand All @@ -234,6 +235,7 @@ const getUsersValidator = async (req, res, next) => {
operator: joi.string().valid(Operators.EXCLUDE),
})
)
.max(7)
.optional(),
date: joi
.array()
Expand All @@ -243,6 +245,7 @@ const getUsersValidator = async (req, res, next) => {
operator: joi.string().valid(Operators.EXCLUDE),
})
)
.max(20)
.optional(),
});

Expand Down
2 changes: 1 addition & 1 deletion middlewares/validators/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const updateUser = async (req, res, next) => {
roles: joi.object().keys({
designer: joi.boolean().optional(),
maven: joi.boolean().optional(),
productmanager: joi.boolean().optional(),
product_manager: joi.boolean().optional(),
}),
});

Expand Down
34 changes: 34 additions & 0 deletions models/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const admin = require("firebase-admin");

import { Question, QuestionBody } from "../types/questions";

const firestore = require("../utils/firestore");

const questionModel = firestore.collection("questions");

const createQuestion = async (questionData: QuestionBody): Promise<Question> => {
try {
const { eventId: event_id, createdBy: created_by, question, maxCharacters: max_characters } = questionData;
const questionRef = questionModel.doc(questionData.id);
const createdAndUpdatedAt = admin.firestore.Timestamp.now();

await questionRef.set({
question,
event_id,
created_by,
max_characters: max_characters || null,
created_at: createdAndUpdatedAt,
updated_at: createdAndUpdatedAt,
});
const questionSnapshot = await questionRef.get();
const id = questionSnapshot.id;
const questionFromDB = questionSnapshot.data();

return { id, ...questionFromDB };
} catch (error) {
logger.error(`Some error occured while creating question ${error}`);
throw error;
}
};

module.exports = { createQuestion };
8 changes: 4 additions & 4 deletions routes/arts.js → routes/arts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const express = require("express");
import express from "express";
const router = express.Router();
const authenticate = require("../middlewares/authenticate");
const arts = require("../controllers/arts");
const artValidator = require("../middlewares/validators/arts");
import authenticate from "../middlewares/authenticate";
import arts from "../controllers/arts";
import artValidator from "../middlewares/validators/arts";

router.get("/", arts.fetchArts);
router.get("/user/self", authenticate, arts.getSelfArts);
Expand Down
8 changes: 4 additions & 4 deletions routes/auctions.js → routes/auctions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const express = require("express");
import express from "express";
import authenticate from "../middlewares/authenticate";
import auction from "../controllers/auction";
import auctionValidator from "../middlewares/validators/auctions";
const router = express.Router();
const authenticate = require("../middlewares/authenticate");
const auction = require("../controllers/auction");
const auctionValidator = require("../middlewares/validators/auctions");

router.get("/:id", auction.fetchAuctionById);
router.get("/", auction.fetchAvailableAuctions);
Expand Down
10 changes: 5 additions & 5 deletions routes/auth.js → routes/auth.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const express = require("express");
import express from "express";
const router = express.Router();
const auth = require("../controllers/auth");
const authenticate = require("../middlewares/authenticate");
const userDeviceInfoValidator = require("../middlewares/validators/qrCodeAuth");
const qrCodeAuthValidator = require("../middlewares/validators/qrCodeAuth");
import auth from "../controllers/auth";
import authenticate from "../middlewares/authenticate";
import userDeviceInfoValidator from "../middlewares/validators/qrCodeAuth";
import qrCodeAuthValidator from "../middlewares/validators/qrCodeAuth";

router.get("/github/login", auth.githubAuthLogin);

Expand Down
8 changes: 4 additions & 4 deletions routes/challenges.js → routes/challenges.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const express = require("express");
import express from "express";
const router = express.Router();
const authenticate = require("../middlewares/authenticate");
const challenges = require("../controllers/challenge");
const { createChallenge } = require("../middlewares/validators/challenges");
import authenticate from "../middlewares/authenticate";
import challenges from "../controllers/challenge";
import { createChallenge } from "../middlewares/validators/challenges";

router.get("/", authenticate, challenges.fetchChallenges);
router.post("/", authenticate, createChallenge, challenges.createChallenge);
Expand Down
6 changes: 3 additions & 3 deletions routes/cloudflareCache.js → routes/cloudflareCache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const express = require("express");
import express from "express";
const router = express.Router();
const cloudflareCache = require("../controllers/cloudflareCache");
const authenticate = require("../middlewares/authenticate");
import cloudflareCache from "../controllers/cloudflareCache";
import authenticate from "../middlewares/authenticate";

router.get("/", authenticate, cloudflareCache.fetchPurgedCacheMetadata);
router.post("/", authenticate, cloudflareCache.purgeCache);
Expand Down
4 changes: 2 additions & 2 deletions routes/contributions.js → routes/contributions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const express = require("express");
import express from "express";
const router = express.Router();
const contributions = require("../controllers/contributions");
import contributions from "../controllers/contributions";

router.get("/:username", contributions.getUserContributions);

Expand Down
Loading