diff --git a/frontend/src/views/admin/UserSettings.vue b/frontend/src/views/admin/UserSettings.vue index 0ffafd7f..5e7e0e7d 100644 --- a/frontend/src/views/admin/UserSettings.vue +++ b/frontend/src/views/admin/UserSettings.vue @@ -21,6 +21,8 @@ const { t } = useI18n({ manualInputPrompt: 'Type and press Enter to add', mailAllowList: 'Mail Address Allow List', maxAddressCount: 'Maximum number of email addresses that can be binded', + emailCheckRegex: 'Email Check Regex (e.g. ^[^.]+@.+$ to disallow dots before @)', + enableEmailCheckRegex: 'Enable Email Check Regex', }, zh: { save: '保存', @@ -33,6 +35,8 @@ const { t } = useI18n({ manualInputPrompt: '输入后按回车键添加', mailAllowList: '邮件地址白名单', maxAddressCount: '可绑定最大邮箱地址数量', + emailCheckRegex: '邮箱正则校验 (例如 ^[^.]+@.+$ 禁止@前面有.)', + enableEmailCheckRegex: '启用邮箱正则校验', } } }); @@ -53,6 +57,8 @@ const userSettings = ref({ enableMailAllowList: false, mailAllowList: commonMail, maxAddressCount: 5, + enableEmailCheckRegex: false, + emailCheckRegex: "", }); const fetchData = async () => { @@ -125,6 +131,16 @@ onMounted(async () => { :placeholder="t('maxAddressCount')" /> + + + + {{ t('enable') }} + + + + diff --git a/worker/src/i18n/en.ts b/worker/src/i18n/en.ts index cbb9d323..3c3f1cb9 100644 --- a/worker/src/i18n/en.ts +++ b/worker/src/i18n/en.ts @@ -26,6 +26,7 @@ const messages: LocaleMessages = { FailedToRegisterMsg: "Failed to register", UserRegistrationDisabledMsg: "User registration is disabled, please contact the administrator", UserMailDomainMustInMsg: "User mail domain must be in this list", + UserEmailNotMatchRegexMsg: "Email address format does not match the required pattern", InvalidVerifyCodeMsg: "Invalid verify code", InvalidEmailOrPasswordMsg: "Invalid email or password", VerifyMailSenderNotSetMsg: "Verify mail sender address is not set, please contact the administrator", diff --git a/worker/src/i18n/type.ts b/worker/src/i18n/type.ts index ac92c1a3..a47d034d 100644 --- a/worker/src/i18n/type.ts +++ b/worker/src/i18n/type.ts @@ -24,6 +24,7 @@ export type LocaleMessages = { FailedToRegisterMsg: string UserRegistrationDisabledMsg: string UserMailDomainMustInMsg: string + UserEmailNotMatchRegexMsg: string InvalidVerifyCodeMsg: string InvalidEmailOrPasswordMsg: string VerifyMailSenderNotSetMsg: string diff --git a/worker/src/i18n/zh.ts b/worker/src/i18n/zh.ts index 7cce2d1a..7bfadfe9 100644 --- a/worker/src/i18n/zh.ts +++ b/worker/src/i18n/zh.ts @@ -26,6 +26,7 @@ const messages: LocaleMessages = { FailedToRegisterMsg: "注册失败", UserRegistrationDisabledMsg: "用户注册已禁用, 请联系管理员", UserMailDomainMustInMsg: "用户邮箱域必须在此列表中", + UserEmailNotMatchRegexMsg: "邮箱地址格式不符合要求", InvalidVerifyCodeMsg: "无效的验证码", InvalidEmailOrPasswordMsg: "无效的邮箱或密码", VerifyMailSenderNotSetMsg: "验证邮件发送邮箱未设置, 请联系管理员", diff --git a/worker/src/models/index.ts b/worker/src/models/index.ts index f576f983..78d119ba 100644 --- a/worker/src/models/index.ts +++ b/worker/src/models/index.ts @@ -99,11 +99,14 @@ export class UserSettings { enableMailAllowList: boolean | undefined; mailAllowList: string[] | undefined; maxAddressCount: number; + enableEmailCheckRegex: boolean | undefined; + emailCheckRegex: string | undefined; constructor(data: UserSettings | undefined | null) { const { enable, enableMailVerify, verifyMailSender, - enableMailAllowList, mailAllowList, maxAddressCount + enableMailAllowList, mailAllowList, maxAddressCount, + enableEmailCheckRegex, emailCheckRegex } = data || {}; this.enable = enable; this.enableMailVerify = enableMailVerify; @@ -111,6 +114,8 @@ export class UserSettings { this.enableMailAllowList = enableMailAllowList; this.mailAllowList = mailAllowList; this.maxAddressCount = maxAddressCount || 5; + this.enableEmailCheckRegex = enableEmailCheckRegex; + this.emailCheckRegex = emailCheckRegex; } } diff --git a/worker/src/user_api/user.ts b/worker/src/user_api/user.ts index dfb71f56..4e64be1a 100644 --- a/worker/src/user_api/user.ts +++ b/worker/src/user_api/user.ts @@ -27,6 +27,17 @@ export default { ) { return c.text(`${msgs.UserMailDomainMustInMsg} ${JSON.stringify(settings.mailAllowList, null, 2)}`, 400) } + // check email regex + if (settings.enableEmailCheckRegex && settings.emailCheckRegex) { + try { + const regex = new RegExp(settings.emailCheckRegex); + if (!regex.test(email)) { + return c.text(`${msgs.UserEmailNotMatchRegexMsg}: /${settings.emailCheckRegex}/`, 400) + } + } catch (e) { + console.error("Failed to check user email regex", e); + } + } if (!settings.verifyMailSender) { return c.text(msgs.VerifyMailSenderNotSetMsg, 400) } @@ -82,6 +93,17 @@ export default { ) { return c.text(`${msgs.UserMailDomainMustInMsg} ${JSON.stringify(settings.mailAllowList, null, 2)}`, 400) } + // check email regex + if (settings.enableEmailCheckRegex && settings.emailCheckRegex) { + try { + const regex = new RegExp(settings.emailCheckRegex); + if (!regex.test(email)) { + return c.text(`${msgs.UserEmailNotMatchRegexMsg}: /${settings.emailCheckRegex}/`, 400) + } + } catch (e) { + console.error("Failed to check user email regex", e); + } + } // check code if (settings.enableMailVerify) { const verifyCode = await c.env.KV.get(`temp-mail:${email}`)