mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-06-28 02:42:44 +08:00
feat: support transfer address from user to user (#484)
* feat: support transfer address from user to user * feat: remove service worker
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
- feat: |UI| 随机生成地址时不超过最大长度
|
||||
- feat: |UI| 邮件时间显示浏览器时区,可在设置中切换显示为 UTC 时间
|
||||
- feat: 支持转移邮件到其他用户
|
||||
|
||||
## v0.7.6
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS raw_mails (
|
||||
CREATE INDEX IF NOT EXISTS idx_raw_mails_address ON raw_mails(address);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS address (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT UNIQUE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
|
||||
@@ -2,12 +2,8 @@ import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import router from './router'
|
||||
import { registerSW } from 'virtual:pwa-register'
|
||||
import { createHead } from '@unhead/vue'
|
||||
|
||||
const disablePwa = import.meta.env.VITE_PWA_DISABLED === 'true';
|
||||
|
||||
if (!disablePwa) registerSW({ immediate: true })
|
||||
const i18n = createI18n({
|
||||
legacy: false, // you must set `false`, to use Composition API
|
||||
locale: 'zh', // set locale
|
||||
|
||||
@@ -20,9 +20,12 @@ const { locale, t } = useI18n({
|
||||
mail_count: 'Mail Count',
|
||||
send_count: 'Send Count',
|
||||
actions: 'Actions',
|
||||
changeMailAddress: 'Change Mail Address',
|
||||
changeMailAddress: 'Change Address',
|
||||
unbindAddress: 'Unbind Address',
|
||||
unbindAddressTip: 'Before unbinding, please switch to this email address and save the email address credential.',
|
||||
transferAddress: 'Transfer Address',
|
||||
targetUserEmail: 'Target User Email',
|
||||
transferAddressTip: 'Transfer address to another user will remove the address from your account and transfer it to another user. Are you sure to transfer the address?'
|
||||
},
|
||||
zh: {
|
||||
success: '成功',
|
||||
@@ -30,14 +33,21 @@ const { locale, t } = useI18n({
|
||||
mail_count: '邮件数量',
|
||||
send_count: '发送数量',
|
||||
actions: '操作',
|
||||
changeMailAddress: '切换邮箱地址',
|
||||
changeMailAddress: '切换地址',
|
||||
unbindAddress: '解绑地址',
|
||||
unbindAddressTip: '解绑前请切换到此邮箱地址并保存邮箱地址凭证。',
|
||||
transferAddress: '转移地址',
|
||||
targetUserEmail: '目标用户邮箱',
|
||||
transferAddressTip: '转移地址到其他用户将会从你的账户中移除此地址并转移给其他用户。确定要转移地址吗?'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const data = ref([])
|
||||
const showTranferAddress = ref(false)
|
||||
const currentAddress = ref("")
|
||||
const currentAddressId = ref(0)
|
||||
const targetUserEmail = ref('')
|
||||
|
||||
const changeMailAddress = async (address_id) => {
|
||||
try {
|
||||
@@ -70,6 +80,35 @@ const unbindAddress = async (address_id) => {
|
||||
}
|
||||
}
|
||||
|
||||
const transferAddress = async () => {
|
||||
if (!targetUserEmail.value) {
|
||||
message.error("targetUserEmail is required");
|
||||
return;
|
||||
}
|
||||
if (!currentAddressId.value) {
|
||||
message.error("currentAddressId is required");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api.fetch(`/user_api/transfer_address`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
address_id: currentAddressId.value,
|
||||
target_user_email: targetUserEmail.value
|
||||
})
|
||||
});
|
||||
message.success(t('transferAddress') + " " + t('success'));
|
||||
await fetchData();
|
||||
showTranferAddress.value = false;
|
||||
currentAddressId.value = 0;
|
||||
currentAddress.value = "";
|
||||
targetUserEmail.value = "";
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const { results, count: addressCount } = await api.fetch(
|
||||
@@ -138,6 +177,18 @@ const columns = [
|
||||
default: () => `${t('changeMailAddress')}?`
|
||||
}
|
||||
),
|
||||
h(NButton,
|
||||
{
|
||||
tertiary: true,
|
||||
type: "primary",
|
||||
onClick: () => {
|
||||
currentAddressId.value = row.id;
|
||||
currentAddress.value = row.name;
|
||||
showTranferAddress.value = true;
|
||||
}
|
||||
},
|
||||
{ default: () => t('transferAddress') }
|
||||
),
|
||||
h(NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => unbindAddress(row.id)
|
||||
@@ -164,6 +215,18 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-modal v-model:show="showTranferAddress" preset="dialog" :title="t('transferAddress')">
|
||||
<span>
|
||||
<p>{{ t("transferAddressTip") }}</p>
|
||||
<p>{{ t('transferAddress') + ": " + currentAddress }}</p>
|
||||
<n-input v-model:value="targetUserEmail" :placeholder="t('targetUserEmail')" />
|
||||
</span>
|
||||
<template #action>
|
||||
<n-button :loading="loading" @click="transferAddress" size="small" tertiary type="error">
|
||||
{{ t('transferAddress') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
<div style="overflow: auto;">
|
||||
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default defineConfig({
|
||||
resolvers: [NaiveUiResolver()]
|
||||
}),
|
||||
VitePWA({
|
||||
registerType: process.env.VITE_PWA_DISABLED == "true" ? null : 'autoUpdate',
|
||||
registerType: null,
|
||||
devOptions: {
|
||||
enabled: true
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { HonoCustomType } from '../types';
|
||||
import { UserSettings } from "../models";
|
||||
import { getJsonSetting } from "../utils"
|
||||
import { CONSTANTS } from "../constants";
|
||||
import { unbindTelegramByAddress } from '../telegram_api/common';
|
||||
|
||||
export default {
|
||||
bind: async (c: Context<HonoCustomType>) => {
|
||||
@@ -89,7 +90,7 @@ export default {
|
||||
return c.text("Failed to unbind", 500)
|
||||
}
|
||||
} catch (e) {
|
||||
return c.text("Invalid address token", 400)
|
||||
return c.text("Failed to unbind", 500)
|
||||
}
|
||||
return c.json({ success: true })
|
||||
},
|
||||
@@ -139,4 +140,92 @@ export default {
|
||||
jwt: jwt
|
||||
})
|
||||
},
|
||||
transferAddress: async (c: Context<HonoCustomType>) => {
|
||||
const { user_id } = c.get("userPayload");
|
||||
const { address_id, target_user_email } = await c.req.json();
|
||||
// check if address exists
|
||||
const address = await c.env.DB.prepare(
|
||||
`SELECT name FROM address where id = ?`
|
||||
).bind(address_id).first<string>("name");
|
||||
if (!address) {
|
||||
return c.text("Address not found", 400)
|
||||
}
|
||||
// check if user exists
|
||||
const db_user_id = await c.env.DB.prepare(
|
||||
`SELECT id FROM users where id = ?`
|
||||
).bind(user_id).first("id");
|
||||
if (!db_user_id) {
|
||||
return c.text("User not found", 400)
|
||||
}
|
||||
// check if target user exists
|
||||
const target_user_id = await c.env.DB.prepare(
|
||||
`SELECT id FROM users where user_email = ?`
|
||||
).bind(target_user_email).first("id");
|
||||
if (!target_user_id) {
|
||||
return c.text("Target user not found", 400)
|
||||
}
|
||||
// check target user binded address count
|
||||
const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY);
|
||||
const settings = new UserSettings(value);
|
||||
if (settings.maxAddressCount > 0) {
|
||||
const { count } = await c.env.DB.prepare(
|
||||
`SELECT COUNT(*) as count FROM users_address where user_id = ?`
|
||||
).bind(target_user_id).first<{ count: number }>() || { count: 0 };
|
||||
if (count >= settings.maxAddressCount) {
|
||||
return c.text("Target User Max address count reached", 400)
|
||||
}
|
||||
}
|
||||
// check if binded
|
||||
const db_user_address_id = await c.env.DB.prepare(
|
||||
`SELECT user_id FROM users_address where user_id = ? and address_id = ?`
|
||||
).bind(user_id, address_id).first("user_id");
|
||||
if (!db_user_address_id) return c.text("Address not binded", 400)
|
||||
// unbind telegram address
|
||||
await unbindTelegramByAddress(c, address);
|
||||
// unbind user address
|
||||
try {
|
||||
const { success } = await c.env.DB.prepare(
|
||||
`DELETE FROM users_address where user_id = ? and address_id = ?`
|
||||
).bind(user_id, address_id).run();
|
||||
if (!success) {
|
||||
return c.text("Failed to unbind", 500)
|
||||
}
|
||||
} catch (e) {
|
||||
return c.text("Failed to unbind user", 500)
|
||||
}
|
||||
// delete address
|
||||
await c.env.DB.prepare(
|
||||
`DELETE FROM address WHERE id = ? `
|
||||
).bind(address_id).run();
|
||||
// new address
|
||||
const { success: newAddressSuccess } = await c.env.DB.prepare(
|
||||
`INSERT INTO address(name) VALUES(?)`
|
||||
).bind(address).run();
|
||||
if (!newAddressSuccess) {
|
||||
throw new Error("Failed to create address")
|
||||
}
|
||||
// find new address id
|
||||
let new_address_id = await c.env.DB.prepare(
|
||||
`SELECT id FROM address WHERE name = ?`
|
||||
).bind(address).first<number | null | undefined>("id");
|
||||
if (!new_address_id) {
|
||||
throw new Error("Failed to find new address id")
|
||||
}
|
||||
// bind
|
||||
try {
|
||||
const { success } = await c.env.DB.prepare(
|
||||
`INSERT INTO users_address (user_id, address_id) VALUES (?, ?)`
|
||||
).bind(target_user_id, new_address_id).run();
|
||||
if (!success) {
|
||||
return c.text("Failed to bind", 500)
|
||||
}
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
if (error.message && error.message.includes("UNIQUE")) {
|
||||
return c.text("Address already binded, please unbind first", 400)
|
||||
}
|
||||
return c.text("Failed to bind", 500)
|
||||
}
|
||||
return c.json({ success: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ api.get('/user_api/bind_address', bind_address.getBindedAddresses);
|
||||
api.post('/user_api/bind_address', bind_address.bind);
|
||||
api.get('/user_api/bind_address_jwt/:address_id', bind_address.getBindedAddressJwt);
|
||||
api.post('/user_api/unbind_address', bind_address.unbind);
|
||||
api.post('/user_api/transfer_address', bind_address.transferAddress);
|
||||
|
||||
// passkey api
|
||||
api.get('/user_api/passkey', passkey.getPassKeys);
|
||||
|
||||
Reference in New Issue
Block a user