feat: send email to owner after member accepted invitation / leave workspace (#4152)

Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
Qi
2023-09-07 13:08:23 +08:00
committed by GitHub
parent 1301605db5
commit 498683ff4c
15 changed files with 259 additions and 47 deletions
@@ -160,4 +160,50 @@ export class MailService {
html,
});
}
async sendAcceptedEmail(
to: string,
{
inviteeName,
workspaceName,
}: {
inviteeName: string;
workspaceName: string;
}
) {
const title = `${inviteeName} accepted your invitation`;
const html = emailTemplate({
title,
content: `${inviteeName} has joined ${workspaceName}`,
});
return this.sendMail({
from: this.config.auth.email.sender,
to,
subject: title,
html,
});
}
async sendLeaveWorkspaceEmail(
to: string,
{
inviteeName,
workspaceName,
}: {
inviteeName: string;
workspaceName: string;
}
) {
const title = `${inviteeName} left ${workspaceName}`;
const html = emailTemplate({
title,
content: `${inviteeName} has left your workspace`,
});
return this.sendMail({
from: this.config.auth.email.sender,
to,
subject: title,
html,
});
}
}
@@ -7,8 +7,8 @@ export const emailTemplate = ({
}: {
title: string;
content: string;
buttonContent: string;
buttonUrl: string;
buttonContent?: string;
buttonUrl?: string;
subContent?: string;
}) => {
return `<body style="background: #f6f7fb; overflow: hidden">
@@ -59,7 +59,9 @@ export const emailTemplate = ({
"
>${content}</td>
</tr>
<tr>
${
buttonContent && buttonUrl
? `<tr>
<td style="margin-left: 24px; padding-top: 0; padding-bottom: ${
subContent ? '0' : '64px'
}">
@@ -88,7 +90,9 @@ export const emailTemplate = ({
</tr>
</table>
</td>
</tr>
</tr>`
: ''
}
${
subContent
? `<tr>
+61 -2
View File
@@ -109,6 +109,8 @@ export class InvitationType {
workspace!: InvitationWorkspaceType;
@Field({ description: 'User information' })
user!: UserType;
@Field({ description: 'Invitee information' })
invitee!: UserType;
}
@InputType()
@@ -514,6 +516,17 @@ export class WorkspaceResolver {
user: true,
},
});
const invitee = await this.prisma.userWorkspacePermission.findUniqueOrThrow(
{
where: {
id: inviteId,
workspaceId: permission.workspaceId,
},
include: {
user: true,
},
}
);
let avatar = '';
@@ -532,6 +545,7 @@ export class WorkspaceResolver {
id: permission.workspaceId,
},
user: owner.user,
invitee: invitee.user,
};
}
@@ -550,8 +564,28 @@ export class WorkspaceResolver {
@Public()
async acceptInviteById(
@Args('workspaceId') workspaceId: string,
@Args('inviteId') inviteId: string
@Args('inviteId') inviteId: string,
@Args('sendAcceptMail', { nullable: true }) sendAcceptMail: boolean
) {
const {
invitee,
user: inviter,
workspace,
} = await this.getInviteInfo(inviteId);
if (!inviter || !invitee) {
throw new ForbiddenException(
`can not find inviter/invitee by inviteId: ${inviteId}`
);
}
if (sendAcceptMail) {
await this.mailer.sendAcceptedEmail(inviter.email, {
inviteeName: invitee.name,
workspaceName: workspace.name,
});
}
return this.permissionProvider.acceptById(workspaceId, inviteId);
}
@@ -566,10 +600,35 @@ export class WorkspaceResolver {
@Mutation(() => Boolean)
async leaveWorkspace(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string
@Args('workspaceId') workspaceId: string,
@Args('workspaceName') workspaceName: string,
@Args('sendLeaveMail', { nullable: true }) sendLeaveMail: boolean
) {
await this.permissionProvider.check(workspaceId, user.id);
const owner = await this.prisma.userWorkspacePermission.findFirstOrThrow({
where: {
workspaceId,
type: Permission.Owner,
},
include: {
user: true,
},
});
if (!owner.user) {
throw new ForbiddenException(
`can not find owner by workspaceId: ${workspaceId}`
);
}
if (sendLeaveMail) {
await this.mailer.sendLeaveWorkspaceEmail(owner.user.email, {
workspaceName,
inviteeName: user.name,
});
}
return this.permissionProvider.revoke(workspaceId, user.id);
}