Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b8816fc
added enpoint for generating and getting discord invite for user
vinayak-trivedi Sep 24, 2023
799efb7
changes in middleware and controller method
vinayak-trivedi Sep 26, 2023
2abbced
changed discordNewComersChannelId constant
vinayak-trivedi Oct 3, 2023
5910297
tests for endpoint for getting discord link generated for user
vinayak-trivedi Oct 8, 2023
36dac7c
tests for post API
vinayak-trivedi Oct 8, 2023
43bb566
Merge branch 'develop' of https://github.com/Real-Dev-Squad/website-b…
vinayak-trivedi Oct 22, 2023
f5655a1
fix for failing tests
vinayak-trivedi Oct 22, 2023
30f7781
fix for failing unit test
vinayak-trivedi Oct 28, 2023
c948307
types issue fixed
vinayak-trivedi Oct 28, 2023
998f559
model tests
vinayak-trivedi Oct 29, 2023
1ea7df4
did suggested changes
vinayak-trivedi Oct 29, 2023
71446ad
invite get model test changes
vinayak-trivedi Oct 29, 2023
1105698
removed the model test getInvite
vinayak-trivedi Nov 1, 2023
99252d1
test description string change
vinayak-trivedi Nov 1, 2023
2bfd652
Update test/integration/discordactions.test.js
vinayak-trivedi Nov 2, 2023
7dde33f
sync dev to discord-invite-api
vinayak-trivedi Nov 9, 2023
c8e52fb
Merge branch 'discord-invite-api' of https://github.com/Real-Dev-Squa…
vinayak-trivedi Nov 9, 2023
09a9eca
fix for failing tests
vinayak-trivedi Nov 9, 2023
1ae3bef
fix for failing unit test
vinayak-trivedi Nov 9, 2023
9f2870c
made suggested changes
vinayak-trivedi Nov 18, 2023
4431f07
updated one model test and added one model test for getInvite model
vinayak-trivedi Nov 24, 2023
5cae6df
Merge branch 'develop' into discord-invite-api
vinayak-trivedi Nov 29, 2023
640a3b2
Merge branch 'develop' into discord-invite-api
prakashchoudhary07 Nov 29, 2023
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/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
port: port,
enableFileLogs: false,
enableConsoleLogs: true,
discordNewComersChannelId: "709080951824842783",

services: {
rdsApi: {
Expand Down
1 change: 1 addition & 0 deletions config/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
enableConsoleLogs: true,
discordUnverifiedRoleId: "1234567890",
discordDeveloperRoleId: "9876543210",
discordNewComersChannelId: "709080951824842783",
discordMavenRoleId: "1212121212",
githubOauth: {
clientId: "clientId",
Expand Down
71 changes: 71 additions & 0 deletions controllers/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,75 @@ const setRoleToUsersWith31DaysPlusOnboarding = async (req, res) => {
}
};

const generateInviteForUser = async (req, res) => {
try {
const { userId } = req.query;
const userIdForInvite = userId || req.userData.id;

const modelResponse = await discordRolesModel.getUserDiscordInvite(userIdForInvite);

if (!modelResponse.notFound) {
return res.status(409).json({
message: "User invite is already present!",
});
}

const channelId = config.get("discordNewComersChannelId");
const authToken = jwt.sign({}, config.get("rdsServerlessBot.rdsServerLessPrivateKey"), {
algorithm: "RS256",
expiresIn: config.get("rdsServerlessBot.ttl"),
});

const inviteOptions = {
channelId: channelId,
};
const response = await fetch(`${DISCORD_BASE_URL}/invite`, {
method: "POST",
body: JSON.stringify(inviteOptions),
headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
});
const discordInviteResponse = await response.json();

const inviteCode = discordInviteResponse.data.code;
const inviteLink = `discord.gg/${inviteCode}`;

await discordRolesModel.addInviteToInviteModel({ userId: userIdForInvite, inviteLink });

return res.status(201).json({
message: "invite generated successfully",
inviteLink,
});
} catch (err) {
logger.error(`Error in generating invite for user: ${err}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

const getUserDiscordInvite = async (req, res) => {
try {
const { userId } = req.query;
const isSuperUser = req.userData.roles.super_user;

if (userId && !isSuperUser) return res.boom.forbidden("User should be super user to get link for other users");

const userIdForInvite = userId || req.userData.id;

const invite = await discordRolesModel.getUserDiscordInvite(userIdForInvite);

if (invite.notFound) {
return res.boom.notFound("User invite doesn't exist");
}

return res.json({
message: "Invite returned successfully",
inviteLink: invite?.inviteLink,
});
} catch (err) {
logger.error(`Error in fetching user invite: ${err}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

module.exports = {
getGroupsRoleId,
createGroupRole,
Expand All @@ -396,4 +465,6 @@ module.exports = {
updateUsersNicknameStatus,
syncDiscordGroupRolesInFirestore,
setRoleToUsersWith31DaysPlusOnboarding,
getUserDiscordInvite,
generateInviteForUser,
};
28 changes: 28 additions & 0 deletions middlewares/checkCanGenerateDiscordLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextFunction } from "express";
import { CustomRequest, CustomResponse } from "../types/global";

const checkCanGenerateDiscordLink = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
const { discordId, roles, id: userId, profileStatus } = req.userData;
const isSuperUser = roles.super_user;
const userIdInQuery = req.query.userId;

if (userIdInQuery && userIdInQuery !== userId && !isSuperUser) {
return res.boom.forbidden("User should be super user to generate link for other users");
}

if (discordId) {
return res.boom.forbidden("Only users who have never joined discord can generate invite link");
}

if (roles.archived) {
return res.boom.forbidden("Archived users cannot generate invite");
}

if (!roles.maven && !roles.designer && !roles.product_manager && profileStatus !== "VERIFIED") {
return res.boom.forbidden("Only selected roles can generate discord link directly");
}

return next();
};

module.exports = checkCanGenerateDiscordLink;
28 changes: 28 additions & 0 deletions models/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { generateDiscordProfileImageUrl } = require("../utils/discord-actions");
const firestore = require("../utils/firestore");
const discordRoleModel = firestore.collection("discord-roles");
const memberRoleModel = firestore.collection("member-group-roles");
const discordInvitesModel = firestore.collection("discord-invites");
const admin = require("firebase-admin");
const { findSubscribedGroupIds } = require("../utils/helper");
const { retrieveUsers } = require("../services/dataAccessLayer");
Expand Down Expand Up @@ -817,6 +818,31 @@ const updateUsersWith31DaysPlusOnboarding = async () => {
}
};

const addInviteToInviteModel = async (inviteObject) => {
try {
const invite = await discordInvitesModel.add(inviteObject);
return invite.id;
} catch (err) {
logger.error("Error in adding invite", err);
throw err;
}
};

const getUserDiscordInvite = async (userId) => {
try {
const invite = await discordInvitesModel.where("userId", "==", userId).get();
const [inviteDoc] = invite.docs;
if (inviteDoc) {
return { id: inviteDoc.id, ...inviteDoc.data(), notFound: false };
} else {
return { notFound: true };
}
} catch (err) {
logger.log("error in getting user invite", err);
throw err;
}
};

module.exports = {
createNewRole,
removeMemberGroup,
Expand All @@ -834,4 +860,6 @@ module.exports = {
updateUsersNicknameStatus,
updateIdle7dUsersOnDiscord,
updateUsersWith31DaysPlusOnboarding,
getUserDiscordInvite,
addInviteToInviteModel,
};
5 changes: 5 additions & 0 deletions routes/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const {
deleteRole,
updateDiscordImageForVerification,
setRoleIdleToIdleUsers,
getUserDiscordInvite,
generateInviteForUser,
setRoleIdle7DToIdleUsers,
updateDiscordNicknames,
updateUsersNicknameStatus,
Expand All @@ -20,6 +22,7 @@ const {
validateUpdateUsersNicknameStatusBody,
} = require("../middlewares/validators/discordactions");
const checkIsVerifiedDiscord = require("../middlewares/verifydiscord");
const checkCanGenerateDiscordLink = require("../middlewares/checkCanGenerateDiscordLink");
const { SUPERUSER } = require("../constants/roles");
const authorizeRoles = require("../middlewares/authorizeRoles");
const { verifyCronJob } = require("../middlewares/authorizeBot");
Expand All @@ -29,6 +32,8 @@ const router = express.Router();
router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
router.get("/groups", authenticate, checkIsVerifiedDiscord, getAllGroupRoles);
router.post("/roles", authenticate, checkIsVerifiedDiscord, validateMemberRoleBody, addGroupRoleToMember);
router.get("/invite", authenticate, getUserDiscordInvite);

Check failure

Code scanning / CodeQL

Missing rate limiting

This route handler performs [authorization](1), but is not rate-limited. This route handler performs [authorization](2), but is not rate-limited. This route handler performs [authorization](3), but is not rate-limited.
router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);

Check failure

Code scanning / CodeQL

Missing rate limiting

This route handler performs [authorization](1), but is not rate-limited. This route handler performs [authorization](2), but is not rate-limited. This route handler performs [authorization](3), but is not rate-limited.
router.delete("/roles", authenticate, checkIsVerifiedDiscord, deleteRole);
router.get("/roles", authenticate, checkIsVerifiedDiscord, getGroupsRoleId);
router.patch(
Expand Down
29 changes: 29 additions & 0 deletions test/fixtures/user/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ module.exports = () => {
archived: false,
member: true,
in_discord: true,
designer: true,
},
twitter_id: "RitvikJamwal4u",
linkedin_id: "ritvik-jamwal4u",
Expand All @@ -239,6 +240,7 @@ module.exports = () => {
member: true,
archived: false,
in_discord: true,
product_manager: true,
},
picture: {
publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar",
Expand All @@ -261,6 +263,7 @@ module.exports = () => {
},
roles: {
member: true,
maven: true,
},
picture: {
publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar",
Expand Down Expand Up @@ -411,5 +414,31 @@ module.exports = () => {
updated_at: Date.now(),
created_at: Date.now(),
},
{
username: "Vinayak",
first_name: "Vinayak",
last_name: "Trivedi",
yoe: 2,
img: "./img.png",
linkedin_id: "_",
github_id: "xfasrfsd",
github_display_name: "vinayak-trivedi",
discordJoinedAt: "2023-04-06T01:47:34.488000+00:00",
phone: "1234567890",
email: "abc@gmail.com",
status: "active",
tokens: {
githubAccessToken: "githubAccessToken",
},
roles: {
restricted: false,
app_owner: true,
archived: true,
},
picture: {
publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1667685133/profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar.jpg",
},
},
];
};
Loading