mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 06:18:45 +08:00
feat(server): new email template (#9528)
use `yarn af server dev:mail` to preview all mail template fix CLOUD-93
This commit is contained in:
@@ -302,31 +302,29 @@ export class AuthService implements OnApplicationBootstrap {
|
||||
}
|
||||
|
||||
async sendChangePasswordEmail(email: string, callbackUrl: string) {
|
||||
return this.mailer.sendChangePasswordEmail(email, callbackUrl);
|
||||
return this.mailer.sendChangePasswordMail(email, { url: callbackUrl });
|
||||
}
|
||||
async sendSetPasswordEmail(email: string, callbackUrl: string) {
|
||||
return this.mailer.sendSetPasswordEmail(email, callbackUrl);
|
||||
return this.mailer.sendSetPasswordMail(email, { url: callbackUrl });
|
||||
}
|
||||
async sendChangeEmail(email: string, callbackUrl: string) {
|
||||
return this.mailer.sendChangeEmail(email, callbackUrl);
|
||||
return this.mailer.sendChangeEmailMail(email, { url: callbackUrl });
|
||||
}
|
||||
async sendVerifyChangeEmail(email: string, callbackUrl: string) {
|
||||
return this.mailer.sendVerifyChangeEmail(email, callbackUrl);
|
||||
return this.mailer.sendVerifyChangeEmail(email, { url: callbackUrl });
|
||||
}
|
||||
async sendVerifyEmail(email: string, callbackUrl: string) {
|
||||
return this.mailer.sendVerifyEmail(email, callbackUrl);
|
||||
return this.mailer.sendVerifyEmail(email, { url: callbackUrl });
|
||||
}
|
||||
async sendNotificationChangeEmail(email: string) {
|
||||
return this.mailer.sendNotificationChangeEmail(email);
|
||||
return this.mailer.sendNotificationChangeEmail(email, {
|
||||
to: email,
|
||||
});
|
||||
}
|
||||
|
||||
async sendSignInEmail(email: string, link: string, signUp: boolean) {
|
||||
return signUp
|
||||
? await this.mailer.sendSignUpMail(link, {
|
||||
to: email,
|
||||
})
|
||||
: await this.mailer.sendSignInMail(link, {
|
||||
to: email,
|
||||
});
|
||||
? await this.mailer.sendSignUpMail(email, { url: link })
|
||||
: await this.mailer.sendSignInMail(email, { url: link });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
type EventPayload,
|
||||
MailService,
|
||||
OnEvent,
|
||||
URLHelper,
|
||||
UserNotFound,
|
||||
} from '../../../base';
|
||||
import { Models } from '../../../models';
|
||||
@@ -23,13 +24,6 @@ export type InviteInfo = {
|
||||
inviteeUserId?: string;
|
||||
};
|
||||
|
||||
const PermissionToRole = {
|
||||
[Permission.Read]: 'readonly' as const,
|
||||
[Permission.Write]: 'member' as const,
|
||||
[Permission.Admin]: 'admin' as const,
|
||||
[Permission.Owner]: 'owner' as const,
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceService {
|
||||
private readonly logger = new Logger(WorkspaceService.name);
|
||||
@@ -41,7 +35,8 @@ export class WorkspaceService {
|
||||
private readonly mailer: MailService,
|
||||
private readonly permission: PermissionService,
|
||||
private readonly prisma: PrismaClient,
|
||||
private readonly models: Models
|
||||
private readonly models: Models,
|
||||
private readonly url: URLHelper
|
||||
) {}
|
||||
|
||||
async getInviteInfo(inviteId: string): Promise<InviteInfo> {
|
||||
@@ -87,7 +82,7 @@ export class WorkspaceService {
|
||||
return {
|
||||
avatar,
|
||||
id: workspaceId,
|
||||
name: workspaceContent?.name ?? '',
|
||||
name: workspaceContent?.name ?? 'Untitled Workspace',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -130,26 +125,47 @@ export class WorkspaceService {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.mailer.sendAcceptedEmail(inviter.email, {
|
||||
inviteeName: invitee.name,
|
||||
workspaceName: workspace.name,
|
||||
await this.mailer.sendMemberAcceptedEmail(inviter.email, {
|
||||
user: invitee,
|
||||
workspace,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
async sendInviteEmail(inviteId: string) {
|
||||
const target = await this.getInviteeEmailTarget(inviteId);
|
||||
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = await this.permission.getWorkspaceOwner(target.workspace.id);
|
||||
|
||||
await this.mailer.sendMemberInviteMail(target.email, {
|
||||
workspace: target.workspace,
|
||||
user: owner,
|
||||
url: this.url.link(`/invite/${inviteId}`),
|
||||
});
|
||||
}
|
||||
|
||||
// ================ Team ================
|
||||
|
||||
async sendTeamWorkspaceUpgradedEmail(workspaceId: string) {
|
||||
const workspace = await this.getWorkspaceInfo(workspaceId);
|
||||
const owner = await this.permission.getWorkspaceOwner(workspaceId);
|
||||
const admin = await this.permission.getWorkspaceAdmin(workspaceId);
|
||||
const admins = await this.permission.getWorkspaceAdmin(workspaceId);
|
||||
|
||||
await this.mailer.sendTeamWorkspaceUpgradedEmail(owner.email, {
|
||||
...workspace,
|
||||
workspace,
|
||||
isOwner: true,
|
||||
url: this.url.link(`/workspace/${workspaceId}`),
|
||||
});
|
||||
for (const user of admin) {
|
||||
|
||||
for (const user of admins) {
|
||||
await this.mailer.sendTeamWorkspaceUpgradedEmail(user.email, {
|
||||
...workspace,
|
||||
workspace,
|
||||
isOwner: false,
|
||||
url: this.url.link(`/workspace/${workspaceId}`),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -174,48 +190,34 @@ export class WorkspaceService {
|
||||
const admin = await this.permission.getWorkspaceAdmin(workspaceId);
|
||||
|
||||
for (const user of [owner, ...admin]) {
|
||||
await this.mailer.sendReviewRequestEmail(
|
||||
user.email,
|
||||
invitee.email,
|
||||
workspace
|
||||
);
|
||||
await this.mailer.sendLinkInvitationReviewRequestMail(user.email, {
|
||||
workspace,
|
||||
user: invitee,
|
||||
url: this.url.link(`/workspace/${workspace.id}`),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async sendInviteEmail(inviteId: string) {
|
||||
const target = await this.getInviteeEmailTarget(inviteId);
|
||||
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = await this.permission.getWorkspaceOwner(target.workspace.id);
|
||||
|
||||
await this.mailer.sendInviteEmail(target.email, inviteId, {
|
||||
workspace: target.workspace,
|
||||
user: {
|
||||
avatar: owner.avatarUrl || '',
|
||||
name: owner.name || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async sendReviewApproveEmail(inviteId: string) {
|
||||
const target = await this.getInviteeEmailTarget(inviteId);
|
||||
if (!target) return;
|
||||
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.mailer.sendReviewApproveEmail(target.email, target.workspace);
|
||||
await this.mailer.sendLinkInvitationApproveMail(target.email, {
|
||||
workspace: target.workspace,
|
||||
url: this.url.link(`/workspace/${target.workspace.id}`),
|
||||
});
|
||||
}
|
||||
|
||||
async sendReviewDeclinedEmail(
|
||||
email: string | undefined,
|
||||
workspaceName: string
|
||||
workspaceId: string
|
||||
) {
|
||||
if (!email) return;
|
||||
await this.mailer.sendReviewDeclinedEmail(email, { name: workspaceName });
|
||||
|
||||
const workspace = await this.getWorkspaceInfo(workspaceId);
|
||||
await this.mailer.sendLinkInvitationDeclineMail(email, {
|
||||
workspace,
|
||||
});
|
||||
}
|
||||
|
||||
async sendRoleChangedEmail(
|
||||
@@ -224,24 +226,42 @@ export class WorkspaceService {
|
||||
) {
|
||||
const user = await this.models.user.getPublicUser(userId);
|
||||
if (!user) throw new UserNotFound();
|
||||
|
||||
const workspace = await this.getWorkspaceInfo(ws.id);
|
||||
await this.mailer.sendRoleChangedEmail(user?.email, {
|
||||
name: workspace.name,
|
||||
role: PermissionToRole[ws.role],
|
||||
});
|
||||
|
||||
if (ws.role === Permission.Admin) {
|
||||
await this.mailer.sendTeamBecomeAdminMail(user.email, {
|
||||
workspace,
|
||||
url: this.url.link(`/workspace/${workspace.id}`),
|
||||
});
|
||||
} else {
|
||||
await this.mailer.sendTeamBecomeCollaboratorMail(user.email, {
|
||||
workspace,
|
||||
url: this.url.link(`/workspace/${workspace.id}`),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async sendOwnerTransferredEmail(email: string, ws: { id: string }) {
|
||||
async sendOwnershipTransferredEmail(email: string, ws: { id: string }) {
|
||||
const workspace = await this.getWorkspaceInfo(ws.id);
|
||||
await this.mailer.sendOwnershipTransferredEmail(email, {
|
||||
name: workspace.name,
|
||||
});
|
||||
await this.mailer.sendOwnershipTransferredMail(email, { workspace });
|
||||
}
|
||||
|
||||
async sendMemberRemoved(email: string, ws: { id: string }) {
|
||||
async sendOwnershipReceivedEmail(email: string, ws: { id: string }) {
|
||||
const workspace = await this.getWorkspaceInfo(ws.id);
|
||||
await this.mailer.sendMemberRemovedEmail(email, {
|
||||
name: workspace.name,
|
||||
await this.mailer.sendOwnershipReceivedMail(email, { workspace });
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.leave')
|
||||
async onMemberLeave({
|
||||
user,
|
||||
workspaceId,
|
||||
}: EventPayload<'workspace.members.leave'>) {
|
||||
const workspace = await this.getWorkspaceInfo(workspaceId);
|
||||
const owner = await this.permission.getWorkspaceOwner(workspaceId);
|
||||
await this.mailer.sendMemberLeaveEmail(owner.email, {
|
||||
workspace,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -252,22 +272,8 @@ export class WorkspaceService {
|
||||
}: EventPayload<'workspace.members.requestDeclined'>) {
|
||||
const user = await this.models.user.get(userId);
|
||||
if (!user) return;
|
||||
await this.sendMemberRemoved(user.email, { id: workspaceId });
|
||||
}
|
||||
|
||||
@OnEvent('workspace.subscription.notify')
|
||||
async onSubscriptionNotify({
|
||||
workspaceId,
|
||||
expirationDate,
|
||||
deletionDate,
|
||||
}: EventPayload<'workspace.subscription.notify'>) {
|
||||
const owner = await this.permission.getWorkspaceOwner(workspaceId);
|
||||
if (!owner) return;
|
||||
const workspace = await this.getWorkspaceInfo(workspaceId);
|
||||
await this.mailer.sendWorkspaceExpireRemindEmail(owner.email, {
|
||||
...workspace,
|
||||
expirationDate,
|
||||
deletionDate,
|
||||
});
|
||||
await this.mailer.sendMemberRemovedMail(user.email, { workspace });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,15 +312,17 @@ export class TeamWorkspaceResolver {
|
||||
);
|
||||
|
||||
if (result) {
|
||||
this.event.emit('workspace.members.roleChanged', {
|
||||
userId,
|
||||
workspaceId,
|
||||
permission,
|
||||
});
|
||||
if (permission === Permission.Owner) {
|
||||
this.event.emit('workspace.members.ownerTransferred', {
|
||||
email: user.email,
|
||||
this.event.emit('workspace.members.ownershipTransferred', {
|
||||
workspaceId,
|
||||
from: user.id,
|
||||
to: userId,
|
||||
});
|
||||
} else {
|
||||
this.event.emit('workspace.members.roleChanged', {
|
||||
userId,
|
||||
workspaceId,
|
||||
permission,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -347,20 +349,6 @@ export class TeamWorkspaceResolver {
|
||||
await this.workspaceService.sendReviewRequestedEmail(inviteId);
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.requestDeclined')
|
||||
async onDeclineRequest({
|
||||
userId,
|
||||
workspaceId,
|
||||
}: EventPayload<'workspace.members.requestDeclined'>) {
|
||||
const user = await this.models.user.getPublicUser(userId);
|
||||
const workspace = await this.workspaceService.getWorkspaceInfo(workspaceId);
|
||||
// send decline mail
|
||||
await this.workspaceService.sendReviewDeclinedEmail(
|
||||
user?.email,
|
||||
workspace.name
|
||||
);
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.requestApproved')
|
||||
async onApproveRequest({
|
||||
inviteId,
|
||||
@@ -369,6 +357,19 @@ export class TeamWorkspaceResolver {
|
||||
await this.workspaceService.sendReviewApproveEmail(inviteId);
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.requestDeclined')
|
||||
async onDeclineRequest({
|
||||
userId,
|
||||
workspaceId,
|
||||
}: EventPayload<'workspace.members.requestDeclined'>) {
|
||||
const user = await this.models.user.getPublicUser(userId);
|
||||
// send decline mail
|
||||
await this.workspaceService.sendReviewDeclinedEmail(
|
||||
user?.email,
|
||||
workspaceId
|
||||
);
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.roleChanged')
|
||||
async onRoleChanged({
|
||||
userId,
|
||||
@@ -382,14 +383,29 @@ export class TeamWorkspaceResolver {
|
||||
});
|
||||
}
|
||||
|
||||
@OnEvent('workspace.members.ownerTransferred')
|
||||
@OnEvent('workspace.members.ownershipTransferred')
|
||||
async onOwnerTransferred({
|
||||
email,
|
||||
workspaceId,
|
||||
}: EventPayload<'workspace.members.ownerTransferred'>) {
|
||||
// send role changed mail
|
||||
await this.workspaceService.sendOwnerTransferredEmail(email, {
|
||||
id: workspaceId,
|
||||
});
|
||||
from,
|
||||
to,
|
||||
}: EventPayload<'workspace.members.ownershipTransferred'>) {
|
||||
// send ownership transferred mail
|
||||
const fromUser = await this.models.user.getPublicUser(from);
|
||||
const toUser = await this.models.user.getPublicUser(to);
|
||||
|
||||
if (fromUser) {
|
||||
await this.workspaceService.sendOwnershipTransferredEmail(
|
||||
fromUser.email,
|
||||
{
|
||||
id: workspaceId,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (toUser) {
|
||||
await this.workspaceService.sendOwnershipReceivedEmail(toUser.email, {
|
||||
id: workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
DocNotFound,
|
||||
EventEmitter,
|
||||
InternalServerError,
|
||||
MailService,
|
||||
MemberQuotaExceeded,
|
||||
RequestMutex,
|
||||
SpaceAccessDenied,
|
||||
@@ -79,7 +78,6 @@ export class WorkspaceResolver {
|
||||
|
||||
constructor(
|
||||
private readonly cache: Cache,
|
||||
private readonly mailer: MailService,
|
||||
private readonly prisma: PrismaClient,
|
||||
private readonly permissions: PermissionService,
|
||||
private readonly quota: QuotaManagementService,
|
||||
@@ -611,17 +609,18 @@ export class WorkspaceResolver {
|
||||
_workspaceName?: string
|
||||
) {
|
||||
await this.permissions.checkWorkspace(workspaceId, user.id);
|
||||
const { name: workspaceName } =
|
||||
await this.workspaceService.getWorkspaceInfo(workspaceId);
|
||||
const owner = await this.permissions.getWorkspaceOwner(workspaceId);
|
||||
const success = this.permissions.revokeWorkspace(workspaceId, user.id);
|
||||
|
||||
if (sendLeaveMail) {
|
||||
await this.mailer.sendLeaveWorkspaceEmail(owner.email, {
|
||||
workspaceName,
|
||||
inviteeName: user.name,
|
||||
this.event.emit('workspace.members.leave', {
|
||||
workspaceId,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return this.permissions.revokeWorkspace(workspaceId, user.id);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user