mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-05 15:38:34 +08:00
feat: add Local Address Manage (#285)
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
<!-- markdownlint-disable-file MD004 MD024 MD034 MD036 -->
|
<!-- markdownlint-disable-file MD004 MD024 MD034 MD036 -->
|
||||||
# CHANGE LOG
|
# CHANGE LOG
|
||||||
|
|
||||||
|
## main branch
|
||||||
|
|
||||||
|
- UI: 增加本地缓存进行地址管理
|
||||||
|
|
||||||
## v0.4.6
|
## v0.4.6
|
||||||
|
|
||||||
- worker 配置文件添加 `TITLE = "Custom Title"`, 可自定义网站标题
|
- worker 配置文件添加 `TITLE = "Custom Title"`, 可自定义网站标题
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "0.4.6",
|
"version": "0.4.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { api } from '../../api'
|
|||||||
import Login from '../common/Login.vue'
|
import Login from '../common/Login.vue'
|
||||||
import AddressManagement from '../user/AddressManagement.vue'
|
import AddressManagement from '../user/AddressManagement.vue'
|
||||||
import TelegramAddress from './TelegramAddress.vue'
|
import TelegramAddress from './TelegramAddress.vue'
|
||||||
|
import LocalAddress from './LocalAddress.vue'
|
||||||
|
|
||||||
const { toClipboard } = useClipboard()
|
const { toClipboard } = useClipboard()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
@@ -50,6 +51,7 @@ const { t } = useI18n({
|
|||||||
|
|
||||||
const showChangeAddress = ref(false)
|
const showChangeAddress = ref(false)
|
||||||
const showTelegramChangeAddress = ref(false)
|
const showTelegramChangeAddress = ref(false)
|
||||||
|
const showLocalAddress = ref(false)
|
||||||
|
|
||||||
const copy = async () => {
|
const copy = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -82,6 +84,10 @@ onMounted(async () => {
|
|||||||
size="small" tertiary type="primary">
|
size="small" tertiary type="primary">
|
||||||
<n-icon :component="ExchangeAlt" /> {{ t('changeAddress') }}
|
<n-icon :component="ExchangeAlt" /> {{ t('changeAddress') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<n-button v-else style="margin-left: 10px" @click="showLocalAddress = true" size="small" tertiary
|
||||||
|
type="primary">
|
||||||
|
<n-icon :component="ExchangeAlt" /> {{ t('addressManage') }}
|
||||||
|
</n-button>
|
||||||
<n-button style="margin-left: 10px" @click="copy" size="small" tertiary type="primary">
|
<n-button style="margin-left: 10px" @click="copy" size="small" tertiary type="primary">
|
||||||
<n-icon :component="Copy" /> {{ t('copy') }}
|
<n-icon :component="Copy" /> {{ t('copy') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -112,6 +118,9 @@ onMounted(async () => {
|
|||||||
<n-modal v-model:show="showChangeAddress" preset="card" :title="t('changeAddress')">
|
<n-modal v-model:show="showChangeAddress" preset="card" :title="t('changeAddress')">
|
||||||
<AddressManagement />
|
<AddressManagement />
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
<n-modal v-model:show="showLocalAddress" preset="card" :title="t('changeAddress')">
|
||||||
|
<LocalAddress />
|
||||||
|
</n-modal>
|
||||||
<n-modal v-model:show="showAddressCredential" preset="dialog" :title="t('addressCredential')">
|
<n-modal v-model:show="showAddressCredential" preset="dialog" :title="t('addressCredential')">
|
||||||
<span>
|
<span>
|
||||||
<p>{{ t("addressCredentialTip") }}</p>
|
<p>{{ t("addressCredentialTip") }}</p>
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, h, computed } from 'vue';
|
||||||
|
import { useLocalStorage } from '@vueuse/core';
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { NPopconfirm, NButton } from 'naive-ui'
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { useGlobalState } from '../../store'
|
||||||
|
// @ts-ignore
|
||||||
|
import Login from '../common/Login.vue';
|
||||||
|
|
||||||
|
const { localeCache, jwt } = useGlobalState()
|
||||||
|
// @ts-ignore
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const { t } = useI18n({
|
||||||
|
locale: localeCache.value || 'zh',
|
||||||
|
messages: {
|
||||||
|
en: {
|
||||||
|
tip: 'These addresses are stored in your browser, maybe loss if you clear the browser cache.',
|
||||||
|
success: 'success',
|
||||||
|
address: 'Address',
|
||||||
|
actions: 'Actions',
|
||||||
|
changeMailAddress: 'Change Mail Address',
|
||||||
|
unbindMailAddress: 'Unbind Mail Address credential',
|
||||||
|
bind: 'Bind',
|
||||||
|
bindAddressSuccess: 'Bind Address Success',
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
tip: '这些地址存储在您的浏览器中,如果您清除浏览器缓存,可能会丢失。',
|
||||||
|
success: '成功',
|
||||||
|
address: '地址',
|
||||||
|
actions: '操作',
|
||||||
|
changeMailAddress: '切换邮箱地址',
|
||||||
|
unbindMailAddress: '解绑邮箱地址',
|
||||||
|
bind: '绑定',
|
||||||
|
bindAddressSuccess: '绑定地址成功',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabValue = ref('address')
|
||||||
|
const localAddressCache = useLocalStorage("LocalAddressCache", []);
|
||||||
|
const data = computed(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (!localAddressCache.value.includes(jwt.value)) {
|
||||||
|
// @ts-ignore
|
||||||
|
localAddressCache.value.push(jwt.value)
|
||||||
|
}
|
||||||
|
return localAddressCache.value.map((curJwt: string) => {
|
||||||
|
try {
|
||||||
|
var payload = JSON.parse(
|
||||||
|
decodeURIComponent(
|
||||||
|
atob(curJwt.split(".")[1]
|
||||||
|
.replace(/-/g, "+").replace(/_/g, "/")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
address: payload.address,
|
||||||
|
jwt: curJwt
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
address: `invalid jwt [${curJwt}]`,
|
||||||
|
jwt: curJwt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const bindAddress = async () => {
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
if (!localAddressCache.value.includes(jwt.value)) {
|
||||||
|
// @ts-ignore
|
||||||
|
localAddressCache.value.push(jwt.value)
|
||||||
|
}
|
||||||
|
tabValue.value = 'address'
|
||||||
|
message.success(t('bindAddressSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
message.error((error as Error).message || "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t('address'),
|
||||||
|
key: "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('actions'),
|
||||||
|
key: 'actions',
|
||||||
|
render(row: any) {
|
||||||
|
return h('div', [
|
||||||
|
h(NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => {
|
||||||
|
jwt.value = row.jwt
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: () => h(NButton,
|
||||||
|
{
|
||||||
|
tertiary: true,
|
||||||
|
type: "primary",
|
||||||
|
},
|
||||||
|
{ default: () => t('changeMailAddress') }
|
||||||
|
),
|
||||||
|
default: () => `${t('changeMailAddress')}?`
|
||||||
|
}
|
||||||
|
),
|
||||||
|
h(NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => {
|
||||||
|
if (jwt.value === row.jwt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localAddressCache.value = localAddressCache.value.filter(
|
||||||
|
(curJwt: string) => curJwt !== row.jwt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: () => h(NButton,
|
||||||
|
{
|
||||||
|
tertiary: true,
|
||||||
|
disabled: jwt.value === row.jwt,
|
||||||
|
type: "warning",
|
||||||
|
},
|
||||||
|
{ default: () => t('unbindMailAddress') }
|
||||||
|
),
|
||||||
|
default: () => `${t('unbindMailAddress')}?`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n-alert type="warning" :show-icon="false">
|
||||||
|
<span>{{ t('tip') }}</span>
|
||||||
|
</n-alert>
|
||||||
|
<n-tabs type="segment" v-model:value="tabValue">
|
||||||
|
<n-tab-pane name="address" :tab="t('address')">
|
||||||
|
<n-data-table :columns="columns" :data="data" :bordered="false" />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="bind" :tab="t('bind')">
|
||||||
|
<Login :bindUserAddress="bindAddress" />
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
# 搭建 SMTP IMAP 代理服务
|
# 搭建 SMTP IMAP 代理服务
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
如果你使用了 `resend`, 可直接使用 `resend` 的 `SMTP` 服务,不需要使用此服务
|
||||||
|
:::
|
||||||
|
|
||||||
## 为什么需要 SMTP IMAP 代理服务
|
## 为什么需要 SMTP IMAP 代理服务
|
||||||
|
|
||||||
`SMTP` `IMAP` 的应用场景更加广泛
|
`SMTP` `IMAP` 的应用场景更加广泛
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
limit = 10
|
limit = 10
|
||||||
offset = 0
|
offset = 0
|
||||||
res = requests.get(
|
res = requests.get(
|
||||||
f"http://localhost:8787/api/mails?limit={limit}&offset={offset}`;",
|
f"http://localhost:8787/api/mails?limit={limit}&offset={offset}",
|
||||||
headers={
|
headers={
|
||||||
"Authorization": f"Bearer {你的JWT密码}",
|
"Authorization": f"Bearer {你的JWT密码}",
|
||||||
# "x-custom-auth": "<你的网站密码>", # 如果启用了自定义密码
|
# "x-custom-auth": "<你的网站密码>", # 如果启用了自定义密码
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VERSION: 'v0.4.6',
|
VERSION: 'v0.4.7',
|
||||||
|
|
||||||
// DB settings
|
// DB settings
|
||||||
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
||||||
|
|||||||
Reference in New Issue
Block a user