3 var Promise = require('bluebird');
4 var TroupeInvite = require('gitter-web-persistence').TroupeInvite;
5 var uuid = require('uuid/v4');
6 var assert = require('assert');
7 var StatusError = require('statuserror');
8 var mongoUtils = require('gitter-web-persistence-utils/lib/mongo-utils');
9 const userService = require('gitter-web-users');
10 var GitHubUserEmailAddressService = require('gitter-web-github').GitHubUserEmailAddressService;
11 var persistence = require('gitter-web-persistence');
12 var identityService = require('gitter-web-identity');
13 const mongoReadPrefs = require('gitter-web-persistence-utils/lib/mongo-read-prefs');
15 var MS_PER_DAY = 24 * 60 * 60 * 1000;
17 var GITTER_IDENTITY_TYPE = 'gitter';
18 var GITHUB_IDENTITY_PROVIDER = identityService.GITHUB_IDENTITY_PROVIDER;
20 function findExistingGitterUser(username) {
21 return persistence.User.findOne({ username: username }).exec();
24 function findExistingIdentityUsername(provider, username) {
25 return identityService.findUserIdForProviderUsername(provider, username).then(function(userId) {
27 return persistence.User.findById(userId).exec();
31 function findExistingUser(type, externalId) {
33 case GITTER_IDENTITY_TYPE:
34 return findExistingGitterUser(externalId);
36 case GITHUB_IDENTITY_PROVIDER:
37 // TODO: Note that we will need to do a lookup once
38 // splitville is complete and gitter usernames <> github usernames
39 return findExistingGitterUser(externalId);
45 return findExistingIdentityUsername(type, externalId);
48 function resolveGitHubUserEmail(invitingUser, githubUsername) {
49 var githubUserEmailAddressService = new GitHubUserEmailAddressService(invitingUser);
50 return githubUserEmailAddressService.findEmailAddressForGitHubUser(githubUsername);
53 function resolveEmailAddress(invitingUser, type, externalId) {
54 // For now, we only try resolve email addresses for GitHub users
55 if (type === GITHUB_IDENTITY_PROVIDER) {
56 return resolveGitHubUserEmail(invitingUser, externalId);
65 function createInvite(roomId, options) {
66 var type = options.type;
67 var externalId = options.externalId;
68 var invitedByUserId = options.invitedByUserId;
69 var emailAddress = options.emailAddress;
71 if (type === 'email') {
72 // Email address is mandatory
73 if (!emailAddress) throw new StatusError(400);
74 externalId = emailAddress;
76 if (!externalId) throw new StatusError(400);
77 // Email address is optional
80 externalId = externalId.toLowerCase();
82 const newInvite = new TroupeInvite({
85 externalId: externalId,
86 emailAddress: emailAddress,
89 invitedByUserId: invitedByUserId,
94 .findById(invitedByUserId)
95 .then(invitedByUser => {
97 throw new StatusError(
99 `Invited by user does not exist invitedByUserId=${invitedByUserId}`
103 // Hellbanned users are not allowed to send out invites
104 // Just fake it for them and don't save it to the database
105 if (invitedByUser.hellbanned) {
109 return newInvite.save();
111 .catch(mongoUtils.mongoErrorWithCode(11000), function() {
112 throw new StatusError(409); // Conflict
119 function accept(userId, secret) {
121 return TroupeInvite.findOne({ secret: String(secret) })
124 .then(function(invite) {
125 if (!invite) throw new StatusError(404);
127 // Is this user re-using the invite?
129 if (!mongoUtils.objectIDsEqual(invite.userId, userId)) {
130 throw new StatusError(404);
141 function markInviteAccepted(inviteId, userId) {
142 return TroupeInvite.update(
145 state: { $ne: 'ACCEPTED' }
159 function markInviteRejected(inviteId, userId) {
160 return TroupeInvite.update(
163 state: { $ne: 'REJECTED' }
177 function markInviteReminded(inviteId) {
178 return TroupeInvite.update(
184 reminderSent: new Date()
190 function findInvitesForReminder(timeHorizonDays) {
191 var cutoffId = mongoUtils.createIdForTimestamp(Date.now() - timeHorizonDays * MS_PER_DAY);
193 return TroupeInvite.aggregate([
197 _id: { $lt: cutoffId },
210 localField: 'invite.troupeId',
218 preserveNullAndEmptyArrays: true
224 localField: 'invite.invitedByUserId',
231 path: '$invitedByUser',
232 preserveNullAndEmptyArrays: true
239 * For exporting things
241 function getInvitesCursorByUserId(userId) {
242 const cursor = TroupeInvite.find({
246 .read(mongoReadPrefs.secondaryPreferred)
254 * For exporting things
256 function getSentInvitesCursorByUserId(userId) {
257 const cursor = TroupeInvite.find({
258 invitedByUserId: userId
261 .read(mongoReadPrefs.secondaryPreferred)
269 findExistingUser: findExistingUser,
270 resolveEmailAddress: resolveEmailAddress,
271 createInvite: Promise.method(createInvite),
272 accept: Promise.method(accept),
273 markInviteAccepted: Promise.method(markInviteAccepted),
274 markInviteRejected: Promise.method(markInviteRejected),
275 findInvitesForReminder: findInvitesForReminder,
276 markInviteReminded: markInviteReminded,
277 getInvitesCursorByUserId,
278 getSentInvitesCursorByUserId