diff --git a/CHANGELOG.md b/CHANGELOG.md index 155744e..62e9681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Features - feat: |Admin| 数据库页面新增 D1 存储容量展示,支持选择并保存 Free 或 Workers Paid 套餐,对比当前数据库大小和容量上限 +- feat: |Admin| 创建邮箱页面支持一键生成随机邮箱名称(issue #1126) - feat: |用户系统| 用户中心新增发送邮件、与收件箱一致的可按绑定地址过滤的发件箱,以及地址管理凭证弹框;提供使用用户 JWT 的地址设置、发信权限申请、发信及发件箱 API ### Bug Fixes diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 0d2eac3..5b0be5a 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -11,6 +11,7 @@ ### Features - feat: |Admin| Add D1 storage capacity details to the database page, with persistent Free and Workers Paid plan selection and a comparison between the current database size and capacity limit +- feat: |Admin| Add one-click random email-name generation to the address creation page (issue #1126) - feat: |User| Add mail composition, inbox-style sent-item filtering by bound address, and the shared address-credentials dialog to the user center, backed by User JWT APIs for address settings, send-access requests, sending, and sent-item management ### Bug Fixes diff --git a/frontend/src/i18n/locales/source/de.ts b/frontend/src/i18n/locales/source/de.ts index 4aeab92..d3ae204 100644 --- a/frontend/src/i18n/locales/source/de.ts +++ b/frontend/src/i18n/locales/source/de.ts @@ -402,6 +402,7 @@ export const deMessages = { "views.user.UserSettings.renamePasskeyNamePlaceholder": "Bitte den neuen Passkey-Namen eingeben", "views.user.UserSettings.passkeyNamePlaceholder": "Bitte den Passkey-Namen eingeben oder leer lassen, um einen zufälligen zu erzeugen", "views.admin.CreateAccount.fillInAllFields": "Bitte alle Felder ausfüllen", + "views.admin.CreateAccount.generateName": "Zufälligen Namen erzeugen", "views.admin.SendBox.queryTip": "Bitte die abzufragende Adresse eingeben; leer lassen für alle", "views.user.UserLogin.pleaseInputCode": "Bitte den Code eingeben", "views.admin.UserManagement.pleaseInput": "Bitte alle erforderlichen Informationen eingeben", diff --git a/frontend/src/i18n/locales/source/es.ts b/frontend/src/i18n/locales/source/es.ts index a91d553..e3a3982 100644 --- a/frontend/src/i18n/locales/source/es.ts +++ b/frontend/src/i18n/locales/source/es.ts @@ -402,6 +402,7 @@ export const esMessages = { "views.user.UserSettings.renamePasskeyNamePlaceholder": "Introduce el nuevo nombre de la passkey", "views.user.UserSettings.passkeyNamePlaceholder": "Introduce el nombre de la passkey o déjalo vacío para generarlo aleatoriamente", "views.admin.CreateAccount.fillInAllFields": "Rellena todos los campos", + "views.admin.CreateAccount.generateName": "Generar nombre aleatorio", "views.admin.SendBox.queryTip": "Introduce la dirección a consultar; vacío para todas", "views.user.UserLogin.pleaseInputCode": "Introduce el código", "views.admin.UserManagement.pleaseInput": "Introduce la información completa", diff --git a/frontend/src/i18n/locales/source/ja.ts b/frontend/src/i18n/locales/source/ja.ts index cb7042c..7633c37 100644 --- a/frontend/src/i18n/locales/source/ja.ts +++ b/frontend/src/i18n/locales/source/ja.ts @@ -402,6 +402,7 @@ export const jaMessages = { "views.user.UserSettings.renamePasskeyNamePlaceholder": "新しい Passkey 名を入力してください", "views.user.UserSettings.passkeyNamePlaceholder": "Passkey 名を入力するか、空欄でランダム生成してください", "views.admin.CreateAccount.fillInAllFields": "すべての項目を入力してください", + "views.admin.CreateAccount.generateName": "ランダム名を生成", "views.admin.SendBox.queryTip": "検索するアドレスを入力してください。空欄で全件検索します", "views.user.UserLogin.pleaseInputCode": "コードを入力してください", "views.admin.UserManagement.pleaseInput": "必要な情報をすべて入力してください", diff --git a/frontend/src/i18n/locales/source/ptBR.ts b/frontend/src/i18n/locales/source/ptBR.ts index 424d281..0ca8591 100644 --- a/frontend/src/i18n/locales/source/ptBR.ts +++ b/frontend/src/i18n/locales/source/ptBR.ts @@ -402,6 +402,7 @@ export const ptBRMessages = { "views.user.UserSettings.renamePasskeyNamePlaceholder": "Informe o novo nome da passkey", "views.user.UserSettings.passkeyNamePlaceholder": "Informe o nome da passkey ou deixe em branco para gerar um aleatório", "views.admin.CreateAccount.fillInAllFields": "Preencha todos os campos", + "views.admin.CreateAccount.generateName": "Gerar nome aleatório", "views.admin.SendBox.queryTip": "Informe o endereço para consulta; deixe em branco para consultar todos", "views.user.UserLogin.pleaseInputCode": "Informe o código", "views.admin.UserManagement.pleaseInput": "Informe todas as informações", diff --git a/frontend/src/i18n/message-registry.ts b/frontend/src/i18n/message-registry.ts index 2668416..5b90fd8 100644 --- a/frontend/src/i18n/message-registry.ts +++ b/frontend/src/i18n/message-registry.ts @@ -2146,6 +2146,10 @@ export const MESSAGE_REGISTRY = { "en": "Please fill in all fields", "zh": "请填写完整信息" }, + "generateName": { + "en": "Generate Fake Name", + "zh": "生成随机名字" + }, "linkWithAddressCredential": { "en": "Open to auto login email link", "zh": "打开即可自动登录邮箱的链接" diff --git a/frontend/src/views/admin/CreateAccount.vue b/frontend/src/views/admin/CreateAccount.vue index fd00d3d..37d165c 100644 --- a/frontend/src/views/admin/CreateAccount.vue +++ b/frontend/src/views/admin/CreateAccount.vue @@ -23,6 +23,40 @@ const result = ref("") const addressPassword = ref("") const createdAddress = ref("") +const addressRegex = computed(() => { + try { + if (openSettings.value.addressRegex) { + return new RegExp(openSettings.value.addressRegex, 'g'); + } + } catch (error) { + console.error(error); + message.error(`Invalid addressRegex: ${openSettings.value.addressRegex}`); + } + return /[^a-z0-9]/g; +}); + +const generateNameLoading = ref(false); +const generateName = async () => { + try { + generateNameLoading.value = true; + const { faker } = await import('https://esm.sh/@faker-js/faker'); + emailName.value = faker.internet.email() + .split('@')[0] + .replace(/\s+/g, '.') + .replace(/\.{2,}/g, '.') + .replace(addressRegex.value, '') + .toLowerCase(); + // support maxAddressLen + if (emailName.value.length > openSettings.value.maxAddressLen) { + emailName.value = emailName.value.slice(0, openSettings.value.maxAddressLen); + } + } catch (error) { + message.error(error.message || "error"); + } finally { + generateNameLoading.value = false; + } +}; + const canUseRandomSubdomain = computed(() => { if (!emailDomain.value) { return false @@ -81,15 +115,22 @@ onMounted(async () => { - - - {{ openSettings.prefix }} - - - @ - - + + + + {{ t('generateName') }} + + + + {{ openSettings.prefix }} + + + @ + + + +