mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-22 00:29:53 +08:00
feat: enhance webhook security with configurable allow list (#719)
- Add enableAllowList flag to webhook settings for flexible access control - Update frontend UI with toggle switch and improved user experience - Maintain backward compatibility with default allow-all behavior - Add input validation hints and better form controls across admin panels 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ const { t } = useI18n({
|
||||
messages: {
|
||||
en: {
|
||||
tip: 'You can manually input the following multiple select input and enter',
|
||||
manualInputPrompt: 'Type and press Enter to add',
|
||||
save: 'Save',
|
||||
successTip: 'Save Success',
|
||||
address_block_list: 'Address Block Keywords for Users(Admin can skip)',
|
||||
@@ -38,6 +39,7 @@ const { t } = useI18n({
|
||||
},
|
||||
zh: {
|
||||
tip: '您可以手动输入以下多选输入框, 回车增加',
|
||||
manualInputPrompt: '输入后按回车键添加',
|
||||
save: '保存',
|
||||
successTip: '保存成功',
|
||||
address_block_list: '邮件地址屏蔽关键词(管理员可跳过检查)',
|
||||
@@ -209,25 +211,55 @@ onMounted(async () => {
|
||||
</n-flex>
|
||||
<n-form-item-row :label="t('address_block_list')">
|
||||
<n-select v-model:value="addressBlockList" filterable multiple tag
|
||||
:placeholder="t('address_block_list_placeholder')" />
|
||||
:placeholder="t('address_block_list_placeholder')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('send_address_block_list')">
|
||||
<n-select v-model:value="sendAddressBlockList" filterable multiple tag
|
||||
:placeholder="t('address_block_list_placeholder')" />
|
||||
:placeholder="t('address_block_list_placeholder')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('noLimitSendAddressList')">
|
||||
<n-select v-model:value="noLimitSendAddressList" filterable multiple tag
|
||||
:placeholder="t('noLimitSendAddressList')" />
|
||||
:placeholder="t('noLimitSendAddressList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('verified_address_list')">
|
||||
<n-select v-model:value="verifiedAddressList" filterable multiple tag
|
||||
:placeholder="t('verified_address_list')" />
|
||||
:placeholder="t('verified_address_list')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('fromBlockList')">
|
||||
<n-select v-model:value="fromBlockList" filterable multiple tag :placeholder="t('fromBlockList')" />
|
||||
<n-select v-model:value="fromBlockList" filterable multiple tag :placeholder="t('fromBlockList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('block_receive_unknow_address_email')">
|
||||
<n-checkbox v-model:checked="emailRuleSettings.blockReceiveUnknowAddressEmail" />
|
||||
<n-switch v-model:value="emailRuleSettings.blockReceiveUnknowAddressEmail" :round="false" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('email_forwarding_config')">
|
||||
<n-button @click="openEmailForwardingModal">{{ t('config') }}</n-button>
|
||||
|
||||
@@ -77,7 +77,7 @@ onMounted(async () => {
|
||||
</n-modal>
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-form-item-row v-if="openSettings.prefix" :label="t('enablePrefix')">
|
||||
<n-checkbox v-model:checked="enablePrefix" />
|
||||
<n-switch v-model:value="enablePrefix" :round="false" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('address')">
|
||||
<n-input-group>
|
||||
|
||||
@@ -15,25 +15,29 @@ const { t } = useI18n({
|
||||
init: 'Init',
|
||||
successTip: 'Success',
|
||||
status: 'Check Status',
|
||||
enableTelegramAllowList: 'Enable Telegram Allow List(Manually input user ID)',
|
||||
enableTelegramAllowList: 'Enable Telegram Allow List(Manually input Chat ID)',
|
||||
enable: 'Enable',
|
||||
telegramAllowList: 'Telegram Allow List(Manually input telegram user ID)',
|
||||
telegramAllowList: 'Telegram Allow List(Manually input telegram Chat ID)',
|
||||
manualInputPrompt: 'Type and press Enter to add',
|
||||
save: 'Save',
|
||||
miniAppUrl: 'Telegram Mini App URL',
|
||||
enableGlobalMailPush: 'Enable Global Mail Push(Manually input telegram user ID)',
|
||||
globalMailPushList: 'Global Mail Push List',
|
||||
enableGlobalMailPush: 'Enable Global Mail Push(Manually input telegram Chat ID)',
|
||||
globalMailPushList: 'Global Mail Push Chat ID List',
|
||||
globalMailPushListTip: 'Support chat_id of private chat/group/channel. You can send a message to your bot, then visit this link to see chat_id, https://api.telegram.org/bot<Replace with your BOT TOKEN>/getUpdates',
|
||||
},
|
||||
zh: {
|
||||
init: '初始化',
|
||||
successTip: '成功',
|
||||
status: '查看状态',
|
||||
enableTelegramAllowList: '启用 Telegram 白名单(手动输入用户 ID, 回车增加)',
|
||||
enableTelegramAllowList: '启用 Telegram 白名单(手动输入 Chat ID, 回车增加)',
|
||||
enable: '启用',
|
||||
telegramAllowList: 'Telegram 白名单(手动输入用户 ID, 回车增加)',
|
||||
telegramAllowList: 'Telegram 白名单(手动输入 Chat ID, 回车增加)',
|
||||
manualInputPrompt: '输入后按回车键添加',
|
||||
save: '保存',
|
||||
miniAppUrl: '电报小程序 URL(请输入你部署的电报小程序网页地址)',
|
||||
enableGlobalMailPush: '启用全局邮件推送(手动输入邮箱管理员的 telegram 用户 ID, 回车增加)',
|
||||
globalMailPushList: '全局邮件推送用户列表',
|
||||
enableGlobalMailPush: '启用全局邮件推送(手动输入邮箱管理员的 telegram Chat ID, 回车增加)',
|
||||
globalMailPushList: '全局邮件推送 Chat ID 列表',
|
||||
globalMailPushListTip: '支持对话/群组/频道的 Chat ID, 您可以发送一条消息给您的机器人,然后访问此链接来查看 chat_id, https://api.telegram.org/bot<这里替换成您的 BOT TOKEN>/getUpdates',
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -113,6 +117,17 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card :bordered="false" embedded style="max-width: 800px; overflow: auto;">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="fetchStatus" secondary>
|
||||
{{ t('status') }}
|
||||
</n-button>
|
||||
<n-button @click="init" type="primary">
|
||||
{{ t('init') }}
|
||||
</n-button>
|
||||
<n-button @click="saveSettings" type="primary">
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<n-card :bordered="false" embedded>
|
||||
<n-form-item-row :label="t('enableTelegramAllowList')">
|
||||
<n-input-group>
|
||||
@@ -120,31 +135,41 @@ onMounted(async () => {
|
||||
{{ t('enable') }}
|
||||
</n-checkbox>
|
||||
<n-select v-model:value="settings.allowList" filterable multiple tag style="width: 80%;"
|
||||
:placeholder="t('telegramAllowList')" />
|
||||
:placeholder="t('telegramAllowList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-input-group>
|
||||
</n-form-item-row>
|
||||
<br />
|
||||
<n-form-item-row :label="t('enableGlobalMailPush')">
|
||||
<n-input-group>
|
||||
<n-checkbox v-model:checked="settings.enableGlobalMailPush" style="width: 20%;">
|
||||
{{ t('enable') }}
|
||||
</n-checkbox>
|
||||
<n-select v-model:value="settings.globalMailPushList" filterable multiple tag
|
||||
style="width: 80%;" :placeholder="t('globalMailPushList')" />
|
||||
style="width: 80%;" :placeholder="t('globalMailPushList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-input-group>
|
||||
<template #feedback>
|
||||
<n-text depth="3">
|
||||
{{ t('globalMailPushListTip') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-form-item-row>
|
||||
<br />
|
||||
<n-form-item-row :label="t('miniAppUrl')">
|
||||
<n-input v-model:value="settings.miniAppUrl"></n-input>
|
||||
</n-form-item-row>
|
||||
<n-button @click="saveSettings" type="primary" block>
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-card>
|
||||
<n-button @click="init" type="primary" block>
|
||||
{{ t('init') }}
|
||||
</n-button>
|
||||
<n-button @click="fetchStatus" secondary block>
|
||||
{{ t('status') }}
|
||||
</n-button>
|
||||
<pre v-if="status.fetched">{{ JSON.stringify(status, null, 2) }}</pre>
|
||||
</n-card>
|
||||
</div>
|
||||
@@ -157,8 +182,4 @@ onMounted(async () => {
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.n-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,6 +21,7 @@ const { t } = useI18n({
|
||||
successTip: 'Save Success',
|
||||
enable: 'Enable',
|
||||
enableMailAllowList: 'Enable Mail Address Allow List(Manually enterable)',
|
||||
manualInputPrompt: 'Type and press Enter to add',
|
||||
mailAllowList: 'Mail Address Allow List',
|
||||
addOauth2: 'Add Oauth2',
|
||||
name: 'Name',
|
||||
@@ -33,6 +34,7 @@ const { t } = useI18n({
|
||||
successTip: '保存成功',
|
||||
enable: '启用',
|
||||
enableMailAllowList: '启用邮件地址白名单(可手动输入, 回车增加)',
|
||||
manualInputPrompt: '输入后按回车键添加',
|
||||
mailAllowList: '邮件地址白名单',
|
||||
addOauth2: '添加 Oauth2',
|
||||
name: '名称',
|
||||
@@ -184,7 +186,7 @@ onMounted(async () => {
|
||||
</template>
|
||||
</n-modal>
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-alert :show-icon="false" type="warning" closable style="margin-bottom: 10px;">
|
||||
<n-alert :show-icon="false" :bordered="false" type="warning" closable style="margin-bottom: 10px;">
|
||||
{{ t("tip") }}
|
||||
</n-alert>
|
||||
<n-flex justify="end">
|
||||
@@ -246,7 +248,13 @@ onMounted(async () => {
|
||||
</n-checkbox>
|
||||
<n-select v-model:value="item.mailAllowList" v-if="item.enableMailAllowList" filterable
|
||||
multiple tag style="width: 80%;" :options="mailAllowOptions"
|
||||
:placeholder="t('mailAllowList')" />
|
||||
:placeholder="t('mailAllowList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-input-group>
|
||||
</n-form-item-row>
|
||||
</n-form>
|
||||
|
||||
@@ -18,6 +18,7 @@ const { t } = useI18n({
|
||||
enableMailVerify: 'Enable Mail Verify (Send address must be an address in the system with a balance and can send mail normally)',
|
||||
verifyMailSender: 'Verify Mail Sender',
|
||||
enableMailAllowList: 'Enable Mail Address Allow List(Manually enterable)',
|
||||
manualInputPrompt: 'Type and press Enter to add',
|
||||
mailAllowList: 'Mail Address Allow List',
|
||||
maxAddressCount: 'Maximum number of email addresses that can be binded',
|
||||
},
|
||||
@@ -29,6 +30,7 @@ const { t } = useI18n({
|
||||
enableMailVerify: '启用邮件验证(发送地址必须是系统中能有余额且能正常发送邮件的地址)',
|
||||
verifyMailSender: '验证邮件发送地址',
|
||||
enableMailAllowList: '启用邮件地址白名单(可手动输入, 回车增加)',
|
||||
manualInputPrompt: '输入后按回车键添加',
|
||||
mailAllowList: '邮件地址白名单',
|
||||
maxAddressCount: '可绑定最大邮箱地址数量',
|
||||
}
|
||||
@@ -83,9 +85,14 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px;">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="save" type="primary" :loading="loading">
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<n-form :model="userSettings">
|
||||
<n-form-item-row :label="t('enableUserRegister')">
|
||||
<n-checkbox v-model:checked="userSettings.enable" />
|
||||
<n-switch v-model:value="userSettings.enable" :round="false" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('enableMailVerify')">
|
||||
<n-input-group>
|
||||
@@ -103,7 +110,13 @@ onMounted(async () => {
|
||||
</n-checkbox>
|
||||
<n-select v-model:value="userSettings.mailAllowList" v-if="userSettings.enableMailAllowList"
|
||||
filterable multiple tag style="width: 80%;" :options="mailAllowOptions"
|
||||
:placeholder="t('mailAllowList')" />
|
||||
:placeholder="t('mailAllowList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-input-group>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('maxAddressCount')">
|
||||
@@ -112,9 +125,6 @@ onMounted(async () => {
|
||||
:placeholder="t('maxAddressCount')" />
|
||||
</n-input-group>
|
||||
</n-form-item-row>
|
||||
<n-button @click="save" type="primary" block :loading="loading">
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
@@ -13,13 +13,17 @@ const { t } = useI18n({
|
||||
messages: {
|
||||
en: {
|
||||
successTip: 'Success',
|
||||
webhookAllowList: 'Webhook Allow List(Enter the address that is allowed to use webhook and enter)',
|
||||
enableAllowList: 'Enable Allow List (Restrict webhook access to specific users)',
|
||||
webhookAllowList: 'Webhook Allow List(Enter the mail address that is allowed to use webhook and enter)',
|
||||
manualInputPrompt: 'Type and press Enter to add',
|
||||
save: 'Save',
|
||||
notEnabled: 'Webhook is not enabled',
|
||||
},
|
||||
zh: {
|
||||
successTip: '成功',
|
||||
webhookAllowList: 'Webhook 白名单(请输入允许使用webhook 的地址, 回车增加)',
|
||||
enableAllowList: '启用白名单 (限制 webhook 访问权限,只有白名单中的用户可以使用)',
|
||||
webhookAllowList: 'Webhook 白名单(请输入允许使用webhook 的邮箱地址, 回车增加)',
|
||||
manualInputPrompt: '输入后按回车键添加',
|
||||
save: '保存',
|
||||
notEnabled: 'Webhook 未开启',
|
||||
}
|
||||
@@ -27,14 +31,16 @@ const { t } = useI18n({
|
||||
});
|
||||
|
||||
class WebhookSettings {
|
||||
enableAllowList: boolean;
|
||||
allowList: string[];
|
||||
|
||||
constructor(allowList: string[]) {
|
||||
constructor(enableAllowList: boolean, allowList: string[]) {
|
||||
this.enableAllowList = enableAllowList;
|
||||
this.allowList = allowList;
|
||||
}
|
||||
}
|
||||
|
||||
const webhookSettings = ref(new WebhookSettings([]))
|
||||
const webhookSettings = ref(new WebhookSettings(false, []))
|
||||
const webhookEnabled = ref(false)
|
||||
const errorInfo = ref('')
|
||||
|
||||
@@ -68,13 +74,24 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card v-if="webhookEnabled" :bordered="false" embedded style="max-width: 800px; overflow: auto;">
|
||||
<n-flex justify="end">
|
||||
<n-button @click="saveSettings" type="primary">
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<n-form-item-row :label="t('enableAllowList')">
|
||||
<n-switch v-model:value="webhookSettings.enableAllowList" :round="false" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('webhookAllowList')">
|
||||
<n-select v-model:value="webhookSettings.allowList" filterable multiple tag
|
||||
:placeholder="t('webhookAllowList')" />
|
||||
:placeholder="t('webhookAllowList')">
|
||||
<template #empty>
|
||||
<n-text depth="3">
|
||||
{{ t('manualInputPrompt') }}
|
||||
</n-text>
|
||||
</template>
|
||||
</n-select>
|
||||
</n-form-item-row>
|
||||
<n-button @click="saveSettings" type="primary" block>
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</n-card>
|
||||
<n-result v-else status="404" :title="t('notEnabled')" :description="errorInfo" />
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card :bordered="false" embedded style="max-width: 600px; overflow: auto;">
|
||||
<n-card :bordered="false" embedded style="max-width: 800px; overflow: auto;">
|
||||
<pre>{{ JSON.stringify(settings, null, 2) }}</pre>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user