chore(server): remove enable flag in mail config (#11680)

close #11625
This commit is contained in:
forehalo
2025-04-15 09:18:09 +00:00
parent b249939093
commit 7257f1b55b
15 changed files with 55 additions and 70 deletions
-5
View File
@@ -200,11 +200,6 @@
"type": "object",
"description": "Configuration for mailer module",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether enabled mail service.\n@default false",
"default": false
},
"SMTP.host": {
"type": "string",
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"\n@environment `MAILER_HOST`",
@@ -275,6 +275,10 @@ export const USER_FRIENDLY_ERRORS = {
args: { message: 'string' },
message: ({ message }) => `HTTP request error, message: ${message}`,
},
email_service_not_configured: {
type: 'internal_server_error',
message: 'Email service is not configured.',
},
// Input errors
query_too_long: {
@@ -54,6 +54,12 @@ export class HttpRequestError extends UserFriendlyError {
super('bad_request', 'http_request_error', message, args);
}
}
export class EmailServiceNotConfigured extends UserFriendlyError {
constructor(message?: string) {
super('internal_server_error', 'email_service_not_configured', message);
}
}
@ObjectType()
class QueryTooLongDataType {
@Field() max!: number
@@ -943,6 +949,7 @@ export enum ErrorNames {
BAD_REQUEST,
GRAPHQL_BAD_REQUEST,
HTTP_REQUEST_ERROR,
EMAIL_SERVICE_NOT_CONFIGURED,
QUERY_TOO_LONG,
VALIDATION_ERROR,
USER_NOT_FOUND,
@@ -37,7 +37,6 @@ import { CurrentUser, Session } from './session';
interface PreflightResponse {
registered: boolean;
hasPassword: boolean;
magicLink: boolean;
}
interface SignInCredential {
@@ -91,20 +90,16 @@ export class AuthController {
const user = await this.models.user.getUserByEmail(params.email);
const magicLinkAvailable = this.config.mailer.enabled;
if (!user) {
return {
registered: false,
hasPassword: false,
magicLink: magicLinkAvailable,
};
}
return {
registered: user.registered,
hasPassword: !!user.password,
magicLink: magicLinkAvailable,
};
}
@@ -3,7 +3,6 @@ import { defineModuleConfig } from '../../base';
declare global {
interface AppConfigSchema {
mailer: {
enabled: boolean;
SMTP: {
host: string;
port: number;
@@ -17,10 +16,6 @@ declare global {
}
defineModuleConfig('mailer', {
enabled: {
desc: 'Whether enabled mail service.',
default: false,
},
'SMTP.host': {
desc: 'Host of the email server (e.g. smtp.gmail.com)',
default: '',
@@ -49,6 +44,6 @@ defineModuleConfig('mailer', {
'SMTP.ignoreTLS': {
desc: "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.",
default: false,
env: 'MAILER_IGNORE_TLS',
env: ['MAILER_IGNORE_TLS', 'boolean'],
},
});
@@ -1,12 +1,25 @@
import { Injectable } from '@nestjs/common';
import { JobQueue } from '../../base';
import { EmailServiceNotConfigured, JobQueue } from '../../base';
import { MailSender } from './sender';
@Injectable()
export class Mailer {
constructor(private readonly queue: JobQueue) {}
constructor(
private readonly queue: JobQueue,
private readonly sender: MailSender
) {}
get enabled() {
// @ts-expect-error internal api
return this.sender.smtp !== null;
}
async send(command: Jobs['notification.sendMail']) {
if (!this.enabled) {
throw new EmailServiceNotConfigured();
}
try {
await this.queue.add('notification.sendMail', command);
return true;
@@ -56,13 +56,7 @@ export class MailSender {
}
private setup() {
const { SMTP, enabled } = this.config.mailer;
if (!enabled) {
this.smtp = null;
return;
}
const { SMTP } = this.config.mailer;
const opts = configToSMTPOptions(SMTP);
if (SMTP.host) {
@@ -83,6 +77,7 @@ export class MailSender {
});
} else {
this.logger.warn('Mailer SMTP transport is not configured.');
this.smtp = null;
}
}
+1
View File
@@ -447,6 +447,7 @@ enum ErrorNames {
DOC_UPDATE_BLOCKED
EARLY_ACCESS_REQUIRED
EMAIL_ALREADY_USED
EMAIL_SERVICE_NOT_CONFIGURED
EMAIL_TOKEN_NOT_FOUND
EMAIL_VERIFICATION_REQUIRED
EXPECT_TO_GRANT_DOC_USER_ROLES
+1
View File
@@ -592,6 +592,7 @@ export enum ErrorNames {
DOC_UPDATE_BLOCKED = 'DOC_UPDATE_BLOCKED',
EARLY_ACCESS_REQUIRED = 'EARLY_ACCESS_REQUIRED',
EMAIL_ALREADY_USED = 'EMAIL_ALREADY_USED',
EMAIL_SERVICE_NOT_CONFIGURED = 'EMAIL_SERVICE_NOT_CONFIGURED',
EMAIL_TOKEN_NOT_FOUND = 'EMAIL_TOKEN_NOT_FOUND',
EMAIL_VERIFICATION_REQUIRED = 'EMAIL_VERIFICATION_REQUIRED',
EXPECT_TO_GRANT_DOC_USER_ROLES = 'EXPECT_TO_GRANT_DOC_USER_ROLES',
-4
View File
@@ -92,10 +92,6 @@
}
},
"mailer": {
"enabled": {
"type": "Boolean",
"desc": "Whether enabled mail service."
},
"SMTP.host": {
"type": "String",
"desc": "Host of the email server (e.g. smtp.gmail.com)",
@@ -76,7 +76,6 @@ export const KNOWN_CONFIG_GROUPS = [
name: 'Notification',
module: 'mailer',
fields: [
'enabled',
'SMTP.host',
'SMTP.port',
'SMTP.username',
@@ -11,7 +11,7 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo
import { AuthService, CaptchaService } from '@affine/core/modules/cloud';
import type { AuthSessionStatus } from '@affine/core/modules/cloud/entities/session';
import { Unreachable } from '@affine/env/constant';
import type { UserFriendlyError } from '@affine/error';
import { UserFriendlyError } from '@affine/error';
import { Trans, useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import {
@@ -95,8 +95,10 @@ export const SignInWithEmailStep = ({
);
} catch (err) {
console.error(err);
const error = UserFriendlyError.fromAny(err);
notify.error({
title: 'Failed to send email, please try again.',
title: 'Failed to sign in',
message: t[`error.${error.name}`](error.data),
});
}
setIsSending(false);
@@ -109,6 +111,7 @@ export const SignInWithEmailStep = ({
needCaptcha,
state.redirectUrl,
verifyToken,
t,
]);
useEffect(() => {
@@ -93,46 +93,22 @@ export const SignInStep = ({
setIsMutating(true);
try {
const { hasPassword, registered, magicLink } =
await authService.checkUserByEmail(email);
const { hasPassword } = await authService.checkUserByEmail(email);
if (registered) {
// provider password sign-in if user has by default
// If with payment, onl support email sign in to avoid redirect to affine app
if (hasPassword) {
changeState(prev => ({
...prev,
email,
step: 'signInWithPassword',
hasPassword: true,
}));
} else {
if (magicLink) {
changeState(prev => ({
...prev,
email,
step: 'signInWithEmail',
hasPassword: false,
}));
} else {
notify.error({
title: 'Failed to send email. Please contact the administrator.',
});
}
}
if (hasPassword) {
changeState(prev => ({
...prev,
email,
step: 'signInWithPassword',
hasPassword: true,
}));
} else {
if (magicLink) {
changeState(prev => ({
...prev,
email,
step: 'signInWithEmail',
hasPassword: false,
}));
} else {
notify.error({
title: 'Failed to send email. Please contact the administrator.',
});
}
changeState(prev => ({
...prev,
email,
step: 'signInWithEmail',
hasPassword: false,
}));
}
} catch (err: any) {
console.error(err);
+4
View File
@@ -7690,6 +7690,10 @@ export function useAFFiNEI18N(): {
["error.HTTP_REQUEST_ERROR"](options: {
readonly message: string;
}): string;
/**
* `Email service is not configured.`
*/
["error.EMAIL_SERVICE_NOT_CONFIGURED"](): string;
/**
* `Query is too long, max length is {{max}}.`
*/
@@ -1923,6 +1923,7 @@
"error.BAD_REQUEST": "Bad request.",
"error.GRAPHQL_BAD_REQUEST": "GraphQL bad request, code: {{code}}, {{message}}",
"error.HTTP_REQUEST_ERROR": "HTTP request error, message: {{message}}",
"error.EMAIL_SERVICE_NOT_CONFIGURED": "Email service is not configured.",
"error.QUERY_TOO_LONG": "Query is too long, max length is {{max}}.",
"error.VALIDATION_ERROR": "Validation error, errors: {{errors}}",
"error.USER_NOT_FOUND": "User not found.",