fix(server): avoid job fail if mail is not configured (#12306)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved email notification handling to prevent errors from interrupting other processes when sending emails is not possible. - **Refactor** - Updated internal email sending logic across notifications and workspace features for more robust operation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -6,6 +6,9 @@ import { MailName } from '../../mails';
|
|||||||
|
|
||||||
export class MockMailer {
|
export class MockMailer {
|
||||||
send = Sinon.createStubInstance(Mailer).send.resolves(true);
|
send = Sinon.createStubInstance(Mailer).send.resolves(true);
|
||||||
|
trySend(command: Jobs['notification.sendMail']) {
|
||||||
|
return this.send(command, true);
|
||||||
|
}
|
||||||
|
|
||||||
last<Mail extends MailName>(
|
last<Mail extends MailName>(
|
||||||
name: Mail
|
name: Mail
|
||||||
|
|||||||
@@ -10,13 +10,20 @@ export class Mailer {
|
|||||||
private readonly sender: MailSender
|
private readonly sender: MailSender
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get enabled() {
|
/**
|
||||||
// @ts-expect-error internal api
|
* try to send mail
|
||||||
return this.sender.smtp !== null;
|
*
|
||||||
|
* @note never throw
|
||||||
|
*/
|
||||||
|
async trySend(command: Jobs['notification.sendMail']) {
|
||||||
|
return this.send(command, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(command: Jobs['notification.sendMail']) {
|
async send(command: Jobs['notification.sendMail'], suppressError = false) {
|
||||||
if (!this.enabled) {
|
if (!this.sender.configured) {
|
||||||
|
if (suppressError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
throw new EmailServiceNotConfigured();
|
throw new EmailServiceNotConfigured();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ export class MailSender {
|
|||||||
return createTransport(configToSMTPOptions(config));
|
return createTransport(configToSMTPOptions(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get configured() {
|
||||||
|
return this.smtp !== null;
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent('config.init')
|
@OnEvent('config.init')
|
||||||
onConfigInit() {
|
onConfigInit() {
|
||||||
this.setup();
|
this.setup();
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class NotificationService {
|
|||||||
elementId: input.body.doc.elementId,
|
elementId: input.body.doc.elementId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'Mention',
|
name: 'Mention',
|
||||||
to: receiver.email,
|
to: receiver.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -110,7 +110,7 @@ export class NotificationService {
|
|||||||
if (!receiver) {
|
if (!receiver) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'MemberInvitation',
|
name: 'MemberInvitation',
|
||||||
to: receiver.email,
|
to: receiver.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -161,7 +161,7 @@ export class NotificationService {
|
|||||||
if (!inviter) {
|
if (!inviter) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'MemberAccepted',
|
name: 'MemberAccepted',
|
||||||
to: inviter.email,
|
to: inviter.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -226,7 +226,7 @@ export class NotificationService {
|
|||||||
if (!reviewer) {
|
if (!reviewer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'LinkInvitationReviewRequest',
|
name: 'LinkInvitationReviewRequest',
|
||||||
to: reviewer.email,
|
to: reviewer.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -273,7 +273,7 @@ export class NotificationService {
|
|||||||
if (!receiver) {
|
if (!receiver) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'LinkInvitationApprove',
|
name: 'LinkInvitationApprove',
|
||||||
to: receiver.email,
|
to: receiver.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -312,7 +312,7 @@ export class NotificationService {
|
|||||||
if (!receiver) {
|
if (!receiver) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'LinkInvitationDecline',
|
name: 'LinkInvitationDecline',
|
||||||
to: receiver.email,
|
to: receiver.email,
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class WorkspaceEvents {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'MemberRemoved',
|
name: 'MemberRemoved',
|
||||||
to: user.email,
|
to: user.email,
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export class WorkspaceService {
|
|||||||
const admins = await this.models.workspaceUser.getAdmins(workspaceId);
|
const admins = await this.models.workspaceUser.getAdmins(workspaceId);
|
||||||
|
|
||||||
const link = this.url.link(`/workspace/${workspaceId}`);
|
const link = this.url.link(`/workspace/${workspaceId}`);
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'TeamWorkspaceUpgraded',
|
name: 'TeamWorkspaceUpgraded',
|
||||||
to: owner.email,
|
to: owner.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -121,7 +121,7 @@ export class WorkspaceService {
|
|||||||
|
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
admins.map(async user => {
|
admins.map(async user => {
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'TeamWorkspaceUpgraded',
|
name: 'TeamWorkspaceUpgraded',
|
||||||
to: user.email,
|
to: user.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -188,7 +188,7 @@ export class WorkspaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ws.role === WorkspaceRole.Admin) {
|
if (ws.role === WorkspaceRole.Admin) {
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'TeamBecomeAdmin',
|
name: 'TeamBecomeAdmin',
|
||||||
to: user.email,
|
to: user.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -199,7 +199,7 @@ export class WorkspaceService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'TeamBecomeCollaborator',
|
name: 'TeamBecomeCollaborator',
|
||||||
to: user.email,
|
to: user.email,
|
||||||
props: {
|
props: {
|
||||||
@@ -213,7 +213,7 @@ export class WorkspaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendOwnershipTransferredEmail(email: string, ws: { id: string }) {
|
async sendOwnershipTransferredEmail(email: string, ws: { id: string }) {
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'OwnershipTransferred',
|
name: 'OwnershipTransferred',
|
||||||
to: email,
|
to: email,
|
||||||
props: {
|
props: {
|
||||||
@@ -225,7 +225,7 @@ export class WorkspaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendOwnershipReceivedEmail(email: string, ws: { id: string }) {
|
async sendOwnershipReceivedEmail(email: string, ws: { id: string }) {
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'OwnershipReceived',
|
name: 'OwnershipReceived',
|
||||||
to: email,
|
to: email,
|
||||||
props: {
|
props: {
|
||||||
@@ -238,7 +238,7 @@ export class WorkspaceService {
|
|||||||
|
|
||||||
async sendLeaveEmail(workspaceId: string, userId: string) {
|
async sendLeaveEmail(workspaceId: string, userId: string) {
|
||||||
const owner = await this.models.workspaceUser.getOwner(workspaceId);
|
const owner = await this.models.workspaceUser.getOwner(workspaceId);
|
||||||
await this.mailer.send({
|
await this.mailer.trySend({
|
||||||
name: 'MemberLeave',
|
name: 'MemberLeave',
|
||||||
to: owner.email,
|
to: owner.email,
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
Reference in New Issue
Block a user