|
|
@@ -1,4 +1,3 @@
|
|
|
-import { callbackify } from 'node:util'
|
|
|
import ProjectGetter from '../Project/ProjectGetter.js'
|
|
|
import LimitationsManager from '../Subscription/LimitationsManager.js'
|
|
|
import UserGetter from '../User/UserGetter.js'
|
|
|
@@ -34,385 +33,373 @@ const rateLimiter = new RateLimiter('invite-to-project-by-user-id', {
|
|
|
duration: 60 * 30,
|
|
|
})
|
|
|
|
|
|
-const CollaboratorsInviteController = {
|
|
|
- async getAllInvites(req, res) {
|
|
|
- const projectId = req.params.Project_id
|
|
|
- logger.debug({ projectId }, 'getting all active invites for project')
|
|
|
- const invites =
|
|
|
- await CollaboratorsInviteGetter.promises.getAllInvites(projectId)
|
|
|
- res.json({ invites })
|
|
|
- },
|
|
|
-
|
|
|
- async _checkShouldInviteEmail(email) {
|
|
|
- if (Settings.restrictInvitesToExistingAccounts === true) {
|
|
|
- logger.debug({ email }, 'checking if user exists with this email')
|
|
|
- const user = await UserGetter.promises.getUserByAnyEmail(email, {
|
|
|
- _id: 1,
|
|
|
- })
|
|
|
- const userExists = user?._id != null
|
|
|
- return userExists
|
|
|
- } else {
|
|
|
- return true
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- async _checkRateLimit(userId) {
|
|
|
- let collabLimit =
|
|
|
- await LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
|
|
|
- userId
|
|
|
- )
|
|
|
-
|
|
|
- if (collabLimit == null || collabLimit === 0) {
|
|
|
- collabLimit = 1
|
|
|
- } else if (collabLimit < 0 || collabLimit > 20) {
|
|
|
- collabLimit = 20
|
|
|
- }
|
|
|
+async function getAllInvites(req, res) {
|
|
|
+ const projectId = req.params.Project_id
|
|
|
+ logger.debug({ projectId }, 'getting all active invites for project')
|
|
|
+ const invites =
|
|
|
+ await CollaboratorsInviteGetter.promises.getAllInvites(projectId)
|
|
|
+ res.json({ invites })
|
|
|
+}
|
|
|
|
|
|
- // Consume enough points to hit the rate limit at 10 * collabLimit
|
|
|
- const maxRequests = 10 * collabLimit
|
|
|
- const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
|
|
|
- try {
|
|
|
- await rateLimiter.consume(userId, points, { method: 'userId' })
|
|
|
- } catch (err) {
|
|
|
- if (err instanceof Error) {
|
|
|
- throw err
|
|
|
- } else {
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
+async function _checkShouldInviteEmail(email) {
|
|
|
+ if (Settings.restrictInvitesToExistingAccounts === true) {
|
|
|
+ logger.debug({ email }, 'checking if user exists with this email')
|
|
|
+ const user = await UserGetter.promises.getUserByAnyEmail(email, {
|
|
|
+ _id: 1,
|
|
|
+ })
|
|
|
+ const userExists = user?._id != null
|
|
|
+ return userExists
|
|
|
+ } else {
|
|
|
return true
|
|
|
- },
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- async inviteToProject(req, res) {
|
|
|
- const projectId = req.params.Project_id
|
|
|
- let { email, privileges } = req.body
|
|
|
- const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
- const sendingUserId = sendingUser._id
|
|
|
- req.logger.addFields({ email, sendingUserId })
|
|
|
+async function _checkRateLimit(userId) {
|
|
|
+ let collabLimit =
|
|
|
+ await LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
|
|
|
+ userId
|
|
|
+ )
|
|
|
|
|
|
- if (email === sendingUser.email) {
|
|
|
- logger.debug(
|
|
|
- { projectId, email, sendingUserId },
|
|
|
- 'cannot invite yourself to project'
|
|
|
- )
|
|
|
- return res.json({ invite: null, error: 'cannot_invite_self' })
|
|
|
+ if (collabLimit == null || collabLimit === 0) {
|
|
|
+ collabLimit = 1
|
|
|
+ } else if (collabLimit < 0 || collabLimit > 20) {
|
|
|
+ collabLimit = 20
|
|
|
+ }
|
|
|
+
|
|
|
+ // Consume enough points to hit the rate limit at 10 * collabLimit
|
|
|
+ const maxRequests = 10 * collabLimit
|
|
|
+ const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
|
|
|
+ try {
|
|
|
+ await rateLimiter.consume(userId, points, { method: 'userId' })
|
|
|
+ } catch (err) {
|
|
|
+ if (err instanceof Error) {
|
|
|
+ throw err
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
}
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
|
|
|
- logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
|
|
|
+async function inviteToProject(req, res) {
|
|
|
+ const projectId = req.params.Project_id
|
|
|
+ let { email, privileges } = req.body
|
|
|
+ const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
+ const sendingUserId = sendingUser._id
|
|
|
+ req.logger.addFields({ email, sendingUserId })
|
|
|
|
|
|
- const project = await ProjectGetter.promises.getProject(projectId, {
|
|
|
- owner_ref: 1,
|
|
|
- })
|
|
|
- const linkSharingChanges =
|
|
|
- await SplitTestHandler.promises.getAssignmentForUser(
|
|
|
- project.owner_ref,
|
|
|
- 'link-sharing-warning'
|
|
|
- )
|
|
|
+ if (email === sendingUser.email) {
|
|
|
+ logger.debug(
|
|
|
+ { projectId, email, sendingUserId },
|
|
|
+ 'cannot invite yourself to project'
|
|
|
+ )
|
|
|
+ return res.json({ invite: null, error: 'cannot_invite_self' })
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
|
|
|
+
|
|
|
+ const project = await ProjectGetter.promises.getProject(projectId, {
|
|
|
+ owner_ref: 1,
|
|
|
+ })
|
|
|
+ const linkSharingChanges =
|
|
|
+ await SplitTestHandler.promises.getAssignmentForUser(
|
|
|
+ project.owner_ref,
|
|
|
+ 'link-sharing-warning'
|
|
|
+ )
|
|
|
|
|
|
- let allowed = false
|
|
|
- if (linkSharingChanges?.variant === 'active') {
|
|
|
- // if link-sharing-warning is active, can always invite read-only collaborators
|
|
|
- if (privileges === PrivilegeLevels.READ_ONLY) {
|
|
|
- allowed = true
|
|
|
- } else {
|
|
|
- allowed = await LimitationsManager.promises.canAddXEditCollaborators(
|
|
|
- projectId,
|
|
|
- 1
|
|
|
- )
|
|
|
- }
|
|
|
+ let allowed = false
|
|
|
+ if (linkSharingChanges?.variant === 'active') {
|
|
|
+ // if link-sharing-warning is active, can always invite read-only collaborators
|
|
|
+ if (privileges === PrivilegeLevels.READ_ONLY) {
|
|
|
+ allowed = true
|
|
|
} else {
|
|
|
- allowed = await LimitationsManager.promises.canAddXCollaborators(
|
|
|
+ allowed = await LimitationsManager.promises.canAddXEditCollaborators(
|
|
|
projectId,
|
|
|
1
|
|
|
)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ allowed = await LimitationsManager.promises.canAddXCollaborators(
|
|
|
+ projectId,
|
|
|
+ 1
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
- if (!allowed) {
|
|
|
- logger.debug(
|
|
|
- { projectId, email, sendingUserId },
|
|
|
- 'not allowed to invite more users to project'
|
|
|
- )
|
|
|
- return res.json({ invite: null })
|
|
|
+ if (!allowed) {
|
|
|
+ logger.debug(
|
|
|
+ { projectId, email, sendingUserId },
|
|
|
+ 'not allowed to invite more users to project'
|
|
|
+ )
|
|
|
+ return res.json({ invite: null })
|
|
|
+ }
|
|
|
+
|
|
|
+ email = EmailHelper.parseEmail(email, true)
|
|
|
+ if (email == null || email === '') {
|
|
|
+ logger.debug({ projectId, email, sendingUserId }, 'invalid email address')
|
|
|
+ return res.status(400).json({ errorReason: 'invalid_email' })
|
|
|
+ }
|
|
|
+
|
|
|
+ const underRateLimit =
|
|
|
+ await CollaboratorsInviteController._checkRateLimit(sendingUserId)
|
|
|
+ if (!underRateLimit) {
|
|
|
+ return res.sendStatus(429)
|
|
|
+ }
|
|
|
+
|
|
|
+ const shouldAllowInvite =
|
|
|
+ await CollaboratorsInviteController._checkShouldInviteEmail(email)
|
|
|
+ if (!shouldAllowInvite) {
|
|
|
+ logger.debug(
|
|
|
+ { email, projectId, sendingUserId },
|
|
|
+ 'not allowed to send an invite to this email address'
|
|
|
+ )
|
|
|
+ return res.json({
|
|
|
+ invite: null,
|
|
|
+ error: 'cannot_invite_non_user',
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const invite = await CollaboratorsInviteHandler.promises.inviteToProject(
|
|
|
+ projectId,
|
|
|
+ sendingUser,
|
|
|
+ email,
|
|
|
+ privileges
|
|
|
+ )
|
|
|
+
|
|
|
+ ProjectAuditLogHandler.addEntryInBackground(
|
|
|
+ projectId,
|
|
|
+ 'send-invite',
|
|
|
+ sendingUserId,
|
|
|
+ req.ip,
|
|
|
+ {
|
|
|
+ inviteId: invite._id,
|
|
|
+ privileges,
|
|
|
}
|
|
|
+ )
|
|
|
|
|
|
- email = EmailHelper.parseEmail(email, true)
|
|
|
- if (email == null || email === '') {
|
|
|
- logger.debug({ projectId, email, sendingUserId }, 'invalid email address')
|
|
|
- return res.status(400).json({ errorReason: 'invalid_email' })
|
|
|
- }
|
|
|
+ logger.debug({ projectId, email, sendingUserId }, 'invite created')
|
|
|
|
|
|
- const underRateLimit =
|
|
|
- await CollaboratorsInviteController._checkRateLimit(sendingUserId)
|
|
|
- if (!underRateLimit) {
|
|
|
- return res.sendStatus(429)
|
|
|
- }
|
|
|
+ EditorRealTimeController.emitToRoom(projectId, 'project:membership:changed', {
|
|
|
+ invites: true,
|
|
|
+ })
|
|
|
+ res.json({ invite })
|
|
|
+}
|
|
|
+async function revokeInvite(req, res) {
|
|
|
+ const projectId = req.params.Project_id
|
|
|
+ const inviteId = req.params.invite_id
|
|
|
+ const user = SessionManager.getSessionUser(req.session)
|
|
|
|
|
|
- const shouldAllowInvite =
|
|
|
- await CollaboratorsInviteController._checkShouldInviteEmail(email)
|
|
|
- if (!shouldAllowInvite) {
|
|
|
- logger.debug(
|
|
|
- { email, projectId, sendingUserId },
|
|
|
- 'not allowed to send an invite to this email address'
|
|
|
- )
|
|
|
- return res.json({
|
|
|
- invite: null,
|
|
|
- error: 'cannot_invite_non_user',
|
|
|
- })
|
|
|
- }
|
|
|
+ logger.debug({ projectId, inviteId }, 'revoking invite')
|
|
|
|
|
|
- const invite = await CollaboratorsInviteHandler.promises.inviteToProject(
|
|
|
- projectId,
|
|
|
- sendingUser,
|
|
|
- email,
|
|
|
- privileges
|
|
|
- )
|
|
|
+ const invite = await CollaboratorsInviteHandler.promises.revokeInvite(
|
|
|
+ projectId,
|
|
|
+ inviteId
|
|
|
+ )
|
|
|
|
|
|
+ if (invite != null) {
|
|
|
ProjectAuditLogHandler.addEntryInBackground(
|
|
|
projectId,
|
|
|
- 'send-invite',
|
|
|
- sendingUserId,
|
|
|
+ 'revoke-invite',
|
|
|
+ user._id,
|
|
|
req.ip,
|
|
|
{
|
|
|
inviteId: invite._id,
|
|
|
- privileges,
|
|
|
+ privileges: invite.privileges,
|
|
|
}
|
|
|
)
|
|
|
-
|
|
|
- logger.debug({ projectId, email, sendingUserId }, 'invite created')
|
|
|
-
|
|
|
EditorRealTimeController.emitToRoom(
|
|
|
projectId,
|
|
|
'project:membership:changed',
|
|
|
{ invites: true }
|
|
|
)
|
|
|
- res.json({ invite })
|
|
|
- },
|
|
|
- async revokeInvite(req, res) {
|
|
|
- const projectId = req.params.Project_id
|
|
|
- const inviteId = req.params.invite_id
|
|
|
- const user = SessionManager.getSessionUser(req.session)
|
|
|
-
|
|
|
- logger.debug({ projectId, inviteId }, 'revoking invite')
|
|
|
-
|
|
|
- const invite = await CollaboratorsInviteHandler.promises.revokeInvite(
|
|
|
- projectId,
|
|
|
- inviteId
|
|
|
- )
|
|
|
+ }
|
|
|
|
|
|
- if (invite != null) {
|
|
|
- ProjectAuditLogHandler.addEntryInBackground(
|
|
|
- projectId,
|
|
|
- 'revoke-invite',
|
|
|
- user._id,
|
|
|
- req.ip,
|
|
|
- {
|
|
|
- inviteId: invite._id,
|
|
|
- privileges: invite.privileges,
|
|
|
- }
|
|
|
- )
|
|
|
- EditorRealTimeController.emitToRoom(
|
|
|
- projectId,
|
|
|
- 'project:membership:changed',
|
|
|
- { invites: true }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- res.sendStatus(204)
|
|
|
- },
|
|
|
-
|
|
|
- async generateNewInvite(req, res) {
|
|
|
- const projectId = req.params.Project_id
|
|
|
- const inviteId = req.params.invite_id
|
|
|
- const user = SessionManager.getSessionUser(req.session)
|
|
|
-
|
|
|
- logger.debug({ projectId, inviteId }, 'resending invite')
|
|
|
- const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
- const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
|
|
|
- sendingUser._id
|
|
|
- )
|
|
|
- if (!underRateLimit) {
|
|
|
- return res.sendStatus(429)
|
|
|
- }
|
|
|
-
|
|
|
- const invite = await CollaboratorsInviteHandler.promises.generateNewInvite(
|
|
|
- projectId,
|
|
|
- sendingUser,
|
|
|
- inviteId
|
|
|
- )
|
|
|
+ res.sendStatus(204)
|
|
|
+}
|
|
|
|
|
|
- EditorRealTimeController.emitToRoom(
|
|
|
+async function generateNewInvite(req, res) {
|
|
|
+ const projectId = req.params.Project_id
|
|
|
+ const inviteId = req.params.invite_id
|
|
|
+ const user = SessionManager.getSessionUser(req.session)
|
|
|
+
|
|
|
+ logger.debug({ projectId, inviteId }, 'resending invite')
|
|
|
+ const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
+ const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
|
|
|
+ sendingUser._id
|
|
|
+ )
|
|
|
+ if (!underRateLimit) {
|
|
|
+ return res.sendStatus(429)
|
|
|
+ }
|
|
|
+
|
|
|
+ const invite = await CollaboratorsInviteHandler.promises.generateNewInvite(
|
|
|
+ projectId,
|
|
|
+ sendingUser,
|
|
|
+ inviteId
|
|
|
+ )
|
|
|
+
|
|
|
+ EditorRealTimeController.emitToRoom(projectId, 'project:membership:changed', {
|
|
|
+ invites: true,
|
|
|
+ })
|
|
|
+
|
|
|
+ if (invite != null) {
|
|
|
+ ProjectAuditLogHandler.addEntryInBackground(
|
|
|
projectId,
|
|
|
- 'project:membership:changed',
|
|
|
- { invites: true }
|
|
|
- )
|
|
|
-
|
|
|
- if (invite != null) {
|
|
|
- ProjectAuditLogHandler.addEntryInBackground(
|
|
|
- projectId,
|
|
|
- 'resend-invite',
|
|
|
- user._id,
|
|
|
- req.ip,
|
|
|
- {
|
|
|
- inviteId: invite._id,
|
|
|
- privileges: invite.privileges,
|
|
|
- }
|
|
|
- )
|
|
|
-
|
|
|
- res.sendStatus(201)
|
|
|
- } else {
|
|
|
- res.sendStatus(404)
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- async viewInvite(req, res) {
|
|
|
- const projectId = req.params.Project_id
|
|
|
- const { token } = req.params
|
|
|
- const _renderInvalidPage = function () {
|
|
|
- res.status(404)
|
|
|
- logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
|
|
|
- res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
|
|
- }
|
|
|
-
|
|
|
- // check if the user is already a member of the project
|
|
|
- const currentUser = SessionManager.getSessionUser(req.session)
|
|
|
- if (currentUser) {
|
|
|
- const isMember =
|
|
|
- await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
|
|
|
- currentUser._id,
|
|
|
- projectId
|
|
|
- )
|
|
|
- if (isMember) {
|
|
|
- logger.debug(
|
|
|
- { projectId, userId: currentUser._id },
|
|
|
- 'user is already a member of this project, redirecting'
|
|
|
- )
|
|
|
- return res.redirect(`/project/${projectId}`)
|
|
|
+ 'resend-invite',
|
|
|
+ user._id,
|
|
|
+ req.ip,
|
|
|
+ {
|
|
|
+ inviteId: invite._id,
|
|
|
+ privileges: invite.privileges,
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // get the invite
|
|
|
- const invite = await CollaboratorsInviteGetter.promises.getInviteByToken(
|
|
|
- projectId,
|
|
|
- token
|
|
|
)
|
|
|
|
|
|
- // check if invite is gone, or otherwise non-existent
|
|
|
- if (invite == null) {
|
|
|
- logger.debug({ projectId }, 'no invite found for this token')
|
|
|
- return _renderInvalidPage()
|
|
|
- }
|
|
|
+ res.sendStatus(201)
|
|
|
+ } else {
|
|
|
+ res.sendStatus(404)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- // check the user who sent the invite exists
|
|
|
- const owner = await UserGetter.promises.getUser(
|
|
|
- { _id: invite.sendingUserId },
|
|
|
- { email: 1, first_name: 1, last_name: 1 }
|
|
|
- )
|
|
|
- if (owner == null) {
|
|
|
- logger.debug({ projectId }, 'no project owner found')
|
|
|
- return _renderInvalidPage()
|
|
|
+async function viewInvite(req, res) {
|
|
|
+ const projectId = req.params.Project_id
|
|
|
+ const { token } = req.params
|
|
|
+ const _renderInvalidPage = function () {
|
|
|
+ res.status(404)
|
|
|
+ logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
|
|
|
+ res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
|
|
+ }
|
|
|
+
|
|
|
+ // check if the user is already a member of the project
|
|
|
+ const currentUser = SessionManager.getSessionUser(req.session)
|
|
|
+ if (currentUser) {
|
|
|
+ const isMember =
|
|
|
+ await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
|
|
|
+ currentUser._id,
|
|
|
+ projectId
|
|
|
+ )
|
|
|
+ if (isMember) {
|
|
|
+ logger.debug(
|
|
|
+ { projectId, userId: currentUser._id },
|
|
|
+ 'user is already a member of this project, redirecting'
|
|
|
+ )
|
|
|
+ return res.redirect(`/project/${projectId}`)
|
|
|
}
|
|
|
-
|
|
|
- // fetch the project name
|
|
|
- const project = await ProjectGetter.promises.getProject(projectId, {
|
|
|
- name: 1,
|
|
|
- })
|
|
|
- if (project == null) {
|
|
|
- logger.debug({ projectId }, 'no project found')
|
|
|
- return _renderInvalidPage()
|
|
|
+ }
|
|
|
+
|
|
|
+ // get the invite
|
|
|
+ const invite = await CollaboratorsInviteGetter.promises.getInviteByToken(
|
|
|
+ projectId,
|
|
|
+ token
|
|
|
+ )
|
|
|
+
|
|
|
+ // check if invite is gone, or otherwise non-existent
|
|
|
+ if (invite == null) {
|
|
|
+ logger.debug({ projectId }, 'no invite found for this token')
|
|
|
+ return _renderInvalidPage()
|
|
|
+ }
|
|
|
+
|
|
|
+ // check the user who sent the invite exists
|
|
|
+ const owner = await UserGetter.promises.getUser(
|
|
|
+ { _id: invite.sendingUserId },
|
|
|
+ { email: 1, first_name: 1, last_name: 1 }
|
|
|
+ )
|
|
|
+ if (owner == null) {
|
|
|
+ logger.debug({ projectId }, 'no project owner found')
|
|
|
+ return _renderInvalidPage()
|
|
|
+ }
|
|
|
+
|
|
|
+ // fetch the project name
|
|
|
+ const project = await ProjectGetter.promises.getProject(projectId, {
|
|
|
+ name: 1,
|
|
|
+ })
|
|
|
+ if (project == null) {
|
|
|
+ logger.debug({ projectId }, 'no project found')
|
|
|
+ return _renderInvalidPage()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!currentUser) {
|
|
|
+ req.session.sharedProjectData = {
|
|
|
+ project_name: project.name,
|
|
|
+ user_first_name: owner.first_name,
|
|
|
}
|
|
|
+ AuthenticationController.setRedirectInSession(req)
|
|
|
+ return res.redirect('/register')
|
|
|
+ }
|
|
|
+
|
|
|
+ // cleanup if set for register page
|
|
|
+ delete req.session.sharedProjectData
|
|
|
+
|
|
|
+ // finally render the invite
|
|
|
+ res.render('project/invite/show', {
|
|
|
+ invite,
|
|
|
+ token,
|
|
|
+ project,
|
|
|
+ owner,
|
|
|
+ title: 'Project Invite',
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- if (!currentUser) {
|
|
|
- req.session.sharedProjectData = {
|
|
|
- project_name: project.name,
|
|
|
- user_first_name: owner.first_name,
|
|
|
- }
|
|
|
- AuthenticationController.setRedirectInSession(req)
|
|
|
- return res.redirect('/register')
|
|
|
+async function acceptInvite(req, res) {
|
|
|
+ const { Project_id: projectId, token } = req.params
|
|
|
+ const currentUser = SessionManager.getSessionUser(req.session)
|
|
|
+ logger.debug(
|
|
|
+ { projectId, userId: currentUser._id },
|
|
|
+ 'got request to accept invite'
|
|
|
+ )
|
|
|
+
|
|
|
+ const invite = await CollaboratorsInviteGetter.promises.getInviteByToken(
|
|
|
+ projectId,
|
|
|
+ token
|
|
|
+ )
|
|
|
+
|
|
|
+ if (invite == null) {
|
|
|
+ throw new Errors.NotFoundError('no matching invite found')
|
|
|
+ }
|
|
|
+
|
|
|
+ await ProjectAuditLogHandler.promises.addEntry(
|
|
|
+ projectId,
|
|
|
+ 'accept-invite',
|
|
|
+ currentUser._id,
|
|
|
+ req.ip,
|
|
|
+ {
|
|
|
+ inviteId: invite._id,
|
|
|
+ privileges: invite.privileges,
|
|
|
}
|
|
|
-
|
|
|
- // cleanup if set for register page
|
|
|
- delete req.session.sharedProjectData
|
|
|
-
|
|
|
- // finally render the invite
|
|
|
- res.render('project/invite/show', {
|
|
|
- invite,
|
|
|
- token,
|
|
|
- project,
|
|
|
- owner,
|
|
|
- title: 'Project Invite',
|
|
|
- })
|
|
|
- },
|
|
|
-
|
|
|
- async acceptInvite(req, res) {
|
|
|
- const { Project_id: projectId, token } = req.params
|
|
|
- const currentUser = SessionManager.getSessionUser(req.session)
|
|
|
- logger.debug(
|
|
|
- { projectId, userId: currentUser._id },
|
|
|
- 'got request to accept invite'
|
|
|
- )
|
|
|
-
|
|
|
- const invite = await CollaboratorsInviteGetter.promises.getInviteByToken(
|
|
|
+ )
|
|
|
+
|
|
|
+ await CollaboratorsInviteHandler.promises.acceptInvite(
|
|
|
+ invite,
|
|
|
+ projectId,
|
|
|
+ currentUser
|
|
|
+ )
|
|
|
+
|
|
|
+ await EditorRealTimeController.emitToRoom(
|
|
|
+ projectId,
|
|
|
+ 'project:membership:changed',
|
|
|
+ { invites: true, members: true }
|
|
|
+ )
|
|
|
+ AnalyticsManager.recordEventForUserInBackground(
|
|
|
+ currentUser._id,
|
|
|
+ 'project-invite-accept',
|
|
|
+ {
|
|
|
projectId,
|
|
|
- token
|
|
|
- )
|
|
|
-
|
|
|
- if (invite == null) {
|
|
|
- throw new Errors.NotFoundError('no matching invite found')
|
|
|
}
|
|
|
+ )
|
|
|
|
|
|
- await ProjectAuditLogHandler.promises.addEntry(
|
|
|
- projectId,
|
|
|
- 'accept-invite',
|
|
|
- currentUser._id,
|
|
|
- req.ip,
|
|
|
- {
|
|
|
- inviteId: invite._id,
|
|
|
- privileges: invite.privileges,
|
|
|
- }
|
|
|
- )
|
|
|
-
|
|
|
- await CollaboratorsInviteHandler.promises.acceptInvite(
|
|
|
- invite,
|
|
|
- projectId,
|
|
|
- currentUser
|
|
|
- )
|
|
|
-
|
|
|
- await EditorRealTimeController.emitToRoom(
|
|
|
- projectId,
|
|
|
- 'project:membership:changed',
|
|
|
- { invites: true, members: true }
|
|
|
- )
|
|
|
- AnalyticsManager.recordEventForUserInBackground(
|
|
|
- currentUser._id,
|
|
|
- 'project-invite-accept',
|
|
|
- {
|
|
|
- projectId,
|
|
|
- }
|
|
|
- )
|
|
|
-
|
|
|
- if (req.xhr) {
|
|
|
- res.sendStatus(204) // Done async via project page notification
|
|
|
- } else {
|
|
|
- res.redirect(`/project/${projectId}`)
|
|
|
- }
|
|
|
- },
|
|
|
+ if (req.xhr) {
|
|
|
+ res.sendStatus(204) // Done async via project page notification
|
|
|
+ } else {
|
|
|
+ res.redirect(`/project/${projectId}`)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-export default {
|
|
|
- promises: CollaboratorsInviteController,
|
|
|
- getAllInvites: expressify(CollaboratorsInviteController.getAllInvites),
|
|
|
- inviteToProject: expressify(CollaboratorsInviteController.inviteToProject),
|
|
|
- revokeInvite: expressify(CollaboratorsInviteController.revokeInvite),
|
|
|
- revokeInviteForUser: expressify(
|
|
|
- CollaboratorsInviteController.revokeInviteForUser
|
|
|
- ),
|
|
|
- generateNewInvite: expressify(
|
|
|
- CollaboratorsInviteController.generateNewInvite
|
|
|
- ),
|
|
|
- viewInvite: expressify(CollaboratorsInviteController.viewInvite),
|
|
|
- acceptInvite: expressify(CollaboratorsInviteController.acceptInvite),
|
|
|
- _checkShouldInviteEmail: callbackify(
|
|
|
- CollaboratorsInviteController._checkShouldInviteEmail
|
|
|
- ),
|
|
|
- _checkRateLimit: callbackify(CollaboratorsInviteController._checkRateLimit),
|
|
|
+const CollaboratorsInviteController = {
|
|
|
+ getAllInvites: expressify(getAllInvites),
|
|
|
+ inviteToProject: expressify(inviteToProject),
|
|
|
+ revokeInvite: expressify(revokeInvite),
|
|
|
+ generateNewInvite: expressify(generateNewInvite),
|
|
|
+ viewInvite: expressify(viewInvite),
|
|
|
+ acceptInvite: expressify(acceptInvite),
|
|
|
+ _checkShouldInviteEmail,
|
|
|
+ _checkRateLimit,
|
|
|
}
|
|
|
+
|
|
|
+export default CollaboratorsInviteController
|