Skip to content

Commit 4f10d7d

Browse files
committed
Merge branch 'task-dependency-v3' of https://github.com/Real-Dev-Squad/website-backend into task-dependency-v3
2 parents 5bde29c + 817fabe commit 4f10d7d

File tree

26 files changed

+3048
-1454
lines changed

26 files changed

+3048
-1454
lines changed

constants/monitor.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const RESOURCE_CREATED_SUCCESSFULLY = "Resource created successfully.";
2+
const RESOURCE_UPDATED_SUCCESSFULLY = "Resource updated successfully.";
3+
const RESOURCE_RETRIEVED_SUCCESSFULLY = "Resource retrieved successfully.";
4+
const RESOURCE_NOT_FOUND = "Resource not found.";
5+
const RESOURCE_ALREADY_TRACKED = "Resource is already being tracked.";
6+
7+
const RESPONSE_MESSAGES = {
8+
RESOURCE_CREATED_SUCCESSFULLY,
9+
RESOURCE_UPDATED_SUCCESSFULLY,
10+
RESOURCE_RETRIEVED_SUCCESSFULLY,
11+
RESOURCE_NOT_FOUND,
12+
RESOURCE_ALREADY_TRACKED,
13+
};
14+
15+
module.exports = { RESPONSE_MESSAGES };

constants/rateLimiting.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const TOO_MANY_REQUESTS = {
2+
ERROR_TYPE: "Too Many Requests",
3+
STATUS_CODE: 429,
4+
};
5+
6+
module.exports = {
7+
TOO_MANY_REQUESTS,
8+
};

constants/roles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const ROLES = {
44
APPOWNER: "app_owner",
55
MEMBER: "member",
66
ARCHIVED: "archived",
7+
INDISCORD: "in_discord",
78
};
89

910
module.exports = ROLES;

constants/users.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const USER_STATUS = {
1313
const ALLOWED_FILTER_PARAMS = {
1414
ITEM_TAG: ["levelId", "levelName", "levelValue", "tagId"],
1515
USER_STATE: ["state"],
16+
ROLE: ["role"],
1617
};
1718

1819
module.exports = { profileStatus, USER_STATUS, ALLOWED_FILTER_PARAMS };

controllers/monitor.js

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
const { Conflict, NotFound } = require("http-errors");
2+
const { INTERNAL_SERVER_ERROR_MESSAGE } = require("../constants/progresses");
3+
const {
4+
createTrackedProgressDocument,
5+
updateTrackedProgressDocument,
6+
getTrackedProgressDocuments,
7+
} = require("../models/monitor");
8+
const { RESPONSE_MESSAGES } = require("../constants/monitor");
9+
const { RESOURCE_CREATED_SUCCESSFULLY, RESOURCE_UPDATED_SUCCESSFULLY, RESOURCE_RETRIEVED_SUCCESSFULLY } =
10+
RESPONSE_MESSAGES;
11+
/**
12+
* @typedef {Object} TrackedProgressRequestBody
13+
* @property {string} type - The type of tracked progress ("user" or "task").
14+
* @property {string} [userId] - The user ID (required if type is "user").
15+
* @property {string} [taskId] - The task ID (required if type is "task").
16+
* @property {boolean} monitored - Indicates if the progress is currently being tracked.
17+
* @property {number} [frequency=1] - The frequency of tracking.By default 1 if not specified
18+
*/
19+
20+
/**
21+
* @typedef {Object} TrackedProgressResponseData
22+
* @property {string} id - The ID of the tracked progress document.
23+
* @property {string} type - The type of tracked progress ("user" or "task").
24+
* @property {string} userId - The user ID.
25+
* @property {boolean} monitored - Indicates if the progress is currently being tracked.
26+
* @property {number} frequency - The frequency of tracking.
27+
* @property {string} createdAt - The timestamp when the document was created.
28+
* @property {string} updatedAt - The timestamp when the document was last updated.
29+
*/
30+
31+
/**
32+
* @typedef {Object} TrackedProgressResponse
33+
* @property {TrackedProgressResponseData} data - The data of the tracked progress document.
34+
* @property {string} message - The success message.
35+
*/
36+
37+
/**
38+
* Controller function for creating a tracked progress document.
39+
*
40+
* @param {Express.Request} req - The Express request object.
41+
* @param {TrackedProgressRequestBody} req.body - The Request body object.
42+
* @param {Express.Response} res - The Express response object.
43+
* @returns {Promise<void>} - A Promise that resolves when the response has been sent.
44+
*/
45+
46+
const createTrackedProgressController = async (req, res) => {
47+
try {
48+
const data = await createTrackedProgressDocument({ ...req.body });
49+
return res.status(201).json({
50+
message: RESOURCE_CREATED_SUCCESSFULLY,
51+
data,
52+
});
53+
} catch (error) {
54+
if (error instanceof Conflict) {
55+
return res.status(409).json({
56+
message: error.message,
57+
});
58+
} else if (error instanceof NotFound) {
59+
return res.status(404).json({
60+
message: error.message,
61+
});
62+
}
63+
return res.status(500).json({
64+
message: INTERNAL_SERVER_ERROR_MESSAGE,
65+
});
66+
}
67+
};
68+
69+
/**
70+
* @typedef {Object} UpdateTrackedProgressRequestParams
71+
* @property {string} type - The type of tracked progress ("user" or "task").
72+
* @property {string} id - The ID of the tracked progress document.
73+
*/
74+
75+
/**
76+
* @typedef {Object} UpdateTrackedProgressRequestBody
77+
* @property {number} frequency - The frequency of tracking.
78+
* @property {boolean} monitored - Indicates if the progress is currently being tracked.
79+
*/
80+
81+
/**
82+
* @typedef {Object} UpdateTrackedProgressResponseData
83+
* @property {string} id - The ID of the tracked progress document.
84+
* @property {string} createdAt - The timestamp when the document was created.
85+
* @property {string} type - The type of tracked progress ("user" or "task").
86+
* @property {string} userId - The user ID.
87+
* @property {number} frequency - The frequency of tracking.
88+
* @property {boolean} monitored - Indicates if the progress is currently being tracked.
89+
* @property {string} updatedAt - The timestamp when the document was last updated.
90+
*/
91+
92+
/**
93+
* @typedef {Object} UpdateTrackedProgressResponse
94+
* @property {UpdateTrackedProgressResponseData} data - The data of the tracked progress document.
95+
* @property {string} message - The success message.
96+
*/
97+
98+
/**
99+
* Controller function for updating a tracked progress document.
100+
*
101+
* @param {Express.Request} req - The Express request object.
102+
* @param {UpdateTrackedProgressRequestParams} req.params - The request path parameters.
103+
* @param {UpdateTrackedProgressRequestBody} req.body - The request body object.
104+
* @param {Express.Response} res - The Express response object.
105+
* @returns {Promise<void>} - A Promise that resolves when the response has been sent.
106+
*/
107+
108+
const updateTrackedProgressController = async (req, res) => {
109+
try {
110+
const data = await updateTrackedProgressDocument({ ...req });
111+
return res.status(200).json({
112+
data,
113+
message: RESOURCE_UPDATED_SUCCESSFULLY,
114+
});
115+
} catch (error) {
116+
if (error instanceof NotFound) {
117+
return res.status(404).json({
118+
message: error.message,
119+
});
120+
}
121+
return res.status(500).json({
122+
message: INTERNAL_SERVER_ERROR_MESSAGE,
123+
});
124+
}
125+
};
126+
127+
/**
128+
* @typedef {Object} GetTrackedProgressRequestParams
129+
* @property {string} [type] - The type of tracked progress ("user" or "task").
130+
* @property {string} [monitored] - Indicates if the progress is currently being tracked.
131+
* @property {string} [userId] - The ID of the User who is currently being tracked.
132+
* @property {string} [taskId] - The ID of the task which is currently being tracked.
133+
*/
134+
135+
/**
136+
* @typedef {Object} TrackedProgressData
137+
* @property {string} id - The ID of the tracked progress document.
138+
* @property {boolean} monitored - Indicates if the progress is currently being tracked.
139+
* @property {string} createdAt - The timestamp when the document was created.
140+
* @property {string} type - The type of tracked progress ("user" or "task").
141+
* @property {string} [userId] - The user ID.
142+
* @property {string} [taskId] - The task ID.
143+
* @property {number} frequency - The frequency of tracking.
144+
* @property {string} updatedAt - The timestamp when the document was last updated.
145+
*/
146+
147+
/**
148+
* @typedef {Object} GetTrackedProgressResponse
149+
* @property {string} message - The success message.
150+
* @property {TrackedProgressData | TrackedProgressData[]} data - The data of the tracked progress document(s).
151+
*/
152+
153+
/**
154+
* Controller function for retrieving tracked progress documents.
155+
*
156+
* @param {Express.Request} req - The Express request object.
157+
* @param {Express.Response} res - The Express response object.
158+
* @returns {Promise<void>} A Promise that resolves when the response has been sent.
159+
*/
160+
161+
const getTrackedProgressController = async (req, res) => {
162+
try {
163+
const data = await getTrackedProgressDocuments({ ...req.query });
164+
return res.status(200).json({
165+
message: RESOURCE_RETRIEVED_SUCCESSFULLY,
166+
data,
167+
});
168+
} catch (error) {
169+
if (error instanceof NotFound) {
170+
const response = {
171+
message: error.message,
172+
};
173+
if (req.query.type) {
174+
response.data = [];
175+
}
176+
return res.status(404).json(response);
177+
}
178+
return res.status(500).json({
179+
message: INTERNAL_SERVER_ERROR_MESSAGE,
180+
});
181+
}
182+
};
183+
184+
module.exports = {
185+
createTrackedProgressController,
186+
updateTrackedProgressController,
187+
getTrackedProgressController,
188+
};

controllers/users.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const logsQuery = require("../models/logs");
55
const imageService = require("../services/imageService");
66
const { profileDiffStatus } = require("../constants/profileDiff");
77
const { logType } = require("../constants/logs");
8-
const { fetch } = require("../utils/fetch");
8+
99
const logger = require("../utils/logger");
1010
const obfuscate = require("../utils/obfuscate");
1111
const { getPaginationLink, getUsernamesFromPRs } = require("../utils/users");
@@ -24,7 +24,11 @@ const verifyUser = async (req, res) => {
2424
logger.error(`Error while verifying user: ${error}`);
2525
return res.boom.serverUnavailable(SOMETHING_WENT_WRONG);
2626
}
27-
fetch(process.env.IDENTITY_SERVICE_URL, "POST", null, { userId }, { "Content-Type": "application/json" });
27+
fetch(process.env.IDENTITY_SERVICE_URL, {
28+
method: "POST",
29+
body: { userId },
30+
headers: { "Content-Type": "application/json" },
31+
});
2832
return res.json({
2933
message: "Your request has been queued successfully",
3034
});
@@ -65,11 +69,40 @@ const getUserById = async (req, res) => {
6569
* @param res {Object} - Express response object
6670
*/
6771

72+
const removePersonalDetails = (user) => {
73+
const { phone, email, ...safeUser } = user;
74+
return safeUser;
75+
};
76+
6877
const getUsers = async (req, res) => {
6978
try {
7079
const query = req.query?.query ?? "";
7180
const qualifiers = getQualifiers(query);
7281

82+
// getting user details by id if present.
83+
if (req.query.id) {
84+
const id = req.query.id;
85+
let result;
86+
try {
87+
result = await userQuery.fetchUser({ userId: id });
88+
} catch (error) {
89+
logger.error(`Error while fetching user: ${error}`);
90+
return res.boom.serverUnavailable(SOMETHING_WENT_WRONG);
91+
}
92+
93+
if (!result.userExists) {
94+
return res.boom.notFound("User doesn't exist");
95+
}
96+
97+
const User = { ...result.user };
98+
const user = removePersonalDetails(User);
99+
100+
return res.json({
101+
message: "User returned successfully!",
102+
user,
103+
});
104+
}
105+
73106
if (qualifiers?.filterBy) {
74107
const allPRs = await getFilteredPRsOrIssues(qualifiers);
75108

middlewares/rateLimiting.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { RateLimiterMemory } = require("rate-limiter-flexible");
2+
const { TOO_MANY_REQUESTS } = require("../constants/rateLimiting");
3+
const { getRetrySeconds } = require("../utils/rateLimiting");
4+
5+
// INFO: temporarily added here, will be take from env-var/config
6+
const opts = {
7+
keyPrefix: "commonRateLimiter--login_fail_by_ip_per_minute",
8+
points: 5,
9+
duration: 30,
10+
blockDuration: 60 * 10,
11+
};
12+
const globalRateLimiter = new RateLimiterMemory(opts);
13+
14+
/**
15+
* @param req object represents the HTTP request and has property for the request ip address
16+
* @param res object represents the HTTP response that app sends when it get an HTTP request
17+
* @param next indicates the next middelware function
18+
* @returns Promise, which:
19+
* - `resolved` with next middelware function call `next()`
20+
* - `resolved` with response status set to 429 and message `Too Many Requests` */
21+
async function commonRateLimiter(req, res, next) {
22+
// INFO: get the clientIP when running behind a proxy
23+
const ipAddress = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
24+
let retrySeconds = 0;
25+
try {
26+
const responseGlobalRateLimiter = await globalRateLimiter.get(ipAddress);
27+
if (responseGlobalRateLimiter && responseGlobalRateLimiter.consumedPoints > opts.points) {
28+
retrySeconds = getRetrySeconds(responseGlobalRateLimiter.msBeforeNext);
29+
}
30+
if (retrySeconds > 0) {
31+
throw Error();
32+
}
33+
await globalRateLimiter.consume(ipAddress);
34+
return next();
35+
} catch (error) {
36+
// INFO: sending raw seconds in response,``
37+
// for letting API user decide how to represent this number.
38+
retrySeconds = getRetrySeconds(error?.msBeforeNext, retrySeconds);
39+
res.set({
40+
"Retry-After": retrySeconds,
41+
"X-RateLimit-Limit": opts.points,
42+
"X-RateLimit-Remaining": error?.remainingPoints ?? 0,
43+
"X-RateLimit-Reset": new Date(Date.now() + error?.msBeforeNext),
44+
});
45+
const message = `${TOO_MANY_REQUESTS.ERROR_TYPE}: Retry After ${retrySeconds} seconds, requests limit reached`;
46+
return res.status(TOO_MANY_REQUESTS.STATUS_CODE).json({ message });
47+
}
48+
}
49+
50+
module.exports = {
51+
commonRateLimiter,
52+
};

0 commit comments

Comments
 (0)