mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Added email invite limit (#8664)
* Added email invite limit * change error message for sending too many invitations to instruct them to email us * fix test error message to use variable in locales string * add comment to warn about keeping INVITES_LIMIT low If INVITES_LIMIT is allowed to be greater than MAX_EMAIL_INVITES_BY_USER then the inviter can send more than MAX_EMAIL_INVITES_BY_USER invitations at once.
This commit is contained in:
committed by
Sabe Jones
parent
0442b87608
commit
7d42e8fc71
@@ -4,9 +4,11 @@ import {
|
|||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../helpers/api-v3-integration.helper';
|
} from '../../../../helpers/api-v3-integration.helper';
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
|
||||||
const INVITES_LIMIT = 100;
|
const INVITES_LIMIT = 100;
|
||||||
const PARTY_LIMIT_MEMBERS = 30;
|
const PARTY_LIMIT_MEMBERS = 30;
|
||||||
|
const MAX_EMAIL_INVITES_BY_USER = 200;
|
||||||
|
|
||||||
describe('Post /groups/:groupId/invite', () => {
|
describe('Post /groups/:groupId/invite', () => {
|
||||||
let inviter;
|
let inviter;
|
||||||
@@ -205,13 +207,37 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error when a user has sent the max number of email invites', async () => {
|
||||||
|
let inviterWithMax = await generateUser({
|
||||||
|
invitesSent: MAX_EMAIL_INVITES_BY_USER,
|
||||||
|
balance: 4,
|
||||||
|
});
|
||||||
|
let tmpGroup = await inviterWithMax.post('/groups', {
|
||||||
|
name: groupName,
|
||||||
|
type: 'guild',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(inviterWithMax.post(`/groups/${tmpGroup._id}/invite`, {
|
||||||
|
emails: [testInvite],
|
||||||
|
inviter: 'inviter name',
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('invites a user to a group by email', async () => {
|
it('invites a user to a group by email', async () => {
|
||||||
let res = await inviter.post(`/groups/${group._id}/invite`, {
|
let res = await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
emails: [testInvite],
|
emails: [testInvite],
|
||||||
inviter: 'inviter name',
|
inviter: 'inviter name',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let updatedUser = await inviter.sync();
|
||||||
|
|
||||||
expect(res).to.exist;
|
expect(res).to.exist;
|
||||||
|
expect(updatedUser.invitesSent).to.eql(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('invites multiple users to a group by email', async () => {
|
it('invites multiple users to a group by email', async () => {
|
||||||
@@ -219,7 +245,10 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
emails: [testInvite, {name: 'test2', email: 'test2@habitica.com'}],
|
emails: [testInvite, {name: 'test2', email: 'test2@habitica.com'}],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let updatedUser = await inviter.sync();
|
||||||
|
|
||||||
expect(res).to.exist;
|
expect(res).to.exist;
|
||||||
|
expect(updatedUser.invitesSent).to.eql(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,7 @@
|
|||||||
"invitationsSent": "Invitations sent!",
|
"invitationsSent": "Invitations sent!",
|
||||||
"invitationSent": "Invitation sent!",
|
"invitationSent": "Invitation sent!",
|
||||||
"inviteAlertInfo2": "Or share this link (copy/paste):",
|
"inviteAlertInfo2": "Or share this link (copy/paste):",
|
||||||
|
"inviteLimitReached": "You have already sent the maximum number of email invitations. We have a limit to prevent spamming, however if you would like more, please contact us at <%= techAssistanceEmail %> and we'll be happy to discuss it!",
|
||||||
"sendGiftHeading": "Send Gift to <%= name %>",
|
"sendGiftHeading": "Send Gift to <%= name %>",
|
||||||
"sendGiftGemsBalance": "From <%= number %> Gems",
|
"sendGiftGemsBalance": "From <%= number %> Gems",
|
||||||
"sendGiftCost": "Total: $<%= cost %> USD",
|
"sendGiftCost": "Total: $<%= cost %> USD",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import Bluebird from 'bluebird';
|
import Bluebird from 'bluebird';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import nconf from 'nconf';
|
||||||
import {
|
import {
|
||||||
model as Group,
|
model as Group,
|
||||||
basicFields as basicGroupFields,
|
basicFields as basicGroupFields,
|
||||||
@@ -27,6 +28,9 @@ import amzLib from '../../libs/amazonPayments';
|
|||||||
import shared from '../../../common';
|
import shared from '../../../common';
|
||||||
import apiMessages from '../../libs/apiMessages';
|
import apiMessages from '../../libs/apiMessages';
|
||||||
|
|
||||||
|
const MAX_EMAIL_INVITES_BY_USER = 200;
|
||||||
|
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @apiDefine GroupBodyInvalid
|
* @apiDefine GroupBodyInvalid
|
||||||
* @apiError (400) {BadRequest} GroupBodyInvalid A parameter in the group body was invalid.
|
* @apiError (400) {BadRequest} GroupBodyInvalid A parameter in the group body was invalid.
|
||||||
@@ -1056,6 +1060,8 @@ api.inviteToGroup = {
|
|||||||
|
|
||||||
req.checkParams('groupId', res.t('groupIdRequired')).notEmpty();
|
req.checkParams('groupId', res.t('groupIdRequired')).notEmpty();
|
||||||
|
|
||||||
|
if (user.invitesSent >= MAX_EMAIL_INVITES_BY_USER) throw new NotAuthorized(res.t('inviteLimitReached', { techAssistanceEmail: TECH_ASSISTANCE_EMAIL }));
|
||||||
|
|
||||||
let validationErrors = req.validationErrors();
|
let validationErrors = req.validationErrors();
|
||||||
if (validationErrors) throw validationErrors;
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
@@ -1079,6 +1085,8 @@ api.inviteToGroup = {
|
|||||||
|
|
||||||
if (emails) {
|
if (emails) {
|
||||||
let emailInvites = emails.map((invite) => _inviteByEmail(invite, group, user, req, res));
|
let emailInvites = emails.map((invite) => _inviteByEmail(invite, group, user, req, res));
|
||||||
|
user.invitesSent += emails.length;
|
||||||
|
await user.save();
|
||||||
let emailResults = await Bluebird.all(emailInvites);
|
let emailResults = await Bluebird.all(emailInvites);
|
||||||
results.push(...emailResults);
|
results.push(...emailResults);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import stripePayments from '../libs/stripePayments';
|
|||||||
const questScrolls = shared.content.quests;
|
const questScrolls = shared.content.quests;
|
||||||
const Schema = mongoose.Schema;
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
export const INVITES_LIMIT = 100;
|
export const INVITES_LIMIT = 100; // must not be greater than MAX_EMAIL_INVITES_BY_USER
|
||||||
export const TAVERN_ID = shared.TAVERN_ID;
|
export const TAVERN_ID = shared.TAVERN_ID;
|
||||||
|
|
||||||
const NO_CHAT_NOTIFICATIONS = [TAVERN_ID];
|
const NO_CHAT_NOTIFICATIONS = [TAVERN_ID];
|
||||||
|
|||||||
@@ -550,6 +550,7 @@ let schema = new Schema({
|
|||||||
}},
|
}},
|
||||||
webhooks: [WebhookSchema],
|
webhooks: [WebhookSchema],
|
||||||
loginIncentives: {type: Number, default: 0},
|
loginIncentives: {type: Number, default: 0},
|
||||||
|
invitesSent: {type: Number, default: 0},
|
||||||
}, {
|
}, {
|
||||||
strict: true,
|
strict: true,
|
||||||
minimize: false, // So empty objects are returned
|
minimize: false, // So empty objects are returned
|
||||||
|
|||||||
Reference in New Issue
Block a user