feat: Automatic invite reminder email (#3354)

* feat: Add user flags concept, for tracking bits on a user

* feat: Example flag usage for user invite resend abuse

* wip

* test

* fix: Set correct flag
This commit is contained in:
Tom Moor
2022-04-12 20:12:33 -07:00
committed by GitHub
parent 5520317ce1
commit 86f1645199
11 changed files with 237 additions and 6 deletions

View File

@@ -0,0 +1,67 @@
import { subDays } from "date-fns";
import { Op } from "sequelize";
import { sequelize } from "@server/database/sequelize";
import InviteReminderEmail from "@server/emails/templates/InviteReminderEmail";
import { APM } from "@server/logging/tracing";
import { User } from "@server/models";
import { UserFlag } from "@server/models/User";
import BaseTask, { TaskPriority } from "./BaseTask";
type Props = undefined;
@APM.trace()
export default class InviteReminderTask extends BaseTask<Props> {
public async perform() {
const users = await User.scope("invited").findAll({
attributes: ["id"],
where: {
createdAt: {
[Op.lt]: subDays(new Date(), 2),
[Op.gt]: subDays(new Date(), 3),
},
},
});
const userIds = users.map((user) => user.id);
for (const userId of userIds) {
await sequelize.transaction(async (transaction) => {
const user = await User.scope("withTeam").findByPk(userId, {
lock: {
level: transaction.LOCK.UPDATE,
of: User,
},
transaction,
});
const invitedBy = user?.invitedById
? await User.findByPk(user?.invitedById, { transaction })
: undefined;
if (
user &&
invitedBy &&
user.getFlag(UserFlag.InviteReminderSent) === 0
) {
await InviteReminderEmail.schedule({
to: user.email,
name: user.name,
actorName: invitedBy.name,
actorEmail: invitedBy.email,
teamName: user.team.name,
teamUrl: user.team.url,
});
user.incrementFlag(UserFlag.InviteReminderSent);
await user.save({ transaction });
}
});
}
}
public get options() {
return {
attempts: 1,
priority: TaskPriority.Background,
};
}
}