diff --git a/mail-vue/src/components/email-scroll/index.vue b/mail-vue/src/components/email-scroll/index.vue
index 13fba5a..e84eb1d 100644
--- a/mail-vue/src/components/email-scroll/index.vue
+++ b/mail-vue/src/components/email-scroll/index.vue
@@ -12,7 +12,7 @@
-
diff --git a/mail-vue/src/i18n/en.js b/mail-vue/src/i18n/en.js
index a0b0b23..938a838 100644
--- a/mail-vue/src/i18n/en.js
+++ b/mail-vue/src/i18n/en.js
@@ -191,6 +191,7 @@ const en = {
delSuccessMsg: 'Deleted successfully',
emptyEmailMsg: 'Email cannot be empty',
notEmailMsg: 'Invalid email',
+ minEmailPrefix: 'Email must be at least {msg} characters',
emptyPwdMsg: 'Password cannot be empty',
pwdLengthMsg: 'Password must be at least 6 characters',
confirmPwdFailMsg: 'The two passwords do not match',
@@ -303,7 +304,10 @@ const en = {
show: 'Show',
hide: 'Hide',
onlyName: 'Only name',
- emailText: 'Email Text'
+ emailText: 'Email Text',
+ emailPrefix: 'Email Prefix',
+ atLeast: 'At Least',
+ character: ''
}
export default en
diff --git a/mail-vue/src/i18n/zh.js b/mail-vue/src/i18n/zh.js
index 14b7ff1..bf83140 100644
--- a/mail-vue/src/i18n/zh.js
+++ b/mail-vue/src/i18n/zh.js
@@ -193,6 +193,7 @@ const zh = {
notEmailMsg: '输入的邮箱不合法',
emptyPwdMsg: '密码不能为空',
pwdLengthMsg: '密码最少六位',
+ minEmailPrefix: '邮箱名不能小于{msg}位',
confirmPwdFailMsg: '两次密码输入不一致',
emptyRegKeyMsg: '注册码不能为空',
regSuccessMsg: '注册成功',
@@ -303,6 +304,9 @@ const zh = {
show: '显示',
hide: '隐藏',
onlyName: '仅名字',
- emailText: '邮件文本'
+ emailText: '邮件文本',
+ emailPrefix: '邮箱前缀',
+ atLeast: '至少',
+ character: '位'
}
export default zh
diff --git a/mail-vue/src/icons/index.js b/mail-vue/src/icons/index.js
index 1269eaa..b2cdeaf 100644
--- a/mail-vue/src/icons/index.js
+++ b/mail-vue/src/icons/index.js
@@ -646,3 +646,40 @@ addCollection({
}
}
})
+addCollection({
+ "prefix": "mingcute",
+ "lastModified": 1754900188,
+ "aliases": {},
+ "width": 24,
+ "height": 24,
+ "icons": {
+ "github-line": {
+ "body": ""
+ }
+ }
+})
+addCollection({
+ "prefix": "hugeicons",
+ "lastModified": 1759033396,
+ "aliases": {},
+ "width": 24,
+ "height": 24,
+ "icons": {
+ "view": {
+ "body": ""
+ }
+ }
+})
+addCollection({
+ "prefix": "bitcoin-icons",
+ "lastModified": 1754898649,
+ "aliases": {},
+ "width": 24,
+ "height": 24,
+ "icons": {
+ "refresh-filled": {
+ "body": ""
+ }
+ }
+})
+
diff --git a/mail-vue/src/layout/account/index.vue b/mail-vue/src/layout/account/index.vue
index 7c3288d..80e5517 100644
--- a/mail-vue/src/layout/account/index.vue
+++ b/mail-vue/src/layout/account/index.vue
@@ -377,6 +377,15 @@ function submit() {
return
}
+ if (addForm.email.length < settingStore.settings.minEmailPrefix) {
+ ElMessage({
+ message: t('minEmailPrefix', {msg: settingStore.settings.minEmailPrefix}),
+ type: 'error',
+ plain: true,
+ })
+ return
+ }
+
if (!isEmail(addForm.email + addForm.suffix)) {
ElMessage({
message: t('notEmailMsg'),
diff --git a/mail-vue/src/views/all-email/index.vue b/mail-vue/src/views/all-email/index.vue
index ea8c25e..cf3d9d8 100644
--- a/mail-vue/src/views/all-email/index.vue
+++ b/mail-vue/src/views/all-email/index.vue
@@ -397,7 +397,7 @@ function getEmailList(emailId, size) {
}
.clear {
- @media (max-width: 407px) {
+ @media (max-width: 409px) {
position: absolute;
top: 41px;
left: 242px;
@@ -405,10 +405,18 @@ function getEmailList(emailId, size) {
}
:deep(.reload) {
- @media (max-width: 407px) {
+ @media (max-width: 409px) {
position: absolute;
top: 42px;
left: 208px;
}
}
+
+:deep(.delete) {
+ @media (max-width: 443px) {
+ position: absolute;
+ top: 43px;
+ left: 284px;
+ }
+}
diff --git a/mail-vue/src/views/login/index.vue b/mail-vue/src/views/login/index.vue
index 53bffad..dc7d953 100644
--- a/mail-vue/src/views/login/index.vue
+++ b/mail-vue/src/views/login/index.vue
@@ -304,6 +304,16 @@ function bind() {
return
}
+
+ if (bindForm.email.length < settingStore.settings.minEmailPrefix) {
+ ElMessage({
+ message: t('minEmailPrefix', {msg: settingStore.settings.minEmailPrefix}),
+ type: 'error',
+ plain: true,
+ })
+ return
+ }
+
let email = bindForm.email + suffix.value;
@@ -407,6 +417,17 @@ function submitRegister() {
return
}
+ console.log(registerForm.email)
+
+ if (registerForm.email.length < settingStore.settings.minEmailPrefix) {
+ ElMessage({
+ message: t('minEmailPrefix', {msg: settingStore.settings.minEmailPrefix}),
+ type: 'error',
+ plain: true,
+ })
+ return
+ }
+
if (!isEmail(registerForm.email + suffix.value)) {
ElMessage({
message: t('notEmailMsg'),
diff --git a/mail-vue/src/views/sys-setting/index.vue b/mail-vue/src/views/sys-setting/index.vue
index 06ead2b..d990946 100644
--- a/mail-vue/src/views/sys-setting/index.vue
+++ b/mail-vue/src/views/sys-setting/index.vue
@@ -61,7 +61,16 @@
v-model="setting.manyEmail"/>
-
+
+
+ {{ $t('emailPrefix') }}
+
+
+
+
+
+
+
@@ -349,7 +358,7 @@
{{ $t('version') }} :
-
+
{{ currentVersion }}
@@ -704,6 +713,16 @@
+
+
+
{{ t('atLeast') }}
+
+
+ {{ t('character') }}
+
+
+
+
@@ -730,7 +749,7 @@ defineOptions({
name: 'sys-setting'
})
-const currentVersion = 'v2.3.0'
+const currentVersion = 'v2.4.0'
const hasUpdate = ref(false)
let getUpdateErrorCount = 1;
const {t, locale} = useI18n();
@@ -747,6 +766,7 @@ const tgSettingShow = ref(false)
const noticePopupShow = ref(false)
const thirdEmailShow = ref(false)
const forwardRulesShow = ref(false)
+const emailPrefixShow = ref(false)
const showResendList = ref(false)
const settingStore = useSettingStore();
const uiStore = useUiStore();
@@ -756,6 +776,7 @@ const settingLoading = ref(false)
const clearS3Loading = ref(false)
const r2DomainInput = ref('')
const loginOpacity = ref(0)
+const minEmailPrefix = ref(0)
const backgroundUrl = ref('')
let backgroundFile = {}
const showSetBackground = ref(false)
@@ -838,6 +859,7 @@ function getSettings() {
settingStore.domainList = settingData.domainList;
resendTokenForm.domain = setting.value.domainList[0]
loginOpacity.value = setting.value.loginOpacity
+ minEmailPrefix.value = setting.value.minEmailPrefix
firstLoading.value = false
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
editTitle.value = setting.value.title
@@ -897,7 +919,7 @@ const resendList = computed(() => {
function getUpdate() {
if (getUpdateErrorCount > 5 || !getUpdateErrorCount) return
- axios.get('https://api.github.com/repos/eoao/cloud-mail/releases/latest').then(({data}) => {
+ axios.get('https://api.github.com/repos/maillab/cloud-mail/releases/latest').then(({data}) => {
hasUpdate.value = data.name !== currentVersion
getUpdateErrorCount = 0
}).catch(e => {
@@ -993,6 +1015,10 @@ function openThirdEmailSetting() {
thirdEmailShow.value = true
}
+function openEmailPrefix() {
+ emailPrefixShow.value = true
+}
+
function openForwardRules() {
ruleType.value = setting.value.ruleType
ruleEmail.value = []
@@ -1111,6 +1137,17 @@ function doOpacityChange() {
editSetting(form, true)
}
+function doEmailPrefix() {
+ const form = {}
+ form.minEmailPrefix = minEmailPrefix.value
+ editSetting(form, true)
+}
+
+const EmailPrefixChange = debounce(doEmailPrefix, 1000, {
+ leading: false,
+ trailing: true
+})
+
const opacityChange = debounce(doOpacityChange, 1000, {
leading: false,
trailing: true
@@ -1279,6 +1316,7 @@ function editSetting(settingForm, refreshStatus = true) {
addS3Show.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
@@ -1350,13 +1388,13 @@ function editSetting(settingForm, refreshStatus = true) {
}
.background {
- width: 230px;
- height: 120px;
+ width: 249px;
+ height: 140px;
border-radius: 4px;
border: 1px solid var(--light-border);
@media (max-width: 500px) {
- width: 150px;
- height: 83px;
+ width: 160px;
+ height: 90px;
}
}
@@ -1632,6 +1670,11 @@ function editSetting(settingForm, refreshStatus = true) {
width: fit-content !important;
}
+.email-prefix {
+ display: flex;
+ justify-content: space-between;
+}
+
.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 fc4db1b..7219aab 100644
--- a/mail-worker/src/entity/setting.js
+++ b/mail-worker/src/entity/setting.js
@@ -45,6 +45,7 @@ export const setting = sqliteTable('setting', {
customDomain: text('custom_domain').default('').notNull(),
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()
+ tgMsgText: text('tg_msg_text').default('hide').notNull(),
+ minEmailPrefix: integer('min_email_prefix').default(0).notNull()
});
export default setting
diff --git a/mail-worker/src/i18n/en.js b/mail-worker/src/i18n/en.js
index b69a0e2..30a6f46 100644
--- a/mail-worker/src/i18n/en.js
+++ b/mail-worker/src/i18n/en.js
@@ -27,7 +27,8 @@ const en = {
notExistEmailReply: 'Mail does not exist and cannot be replied to',
pwdLengthLimit: 'Password length exceeds the limit',
emailLengthLimit: 'Email length exceeds the limit',
- pwdMinLengthLimit: 'Password must be at least 6 characters',
+ minEmailPrefix: 'Email must be at least {{msg}} characters',
+ pwdMinLength: 'Password must be at least 6 characters',
notEmailDomain: 'Invalid email domain',
emptyRegKey: 'Invite code cannot be empty',
notExistRegKey: 'Invite code does not exist',
diff --git a/mail-worker/src/i18n/i18n.js b/mail-worker/src/i18n/i18n.js
index 5fdfdf2..a90393e 100644
--- a/mail-worker/src/i18n/i18n.js
+++ b/mail-worker/src/i18n/i18n.js
@@ -25,6 +25,6 @@ i18next.init({
resources,
});
-export const t = (key) => i18next.t(key)
+export const t = (key, values) => i18next.t(key, values)
export default i18next;
diff --git a/mail-worker/src/i18n/zh.js b/mail-worker/src/i18n/zh.js
index ffda96f..a963aa9 100644
--- a/mail-worker/src/i18n/zh.js
+++ b/mail-worker/src/i18n/zh.js
@@ -27,7 +27,8 @@ const zh = {
notExistEmailReply: '邮件不存在无法回复',
pwdLengthLimit: '密码长度超出限制',
emailLengthLimit: '邮箱长度超出限制',
- pwdMinLengthLimit: '密码不能小于6位',
+ minEmailPrefix: '邮箱名不能小于{{msg}}位',
+ pwdMinLength: '密码不能小于6位',
notEmailDomain: '非法邮箱域名',
emptyRegKey: '注册码不能为空',
notExistRegKey: '注册码不存在',
diff --git a/mail-worker/src/init/init.js b/mail-worker/src/init/init.js
index 693a0c0..a70fb2c 100644
--- a/mail-worker/src/init/init.js
+++ b/mail-worker/src/init/init.js
@@ -29,20 +29,31 @@ const init = {
},
async v2_4DB(c) {
- await c.env.db.prepare(`
- CREATE TABLE IF NOT EXISTS oauth (
- oauth_id INTEGER PRIMARY KEY AUTOINCREMENT,
- oauth_user_id TEXT,
- username TEXT,
- name TEXT,
- avatar TEXT,
- active INTEGER,
- trust_level INTEGER,
- silenced INTEGER,
- create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
- user_id INTEGER NOT NULL DEFAULT 0
- )
- `).run();
+ try {
+ await c.env.db.prepare(`
+ CREATE TABLE IF NOT EXISTS oauth (
+ oauth_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ oauth_user_id TEXT,
+ username TEXT,
+ name TEXT,
+ avatar TEXT,
+ active INTEGER,
+ trust_level INTEGER,
+ silenced INTEGER,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+ platform INTEGER NOT NULL DEFAULT 0,
+ user_id INTEGER NOT NULL DEFAULT 0
+ )
+ `).run();
+ } catch (e) {
+ console.error(e)
+ }
+
+ try {
+ await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN min_email_prefix INTEGER NOT NULL DEFAULT 1;`).run();
+ } catch (e) {
+ console.error(e)
+ }
},
async v2_3DB(c) {
@@ -59,7 +70,7 @@ const init = {
}
try {
- await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN tg_msg_text TEXT NOT NULL DEFAULT 'hide';`).run();
+ await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN tg_msg_text TEXT NOT NULL DEFAULT 'show';`).run();
} catch (e) {
console.error(e)
}
diff --git a/mail-worker/src/service/account-service.js b/mail-worker/src/service/account-service.js
index 66fb50a..4e40ffd 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} = await settingService.query(c);
+ const {addEmailVerify , addEmail, manyEmail, addVerifyCount, minEmailPrefix} = await settingService.query(c);
let { email, token } = params;
@@ -39,6 +39,9 @@ const accountService = {
throw new BizError(t('notExistDomain'));
}
+ if (emailUtils.getName(email).length < minEmailPrefix) {
+ throw new BizError(t('minEmailPrefix', { msg: minEmailPrefix } ));
+ }
let accountRow = await this.selectByEmailIncludeDel(c, email);
diff --git a/mail-worker/src/service/login-service.js b/mail-worker/src/service/login-service.js
index acff013..6cc21d8 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} = await settingService.query(c)
+ let {regKey, register, registerVerify, regVerifyCount, minEmailPrefix} = await settingService.query(c)
if (oauth) {
registerVerify = settingConst.registerVerify.CLOSE;
@@ -41,16 +41,20 @@ const loginService = {
throw new BizError(t('notEmail'));
}
+ if (emailUtils.getName(email).length < minEmailPrefix) {
+ throw new BizError(t('minEmailPrefix', { msg: minEmailPrefix } ));
+ }
+
+ if (emailUtils.getName(email).length > 64) {
+ throw new BizError(t('emailLengthLimit'));
+ }
+
if (password.length > 30) {
throw new BizError(t('pwdLengthLimit'));
}
- if (emailUtils.getName(email).length > 30) {
- throw new BizError(t('emailLengthLimit'));
- }
-
if (password.length < 6) {
- throw new BizError(t('pwdMinLengthLimit'));
+ throw new BizError(t('pwdMinLength'));
}
if (!c.env.domain.includes(emailUtils.getDomain(email))) {
diff --git a/mail-worker/src/service/setting-service.js b/mail-worker/src/service/setting-service.js
index de38a18..3f70e46 100644
--- a/mail-worker/src/service/setting-service.js
+++ b/mail-worker/src/service/setting-service.js
@@ -206,7 +206,8 @@ const settingService = {
loginDomain: settingRow.loginDomain,
linuxdoClientId: settingRow.linuxdoClientId,
linuxdoCallbackUrl: settingRow.linuxdoCallbackUrl,
- linuxdoSwitch: settingRow.linuxdoSwitch
+ linuxdoSwitch: settingRow.linuxdoSwitch,
+ minEmailPrefix: settingRow.minEmailPrefix
};
}
};
diff --git a/mail-worker/src/service/telegram-service.js b/mail-worker/src/service/telegram-service.js
index 1b76f17..aaf5151 100644
--- a/mail-worker/src/service/telegram-service.js
+++ b/mail-worker/src/service/telegram-service.js
@@ -12,6 +12,7 @@ import emailMsgTemplate from '../template/email-msg';
import emailTextTemplate from '../template/email-text';
import emailHtmlTemplate from '../template/email-html';
import verifyUtils from '../utils/verify-utils';
+import domainUtils from "../utils/domain-uitls";
const telegramService = {
@@ -50,7 +51,7 @@ const telegramService = {
const jwtToken = await jwtUtils.generateToken(c, { emailId: email.emailId })
- const webAppUrl = verifyUtils.isDomain(customDomain) ? `https://${customDomain}/api/telegram/getEmail/${jwtToken}` : 'https://www.cloudflare.com/404'
+ const webAppUrl = customDomain ? `${domainUtils.toOssDomain(customDomain)}/api/telegram/getEmail/${jwtToken}` : 'https://www.cloudflare.com/404'
await Promise.all(tgChatIds.map(async chatId => {
try {
diff --git a/mail-worker/src/service/user-service.js b/mail-worker/src/service/user-service.js
index 129f4ac..2d7468b 100644
--- a/mail-worker/src/service/user-service.js
+++ b/mail-worker/src/service/user-service.js
@@ -53,7 +53,7 @@ const userService = {
const { password } = params;
if (password < 6) {
- throw new BizError(t('pwdMinLengthLimit'));
+ throw new BizError(t('pwdMinLength'));
}
const { salt, hash } = await cryptoUtils.hashPassword(password);
await orm(c).update(user).set({ password: hash, salt: salt }).where(eq(user.userId, userId)).run();
@@ -304,7 +304,7 @@ const userService = {
}
if (password.length < 6) {
- throw new BizError(t('pwdMinLengthLimit'));
+ throw new BizError(t('pwdMinLength'));
}
const accountRow = await accountService.selectByEmailIncludeDel(c, email);