feat: do not enforce TLD on email (#2075)

fix #1846
This commit is contained in:
Ludovic Ortega
2025-10-20 17:24:24 +03:00
committed by GitHub
parent 48a61d812b
commit b34ca1543a
8 changed files with 47 additions and 11 deletions

View File

@@ -221,7 +221,9 @@ class EmailAgent
this.getSettings(), this.getSettings(),
payload.notifyUser.settings?.pgpKey payload.notifyUser.settings?.pgpKey
); );
if (validator.isEmail(payload.notifyUser.email)) { if (
validator.isEmail(payload.notifyUser.email, { require_tld: false })
) {
await email.send( await email.send(
this.buildMessage( this.buildMessage(
type, type,
@@ -283,7 +285,7 @@ class EmailAgent
this.getSettings(), this.getSettings(),
user.settings?.pgpKey user.settings?.pgpKey
); );
if (validator.isEmail(user.email)) { if (validator.isEmail(user.email, { require_tld: false })) {
await email.send( await email.send(
this.buildMessage(type, payload, user.email, user.displayName) this.buildMessage(type, payload, user.email, user.displayName)
); );

View File

@@ -37,7 +37,7 @@ authRoutes.get('/me', isAuthenticated(), async (req, res) => {
const settings = await getSettings(); const settings = await getSettings();
if ( if (
settings.notifications.agents.email.options.userEmailRequired && settings.notifications.agents.email.options.userEmailRequired &&
!validator.isEmail(user.email) !validator.isEmail(user.email, { require_tld: false })
) { ) {
user.warnings.push('userEmailRequired'); user.warnings.push('userEmailRequired');
logger.warn(`User ${user.username} has no valid email address`); logger.warn(`User ${user.username} has no valid email address`);

View File

@@ -5,6 +5,7 @@ import { Transition } from '@headlessui/react';
import axios from 'axios'; import axios from 'axios';
import { Field, Formik } from 'formik'; import { Field, Formik } from 'formik';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
const messages = defineMessages('components.Login', { const messages = defineMessages('components.Login', {
@@ -36,7 +37,11 @@ const AddEmailModal: React.FC<AddEmailModalProps> = ({
const EmailSettingsSchema = Yup.object().shape({ const EmailSettingsSchema = Yup.object().shape({
email: Yup.string() email: Yup.string()
.email(intl.formatMessage(messages.validationEmailFormat)) .test(
'email',
intl.formatMessage(messages.validationEmailFormat),
(value) => !value || validator.isEmail(value, { require_tld: false })
)
.required(intl.formatMessage(messages.validationEmailRequired)), .required(intl.formatMessage(messages.validationEmailRequired)),
}); });

View File

@@ -10,6 +10,7 @@ import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { useState } from 'react'; import { useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
const messages = defineMessages('components.ResetPassword', { const messages = defineMessages('components.ResetPassword', {
@@ -29,7 +30,11 @@ const ResetPassword = () => {
const ResetSchema = Yup.object().shape({ const ResetSchema = Yup.object().shape({
email: Yup.string() email: Yup.string()
.email(intl.formatMessage(messages.validationemailrequired)) .test(
'email',
intl.formatMessage(messages.validationemailrequired),
(value) => !value || validator.isEmail(value, { require_tld: false })
)
.required(intl.formatMessage(messages.validationemailrequired)), .required(intl.formatMessage(messages.validationemailrequired)),
}); });

View File

@@ -11,6 +11,7 @@ import { useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
const messages = defineMessages('components.Settings.Notifications', { const messages = defineMessages('components.Settings.Notifications', {
@@ -77,7 +78,11 @@ const NotificationsEmail = () => {
.required(intl.formatMessage(messages.validationEmail)), .required(intl.formatMessage(messages.validationEmail)),
otherwise: Yup.string().nullable(), otherwise: Yup.string().nullable(),
}) })
.email(intl.formatMessage(messages.validationEmail)), .test(
'email',
intl.formatMessage(messages.validationEmail),
(value) => !value || validator.isEmail(value, { require_tld: false })
),
smtpHost: Yup.string().when('enabled', { smtpHost: Yup.string().when('enabled', {
is: true, is: true,
then: Yup.string() then: Yup.string()

View File

@@ -8,6 +8,7 @@ import axios from 'axios';
import { Field, Form, Formik } from 'formik'; import { Field, Form, Formik } from 'formik';
import { FormattedMessage, useIntl } from 'react-intl'; import { FormattedMessage, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
const messages = defineMessages('components.Login', { const messages = defineMessages('components.Login', {
@@ -90,7 +91,11 @@ function JellyfinSetup({
(value) => !value || !value.endsWith('/') (value) => !value || !value.endsWith('/')
), ),
email: Yup.string() email: Yup.string()
.email(intl.formatMessage(messages.validationemailformat)) .test(
'email',
intl.formatMessage(messages.validationemailformat),
(value) => !value || validator.isEmail(value, { require_tld: false })
)
.required(intl.formatMessage(messages.validationemailrequired)), .required(intl.formatMessage(messages.validationemailrequired)),
username: Yup.string().required( username: Yup.string().required(
intl.formatMessage(messages.validationusernamerequired) intl.formatMessage(messages.validationusernamerequired)

View File

@@ -36,6 +36,7 @@ import { useEffect, useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr'; import useSWR from 'swr';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
import JellyfinImportModal from './JellyfinImportModal'; import JellyfinImportModal from './JellyfinImportModal';
@@ -210,7 +211,11 @@ const UserList = () => {
), ),
email: Yup.string() email: Yup.string()
.required() .required()
.email(intl.formatMessage(messages.validationEmail)), .test(
'email',
intl.formatMessage(messages.validationEmail),
(value) => !value || validator.isEmail(value, { require_tld: false })
),
password: Yup.lazy((value) => password: Yup.lazy((value) =>
!value !value
? Yup.string() ? Yup.string()

View File

@@ -23,6 +23,7 @@ import { useEffect, useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr'; import useSWR from 'swr';
import validator from 'validator';
import * as Yup from 'yup'; import * as Yup from 'yup';
const messages = defineMessages( const messages = defineMessages(
@@ -105,10 +106,18 @@ const UserGeneralSettings = () => {
user?.id === 1 || user?.id === 1 ||
(user?.userType !== UserType.JELLYFIN && user?.userType !== UserType.EMBY) (user?.userType !== UserType.JELLYFIN && user?.userType !== UserType.EMBY)
? Yup.string() ? Yup.string()
.email(intl.formatMessage(messages.validationemailformat)) .test(
'email',
intl.formatMessage(messages.validationemailformat),
(value) =>
!value || validator.isEmail(value, { require_tld: false })
)
.required(intl.formatMessage(messages.validationemailrequired)) .required(intl.formatMessage(messages.validationemailrequired))
: Yup.string().email( : Yup.string().test(
intl.formatMessage(messages.validationemailformat) 'email',
intl.formatMessage(messages.validationemailformat),
(value) =>
!value || validator.isEmail(value, { require_tld: false })
), ),
discordId: Yup.string() discordId: Yup.string()
.nullable() .nullable()