From aa86b2dc38d6dda2bfe9c47f91fece3b5818e794 Mon Sep 17 00:00:00 2001 From: eoao Date: Sun, 16 Nov 2025 12:52:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B3=A8=E5=86=8C=E9=82=AE?= =?UTF-8?q?=E7=AE=B1=E5=90=8D=E8=87=AA=E5=AE=9A=E4=B9=89=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mail-vue/src/i18n/en.js | 6 ++-- mail-vue/src/i18n/zh.js | 12 ++++---- mail-vue/src/layout/aside/index.vue | 11 ++++++- mail-vue/src/router/index.js | 2 +- mail-vue/src/views/sys-setting/index.vue | 35 +++++++++++++++------- mail-worker/src/entity/setting.js | 3 +- mail-worker/src/i18n/en.js | 1 + mail-worker/src/i18n/zh.js | 5 ++-- mail-worker/src/init/init.js | 10 +++++++ mail-worker/src/service/account-service.js | 6 +++- mail-worker/src/service/login-service.js | 6 +++- mail-worker/src/service/setting-service.js | 7 +++++ 12 files changed, 79 insertions(+), 25 deletions(-) diff --git a/mail-vue/src/i18n/en.js b/mail-vue/src/i18n/en.js index 938a838..7e743b0 100644 --- a/mail-vue/src/i18n/en.js +++ b/mail-vue/src/i18n/en.js @@ -1,5 +1,5 @@ const en = { - '收件箱': 'Inbox', + inbox: 'Inbox', drafts: 'Drafts', sent: 'Sent', starred: 'Starred', @@ -307,7 +307,9 @@ const en = { emailText: 'Email Text', emailPrefix: 'Email Prefix', atLeast: 'At Least', - character: '' + character: '', + mustNotContain: 'Must Not Contain', + mustNotContainDesc: 'Separate with commas' } export default en diff --git a/mail-vue/src/i18n/zh.js b/mail-vue/src/i18n/zh.js index bf83140..2d7fe30 100644 --- a/mail-vue/src/i18n/zh.js +++ b/mail-vue/src/i18n/zh.js @@ -1,5 +1,5 @@ const zh = { - '收件箱': '收件箱', + inbox: '收件箱', drafts: '草稿箱', sent: '已发送', starred: '星标邮件', @@ -192,8 +192,8 @@ const zh = { emptyEmailMsg: '邮箱不能为空', notEmailMsg: '输入的邮箱不合法', emptyPwdMsg: '密码不能为空', - pwdLengthMsg: '密码最少六位', - minEmailPrefix: '邮箱名不能小于{msg}位', + pwdLengthMsg: '密码至少六位', + minEmailPrefix: '邮箱名至少{msg}位', confirmPwdFailMsg: '两次密码输入不一致', emptyRegKeyMsg: '注册码不能为空', regSuccessMsg: '注册成功', @@ -228,7 +228,7 @@ const zh = { banRestore: '确认禁用 {msg} 吗?', logOut: '退出', clearContentConfirm: '确定要清空所有内容吗?', - attLimitMsg: '附件大小限制28mb', + attLimitMsg: '附件不能超过28MB', emptyRecipientMsg: '收件人邮箱地址不能为空', emptySubjectMsg: '主题不能为空', emptyContentMsg: '邮件正文不能为空', @@ -307,6 +307,8 @@ const zh = { emailText: '邮件文本', emailPrefix: '邮箱前缀', atLeast: '至少', - character: '位' + character: '位', + mustNotContain: '禁止包含', + mustNotContainDesc: '输入多个值用,分开' } export default zh diff --git a/mail-vue/src/layout/aside/index.vue b/mail-vue/src/layout/aside/index.vue index a230076..38ed8f6 100644 --- a/mail-vue/src/layout/aside/index.vue +++ b/mail-vue/src/layout/aside/index.vue @@ -9,7 +9,7 @@ - {{$t('收件箱')}} + {{$t('inbox')}} @@ -96,8 +96,17 @@ const route = useRoute(); color: #ffffff; background: linear-gradient(135deg, #1890ff, #3a80dd); transition: all 0.3s ease; + max-width: 240px; + padding: 0 10px; + > div { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: calc(240px - 20px - 30px); + } :deep(.el-icon) { + flex-shrink: 0; font-size: 20px; } diff --git a/mail-vue/src/router/index.js b/mail-vue/src/router/index.js index 4d75905..c090a9c 100644 --- a/mail-vue/src/router/index.js +++ b/mail-vue/src/router/index.js @@ -16,7 +16,7 @@ const routes = [ name: 'email', component: () => import('@/views/email/index.vue'), meta: { - title: '收件箱', + title: 'inbox', name: 'email', menu: true } diff --git a/mail-vue/src/views/sys-setting/index.vue b/mail-vue/src/views/sys-setting/index.vue index d990946..5fff62b 100644 --- a/mail-vue/src/views/sys-setting/index.vue +++ b/mail-vue/src/views/sys-setting/index.vue @@ -713,15 +713,20 @@ - + +
+
{{ t('mustNotContain') }}
+ +
+ {{ $t('save') }}
@@ -777,6 +782,7 @@ const clearS3Loading = ref(false) const r2DomainInput = ref('') const loginOpacity = ref(0) const minEmailPrefix = ref(0) +const emailPrefixFilter = ref([]) const backgroundUrl = ref('') let backgroundFile = {} const showSetBackground = ref(false) @@ -868,6 +874,7 @@ function getSettings() { regVerifyCount.value = setting.value.regVerifyCount resetNoticeForm() resetAddS3Form() + resetEmailPrefix() }) } @@ -1137,16 +1144,17 @@ function doOpacityChange() { editSetting(form, true) } -function doEmailPrefix() { - const form = {} - form.minEmailPrefix = minEmailPrefix.value - editSetting(form, true) +function resetEmailPrefix() { + minEmailPrefix.value = setting.value.minEmailPrefix + emailPrefixFilter.value = setting.value.emailPrefixFilter } -const EmailPrefixChange = debounce(doEmailPrefix, 1000, { - leading: false, - trailing: true -}) +function saveEmailPrefix() { + const form = {} + form.minEmailPrefix = minEmailPrefix.value + form.emailPrefixFilter = emailPrefixFilter.value + editSetting(form, true) +} const opacityChange = debounce(doOpacityChange, 1000, { leading: false, @@ -1314,9 +1322,9 @@ function editSetting(settingForm, refreshStatus = true) { regVerifyCountShow.value = false noticePopupShow.value = false addS3Show.value = false + emailPrefixShow.value = false }).catch((e) => { loginOpacity.value = setting.value.loginOpacity - minEmailPrefix.value = setting.value.minEmailPrefix setting.value = {...setting.value, ...JSON.parse(backup)} }).finally(() => { settingLoading.value = false @@ -1675,6 +1683,11 @@ function editSetting(settingForm, refreshStatus = true) { justify-content: space-between; } +.prefix-filter { + display: flex; + flex-direction: column; +} + .s3-button { display: grid; grid-template-columns: 80px 1fr; diff --git a/mail-worker/src/entity/setting.js b/mail-worker/src/entity/setting.js index 7219aab..4ff0eae 100644 --- a/mail-worker/src/entity/setting.js +++ b/mail-worker/src/entity/setting.js @@ -46,6 +46,7 @@ export const setting = sqliteTable('setting', { tgMsgFrom: text('tg_msg_from').default('only-name').notNull(), tgMsgTo: text('tg_msg_to').default('show').notNull(), tgMsgText: text('tg_msg_text').default('hide').notNull(), - minEmailPrefix: integer('min_email_prefix').default(0).notNull() + minEmailPrefix: integer('min_email_prefix').default(0).notNull(), + emailPrefixFilter: text('email_prefix_filter').default('').notNull() }); export default setting diff --git a/mail-worker/src/i18n/en.js b/mail-worker/src/i18n/en.js index 30a6f46..7b1192b 100644 --- a/mail-worker/src/i18n/en.js +++ b/mail-worker/src/i18n/en.js @@ -28,6 +28,7 @@ const en = { pwdLengthLimit: 'Password length exceeds the limit', emailLengthLimit: 'Email length exceeds the limit', minEmailPrefix: 'Email must be at least {{msg}} characters', + banEmailPrefix: 'Invalid characters in email address', pwdMinLength: 'Password must be at least 6 characters', notEmailDomain: 'Invalid email domain', emptyRegKey: 'Invite code cannot be empty', diff --git a/mail-worker/src/i18n/zh.js b/mail-worker/src/i18n/zh.js index a963aa9..ba928cd 100644 --- a/mail-worker/src/i18n/zh.js +++ b/mail-worker/src/i18n/zh.js @@ -27,8 +27,9 @@ const zh = { notExistEmailReply: '邮件不存在无法回复', pwdLengthLimit: '密码长度超出限制', emailLengthLimit: '邮箱长度超出限制', - minEmailPrefix: '邮箱名不能小于{{msg}}位', - pwdMinLength: '密码不能小于6位', + minEmailPrefix: '邮箱名至少{{msg}}位', + banEmailPrefix: '邮箱名包含非法字符', + pwdMinLength: '密码至少六位', notEmailDomain: '非法邮箱域名', emptyRegKey: '注册码不能为空', notExistRegKey: '注册码不存在', diff --git a/mail-worker/src/init/init.js b/mail-worker/src/init/init.js index a70fb2c..ebb398d 100644 --- a/mail-worker/src/init/init.js +++ b/mail-worker/src/init/init.js @@ -24,10 +24,19 @@ const init = { await this.v2DB(c); await this.v2_3DB(c); await this.v2_4DB(c); + await this.v2_5DB(c); await settingService.refresh(c); return c.text(t('initSuccess')); }, + async v2_5DB(c) { + try { + await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN email_prefix_filter text NOT NULL DEFAULT '';`).run(); + } catch (e) { + console.error(e) + } + }, + async v2_4DB(c) { try { await c.env.db.prepare(` @@ -54,6 +63,7 @@ const init = { } catch (e) { console.error(e) } + }, async v2_3DB(c) { diff --git a/mail-worker/src/service/account-service.js b/mail-worker/src/service/account-service.js index 4e40ffd..76d347b 100644 --- a/mail-worker/src/service/account-service.js +++ b/mail-worker/src/service/account-service.js @@ -17,7 +17,7 @@ const accountService = { async add(c, params, userId) { - const {addEmailVerify , addEmail, manyEmail, addVerifyCount, minEmailPrefix} = await settingService.query(c); + const { addEmailVerify , addEmail, manyEmail, addVerifyCount, minEmailPrefix, emailPrefixFilter } = await settingService.query(c); let { email, token } = params; @@ -43,6 +43,10 @@ const accountService = { throw new BizError(t('minEmailPrefix', { msg: minEmailPrefix } )); } + if (emailPrefixFilter.some(content => emailUtils.getName(email).includes(content))) { + throw new BizError(t('banEmailPrefix')); + } + let accountRow = await this.selectByEmailIncludeDel(c, email); if (accountRow && accountRow.isDel === isDel.DELETE) { diff --git a/mail-worker/src/service/login-service.js b/mail-worker/src/service/login-service.js index 6cc21d8..be7f8be 100644 --- a/mail-worker/src/service/login-service.js +++ b/mail-worker/src/service/login-service.js @@ -26,7 +26,7 @@ const loginService = { const { email, password, token, code } = params; - let {regKey, register, registerVerify, regVerifyCount, minEmailPrefix} = await settingService.query(c) + let { regKey, register, registerVerify, regVerifyCount, minEmailPrefix, emailPrefixFilter } = await settingService.query(c) if (oauth) { registerVerify = settingConst.registerVerify.CLOSE; @@ -45,6 +45,10 @@ const loginService = { throw new BizError(t('minEmailPrefix', { msg: minEmailPrefix } )); } + if (emailPrefixFilter.some(content => emailUtils.getName(email).includes(content))) { + throw new BizError(t('banEmailPrefix')); + } + if (emailUtils.getName(email).length > 64) { throw new BizError(t('emailLengthLimit')); } diff --git a/mail-worker/src/service/setting-service.js b/mail-worker/src/service/setting-service.js index 3f70e46..76dbca6 100644 --- a/mail-worker/src/service/setting-service.js +++ b/mail-worker/src/service/setting-service.js @@ -58,6 +58,8 @@ const settingService = { setting.linuxdoCallbackUrl = c.env.linuxdo_callback_url; setting.linuxdoSwitch = linuxdoSwitch; + setting.emailPrefixFilter = setting.emailPrefixFilter.split(",").filter(Boolean); + c.set?.('setting', setting); return setting; }, @@ -108,6 +110,11 @@ const settingService = { Object.keys(resendTokens).forEach(domain => { if (!resendTokens[domain]) delete resendTokens[domain]; }); + + if (Array.isArray(params.emailPrefixFilter)) { + params.emailPrefixFilter = params.emailPrefixFilter + ''; + } + params.resendTokens = JSON.stringify(resendTokens); await orm(c).update(setting).set({ ...params }).returning().get(); await this.refresh(c);