Skip to content

Commit c3233ae

Browse files
committed
Merge branch 'develop' of https://github.com/Real-Dev-Squad/website-backend into HEAD
2 parents 79bc679 + b6351b7 commit c3233ae

16 files changed

+662
-9
lines changed

controllers/discordactions.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const discordRolesModel = require("../models/discordactions");
66
const discordServices = require("../services/discordService");
77
const { fetchAllUsers, fetchUser } = require("../models/users");
88
const { generateCloudFlareHeaders } = require("../utils/discord-actions");
9+
const { addLog } = require("../models/logs");
910
const discordDeveloperRoleId = config.get("discordDeveloperRoleId");
1011
const discordMavenRoleId = config.get("discordMavenRoleId");
1112

@@ -63,6 +64,60 @@ const createGroupRole = async (req, res) => {
6364
}
6465
};
6566

67+
/**
68+
* Controller function to handle the soft deletion of a group role.
69+
*
70+
* @param {Object} req - The request object
71+
* @param {Object} res - The response object
72+
* @returns {Promise<void>}
73+
*/
74+
const deleteGroupRole = async (req, res) => {
75+
const { groupId } = req.params;
76+
77+
try {
78+
const { roleExists, existingRoles } = await discordRolesModel.isGroupRoleExists({ groupId });
79+
80+
if (!roleExists) {
81+
return res.boom.notFound("Group role not found");
82+
}
83+
84+
const roleData = existingRoles.data();
85+
86+
const discordDeletion = await discordServices.deleteGroupRoleFromDiscord(roleData.roleid);
87+
88+
if (!discordDeletion.success) {
89+
return res.boom.badImplementation(discordDeletion.message);
90+
}
91+
92+
const { isSuccess } = await discordRolesModel.deleteGroupRole(groupId, req.userData.id);
93+
94+
if (!isSuccess) {
95+
logger.error(`Role deleted from Discord but failed to delete from database for groupId: ${groupId}`);
96+
return res.boom.badImplementation("Group role deletion failed");
97+
}
98+
99+
const groupDeletionLog = {
100+
type: "group-role-deletion",
101+
meta: {
102+
userId: req.userData.id,
103+
},
104+
body: {
105+
groupId: groupId,
106+
roleName: roleData.rolename,
107+
discordRoleId: roleData.roleid,
108+
action: "delete",
109+
},
110+
};
111+
await addLog(groupDeletionLog.type, groupDeletionLog.meta, groupDeletionLog.body);
112+
return res.status(200).json({
113+
message: "Group role deleted successfully",
114+
});
115+
} catch (error) {
116+
logger.error(`Error while deleting group role: ${error}`);
117+
return res.boom.badImplementation("Internal server error");
118+
}
119+
};
120+
66121
/**
67122
* Gets all group-roles
68123
*
@@ -491,4 +546,5 @@ module.exports = {
491546
setRoleToUsersWith31DaysPlusOnboarding,
492547
getUserDiscordInvite,
493548
generateInviteForUser,
549+
deleteGroupRole,
494550
};

controllers/extensionRequests.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,22 @@ const getSelfExtensionRequests = async (req, res) => {
202202
* @param res {Object} - Express response object
203203
*/
204204
const updateExtensionRequest = async (req, res) => {
205+
const { dev } = req.query;
206+
const isDev = dev === "true";
205207
try {
206208
const extensionRequest = await extensionRequestsQuery.fetchExtensionRequest(req.params.id);
207209
if (!extensionRequest.extensionRequestData) {
208210
return res.boom.notFound("Extension Request not found");
209211
}
210212

213+
if (
214+
isDev &&
215+
!req.userData?.roles.super_user &&
216+
extensionRequest.extensionRequestData.status !== EXTENSION_REQUEST_STATUS.PENDING
217+
) {
218+
return res.boom.badRequest("Only pending extension request can be updated");
219+
}
220+
211221
if (req.body.assignee) {
212222
const { taskData: task } = await tasks.fetchTask(extensionRequest.extensionRequestData.taskId);
213223
if (task.assignee !== (await getUsername(req.body.assignee))) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const skipAuthorizeRolesUnderFF = (authorizeMiddleware) => {
2+
return (req, res, next) => {
3+
const { dev } = req.query;
4+
const isDev = dev === "true";
5+
if (isDev) {
6+
next();
7+
} else {
8+
authorizeMiddleware(req, res, next);
9+
}
10+
};
11+
};
12+
13+
module.exports = skipAuthorizeRolesUnderFF;

models/discordactions.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,31 @@ const createNewRole = async (roleData) => {
4646
}
4747
};
4848

49+
/**
50+
* Soft deletes a group role by marking it as deleted in the database.
51+
* This function updates the role document in Firestore, setting isDeleted to true
52+
* and recording who deleted it and when.
53+
*
54+
* @param {string} groupId - The ID of the group role to be deleted
55+
* @param {string} deletedBy - The ID of the user performing the deletion for logging purpose
56+
* @returns {Promise<Object>} An object indicating whether the operation was successful
57+
*/
58+
const deleteGroupRole = async (groupId, deletedBy) => {
59+
try {
60+
const roleRef = admin.firestore().collection("discord-roles").doc(groupId);
61+
await roleRef.update({
62+
isDeleted: true,
63+
deletedAt: admin.firestore.Timestamp.fromDate(new Date()),
64+
deletedBy: deletedBy,
65+
});
66+
67+
return { isSuccess: true };
68+
} catch (error) {
69+
logger.error(`Error in deleteGroupRole: ${error}`);
70+
return { isSuccess: false };
71+
}
72+
};
73+
4974
const removeMemberGroup = async (roleId, discordId) => {
5075
try {
5176
const backendResponse = await deleteRoleFromDatabase(roleId, discordId);
@@ -139,10 +164,13 @@ const updateGroupRole = async (roleData, docId) => {
139164

140165
const isGroupRoleExists = async (options = {}) => {
141166
try {
142-
const { rolename = null, roleid = null } = options;
167+
const { groupId = null, rolename = null, roleid = null } = options;
143168

144169
let existingRoles;
145-
if (rolename && roleid) {
170+
if (groupId) {
171+
existingRoles = await discordRoleModel.doc(groupId).get();
172+
return { roleExists: existingRoles.exists, existingRoles };
173+
} else if (rolename && roleid) {
146174
existingRoles = await discordRoleModel
147175
.where("rolename", "==", rolename)
148176
.where("roleid", "==", roleid)
@@ -153,9 +181,8 @@ const isGroupRoleExists = async (options = {}) => {
153181
} else if (roleid) {
154182
existingRoles = await discordRoleModel.where("roleid", "==", roleid).limit(1).get();
155183
} else {
156-
throw Error("Either rolename or roleId is required");
184+
throw Error("Either rolename, roleId, or groupId is required");
157185
}
158-
159186
return { roleExists: !existingRoles.empty, existingRoles };
160187
} catch (err) {
161188
logger.error("Error in getting all group-roles", err);
@@ -1075,4 +1102,5 @@ module.exports = {
10751102
getUserDiscordInvite,
10761103
addInviteToInviteModel,
10771104
groupUpdateLastJoinDate,
1105+
deleteGroupRole,
10781106
};

models/userStatus.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const { userState } = require("../constants/userStatus");
2323
const discordRoleModel = firestore.collection("discord-roles");
2424
const memberRoleModel = firestore.collection("member-group-roles");
2525
const usersCollection = firestore.collection("users");
26+
const config = require("config");
2627
const DISCORD_BASE_URL = config.get("services.discordBot.baseUrl");
2728
const { generateAuthTokenForCloudflare } = require("../utils/discord-actions");
2829

routes/discordactions.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
updateUsersNicknameStatus,
1616
syncDiscordGroupRolesInFirestore,
1717
setRoleToUsersWith31DaysPlusOnboarding,
18+
deleteGroupRole,
1819
} = require("../controllers/discordactions");
1920
const {
2021
validateGroupRoleBody,
@@ -29,11 +30,19 @@ const ROLES = require("../constants/roles");
2930
const { Services } = require("../constants/bot");
3031
const { verifyCronJob } = require("../middlewares/authorizeBot");
3132
const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndService");
32-
33+
const { devFlagMiddleware } = require("../middlewares/devFlag");
3334
const router = express.Router();
3435

3536
router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
3637
router.get("/groups", authenticate, checkIsVerifiedDiscord, getAllGroupRoles);
38+
router.delete(
39+
"/groups/:groupId",
40+
authenticate,
41+
checkIsVerifiedDiscord,
42+
authorizeRoles([SUPERUSER]),
43+
devFlagMiddleware,
44+
deleteGroupRole
45+
);
3746
router.post("/roles", authenticate, checkIsVerifiedDiscord, validateMemberRoleBody, addGroupRoleToMember);
3847
router.get("/invite", authenticate, getUserDiscordInvite);
3948
router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);

routes/extensionRequests.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ const {
1010
updateExtensionRequestStatus,
1111
getExtensionRequestsValidator,
1212
} = require("../middlewares/validators/extensionRequests");
13+
const skipAuthorizeRolesUnderFF = require("../middlewares/skipAuthorizeRolesWrapper");
1314

1415
router.post("/", authenticate, createExtensionRequest, extensionRequests.createTaskExtensionRequest);
1516
router.get("/", authenticate, getExtensionRequestsValidator, extensionRequests.fetchExtensionRequests);
1617
router.get("/self", authenticate, extensionRequests.getSelfExtensionRequests);
1718
router.get("/:id", authenticate, authorizeRoles([SUPERUSER, APPOWNER]), extensionRequests.getExtensionRequest);
19+
// remove the skipAuthorizeRolesUnderFF & authorizeRoles middleware when removing the feature flag
1820
router.patch(
1921
"/:id",
2022
authenticate,
21-
authorizeRoles([SUPERUSER, APPOWNER]),
23+
skipAuthorizeRolesUnderFF(authorizeRoles([SUPERUSER, APPOWNER])),
2224
updateExtensionRequest,
2325
extensionRequests.updateExtensionRequest
2426
);

services/discordMembersService.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const jwt = require("jsonwebtoken");
2-
2+
const config = require("config");
33
const DISCORD_BASE_URL = config.get("services.discordBot.baseUrl");
44

55
/**

services/discordService.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,40 @@ const setUserDiscordNickname = async (userName, discordId) => {
103103
};
104104
} catch (err) {
105105
logger.error("Error in updating discord Nickname", err);
106-
throw err;
106+
throw new Error(err);
107+
}
108+
};
109+
110+
/**
111+
* Deletes a group role from the Discord server.
112+
* This function sends a DELETE request to the Discord API to remove the role.
113+
* It's part of the soft delete process, where we remove the role from Discord
114+
* but keep a record of it in our database.
115+
*
116+
* @param {string} roleId - The Discord ID of the role to be deleted
117+
* @returns {Promise<Object>} The response from the Discord API
118+
* @throws {Error} If the deletion fails or there's a network error
119+
*/
120+
121+
const deleteGroupRoleFromDiscord = async (roleId) => {
122+
try {
123+
const authToken = generateAuthTokenForCloudflare();
124+
const response = await fetch(`${DISCORD_BASE_URL}/roles/${roleId}?dev=true`, {
125+
method: "DELETE",
126+
headers: {
127+
"Content-Type": "application/json",
128+
Authorization: `Bearer ${authToken}`,
129+
},
130+
});
131+
132+
if (response.status === 204) {
133+
return { success: true, message: "Role deleted successfully" };
134+
}
135+
136+
return { success: false, message: "Failed to delete role from discord" };
137+
} catch (err) {
138+
logger.error("Error deleting role from Discord", err);
139+
return { success: false, message: "Internal server error" };
107140
}
108141
};
109142

@@ -114,4 +147,5 @@ module.exports = {
114147
addRoleToUser,
115148
removeRoleFromUser,
116149
setUserDiscordNickname,
150+
deleteGroupRoleFromDiscord,
117151
};

0 commit comments

Comments
 (0)