feat: add custom address (#13)

* feat: add custom address

* feat: update readme
This commit is contained in:
Dream Hunter
2023-09-07 13:21:36 +08:00
committed by GitHub
parent a6b29073f5
commit 536cd98df2
6 changed files with 106 additions and 27 deletions

View File

@@ -31,6 +31,7 @@
- [x] 使用 Cloudflare Workers 部署后端
- [x] email 转发使用 Cloudflare Email Routing
- [x] 使用 password 重新登录之前的邮箱
- [x] 获取自定义名字的邮箱
- [ ] 免费版附件过大会造成 Exceeded CPU Limit 错误
---

View File

@@ -13,6 +13,7 @@ This is a temporary email service that uses Cloudflare Workers to create a tempo
- [x] Deploy the backend with Cloudflare Workers
- [x] Email forwarding using Cloudflare Email Routing
- [x] Use password to login to the previous mailbox again.
- [x] Get Custom Name Email
- [ ] Exceeded CPU Limit error caused by the free version of the attachment
![demo](readme_assets/demo.png)

2
frontend/.gitignore vendored
View File

@@ -27,4 +27,4 @@ coverage
*.sln
*.sw?
.env.local
.env.*

View File

@@ -1,7 +1,7 @@
<script setup>
import { NMessageProvider, NGrid, NBackTop, NLayoutHeader, NInput } from 'naive-ui'
import { NGi, NSpace, NButton, NConfigProvider, NSelect, NModal } from 'naive-ui'
import { darkTheme, NSwitch, NGlobalStyle } from 'naive-ui'
import { darkTheme, NSwitch, NGlobalStyle, NPopconfirm } from 'naive-ui'
import { computed, ref } from 'vue'
import { useStorage } from '@vueuse/core'
@@ -17,6 +17,10 @@ const login = () => {
jwt.value = password.value;
location.reload()
}
const logout = () => {
jwt.value = '';
location.reload()
}
</script>
<template>
@@ -41,7 +45,17 @@ const login = () => {
Light
</template>
</n-switch>
<n-button tertiary @click="showLogin = true" round type="primary">
<n-popconfirm v-if="jwt" @positive-click="logout">
<template #trigger>
<n-button tertiary round type="primary">
Logout
</n-button>
</template>
<template #default>
<span>Are you sure to logout?</span>
</template>
</n-popconfirm>
<n-button v-else tertiary @click="showLogin = true" round type="primary">
Login
</n-button>
<n-button tag="a" target="_blank" tertiary type="primary" round

View File

@@ -1,6 +1,6 @@
<script setup>
import { NSpace, NAlert, NSwitch, NCard } from 'naive-ui'
import { NSpin, NButton, NLayout, NPopconfirm, NModal } from 'naive-ui'
import { NSpace, NAlert, NSwitch, NCard, NInput, NInputGroupLabel } from 'naive-ui'
import { NSpin, NButton, NLayout, NInputGroup, NModal } from 'naive-ui'
import { NList, NListItem, NThing, NTag, NNumberAnimation } from 'naive-ui'
import { watch, onMounted, ref } from "vue";
import { useStorage } from '@vueuse/core'
@@ -18,6 +18,12 @@ const data = ref([])
const API_BASE = import.meta.env.VITE_API_BASE || "";
const timer = ref(null)
const showPassword = ref(false)
const showNewEmail = ref(false)
const emailName = ref("")
const openSettings = ref({
prefix: 'test',
domain: 'test.com'
})
const setupAutoRefresh = async (autoRefresh) => {
if (autoRefresh) {
@@ -74,7 +80,11 @@ const copy = async () => {
const newEmail = async () => {
try {
loading.value = true;
const response = await fetch(`${API_BASE}/api/new_address`, {
let url = `${API_BASE}/api/new_address`;
if (emailName.value) {
url = `${url}?name=${emailName.value}`;
}
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json"
@@ -82,24 +92,38 @@ const newEmail = async () => {
});
if (!response.ok) {
message.error(
`${response.status} ${await response.text()}` || "error",
);
throw new Error(`${response.status} ${await response.text()}` || "error");
}
let res = await response.json();
jwt.value = res["jwt"];
await refresh();
showNewEmail.value = false;
showPassword.value = true;
} catch (error) {
jwt.value = "";
message.error(error.message || "error");
console.error(error);
} finally {
loading.value = false;
}
await refresh();
showPassword.value = true;
};
const getOpenSettings = async (jwt) => {
const response = await fetch(`${API_BASE}/open_api/settings`, {
method: "GET",
headers: {
"Content-Type": "application/json"
},
});
if (!response.ok) {
message.error(`${response.status} ${await response.text()}` || "error");
console.error(response);
return;
}
let res = await response.json();
openSettings.value = res;
}
const getSettings = async (jwt) => {
if (typeof jwt != 'string' || jwt.trim() === '' || jwt === 'undefined') {
return;
@@ -126,6 +150,7 @@ const getSettings = async (jwt) => {
watch(jwt, async (jwt, old) => getSettings(jwt))
onMounted(async () => {
getOpenSettings()
getSettings(jwt.value)
await refresh();
const token = import.meta.env.VITE_CF_WEB_ANALY_TOKEN;
@@ -156,17 +181,12 @@ onMounted(async () => {
Please click <b>Get New Email</b> button to get a new email address
</span>
</n-alert>
<n-button class="center" @click="showPassword = true" tertiary round type="primary">
<n-button v-if="address" class="center" @click="showPassword = true" tertiary round type="primary">
Show Password
</n-button>
<n-popconfirm @positive-click="newEmail" :show-icon="false">
<template #trigger>
<n-button class="center" tertiary round type="primary">
Get New Email
</n-button>
</template>
Get New Email?
</n-popconfirm>
<n-button v-else class="center" @click="showNewEmail = true" tertiary round type="primary">
Get New Email
</n-button>
<n-switch v-model:value="autoRefresh">
<template #checked>
Auto Refresh
@@ -195,6 +215,32 @@ onMounted(async () => {
</n-list-item>
</n-list>
</n-layout>
<n-modal v-model:show="showNewEmail" preset="dialog" title="Dialog">
<template #header>
<div>Get New Email</div>
</template>
<span>
<p>Please input the email you want to use.</p>
<p>Levaing it blank will generate a random email address.</p>
</span>
<n-input-group>
<n-input-group-label v-if="openSettings.prefix">
{{ openSettings.prefix }}
</n-input-group-label>
<n-input v-model:value="emailName" />
<n-input-group-label>
@{{ openSettings.domain }}
</n-input-group-label>
</n-input-group>
<template #action>
<n-button @click="showNewEmail = false">
Cancel
</n-button>
<n-button @click="newEmail" type="primary">
OK
</n-button>
</template>
</n-modal>
<n-modal v-model:show="showPassword" preset="dialog" title="Dialog">
<template #header>
<div>Password</div>

View File

@@ -18,14 +18,31 @@ api.get('/api/settings', async (c) => {
return c.json(c.get("jwtPayload"));
})
api.get('/open_api/settings', async (c) => {
return c.json({
"prefix": c.env.PREFIX,
"domain": c.env.DOMAIN,
});
})
api.get('/api/new_address', async (c) => {
// insert new address
const name = Math.random().toString(36).substring(2, 15)
const { success } = await c.env.DB.prepare(
`INSERT INTO address (name) VALUES (?)`
).bind(name).run();
if (!success) {
return c.json({ "error": "Failed to create address" }, 500)
let { name } = await c.req.query();
if (!name) {
name = Math.random().toString(36).substring(2, 15)
}
try {
const { success } = await c.env.DB.prepare(
`INSERT INTO address (name) VALUES (?)`
).bind(name).run();
if (!success) {
return c.text("Failed to create address", 500)
}
} catch (e) {
if (e.message && e.message.includes("UNIQUE")) {
return c.text("Please retry a new address", 400)
}
return c.text("Failed to create address", 500)
}
// create jwt
const jwt = await Jwt.sign({