feat: telegram mini app open mail from bot (#256)

This commit is contained in:
Dream Hunter
2024-05-21 02:03:06 +08:00
committed by GitHub
parent 69771fc1d1
commit 91d7896e65
21 changed files with 487 additions and 151 deletions

View File

@@ -1,9 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router'
import Index from '../views/Index.vue'
import UserLogin from '../views/user/UserLogin.vue'
import User from '../views/User.vue'
import SendMail from '../views/index/SendMail.vue'
import Admin from '../views/Admin.vue'
import TelegramMail from '../views/telegram/Mail.vue'
const router = createRouter({
history: createWebHistory(),
@@ -19,7 +18,11 @@ const router = createRouter({
{
path: '/admin',
component: Admin
}
},
{
path: '/telegram_mail',
component: TelegramMail
},
]
})

View File

@@ -22,6 +22,7 @@ const { t } = useI18n({
enable: 'Enable',
telegramAllowList: 'Telegram Allow List',
save: 'Save',
miniAppUrl: 'Telegram Mini App URL',
},
zh: {
init: '初始化',
@@ -31,6 +32,7 @@ const { t } = useI18n({
enable: '启用',
telegramAllowList: 'Telegram 白名单',
save: '保存',
miniAppUrl: '电报小程序 URL(请输入你部署的电报小程序网页地址)',
}
}
});
@@ -63,14 +65,16 @@ const init = async () => {
class TelegramSettings {
enableAllowList: boolean;
allowList: string[];
miniAppUrl: string;
constructor(enableAllowList: boolean, allowList: string[]) {
constructor(enableAllowList: boolean, allowList: string[], miniAppUrl: string) {
this.enableAllowList = enableAllowList;
this.allowList = allowList;
this.miniAppUrl = miniAppUrl;
}
}
const settings = ref(new TelegramSettings(false, []))
const settings = ref(new TelegramSettings(false, [], ''))
const getSettings = async () => {
try {
@@ -111,6 +115,9 @@ onMounted(async () => {
:placeholder="t('telegramAllowList')" />
</n-input-group>
</n-form-item-row>
<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>

View File

@@ -9,6 +9,29 @@ import Turnstile from '../../components/Turnstile.vue'
import { useGlobalState } from '../../store'
import { api } from '../../api'
const props = defineProps({
bindUserAddress: {
type: Function,
default: async () => { await api.bindUserAddress(); },
requried: true
},
newAddressPath: {
type: Function,
default: async (address_name, domain, cf_token) => {
return await api.fetch("/api/new_address", {
method: "POST",
body: JSON.stringify({
name: address_name,
domain: domain,
cf_token: cf_token,
}),
});
},
requried: true
},
})
const message = useMessage()
const router = useRouter()
@@ -32,7 +55,7 @@ const login = async () => {
jwt.value = credential.value;
await api.getSettings();
try {
await api.bindUserAddress();
await props.bindUserAddress();
} catch (error) {
message.error(`${t('bindUserAddressError')}: ${error.message}`);
}
@@ -98,20 +121,17 @@ const generateName = async () => {
const newEmail = async () => {
try {
const res = await api.fetch(`/api/new_address`, {
method: "POST",
body: JSON.stringify({
name: emailName.value,
domain: emailDomain.value,
cf_token: cfToken.value,
}),
});
const res = await props.newAddressPath(
emailName.value,
emailDomain.value,
cfToken.value
);
jwt.value = res["jwt"];
await api.getSettings();
await router.push("/");
showAddressCredential.value = true;
try {
await api.bindUserAddress();
await props.bindUserAddress();
} catch (error) {
message.error(`${t('bindUserAddressError')}: ${error.message}`);
}

View File

@@ -24,6 +24,7 @@ const { t } = useI18n({
locale: localeCache.value || 'zh',
messages: {
en: {
addressManage: 'Address Manage',
changeAddress: 'Change Address',
ok: 'OK',
copy: 'Copy',
@@ -34,6 +35,7 @@ const { t } = useI18n({
userLogin: 'User Login',
},
zh: {
addressManage: '地址管理',
changeAddress: '更换地址',
ok: '确定',
copy: '复制',
@@ -74,7 +76,7 @@ onMounted(async () => {
<b>{{ settings.address }}</b>
<n-button v-if="isTelegram" style="margin-left: 10px" @click="showTelegramChangeAddress = true"
size="small" tertiary type="primary">
<n-icon :component="ExchangeAlt" /> {{ t('changeAddress') }}
<n-icon :component="ExchangeAlt" /> {{ t('addressManage') }}
</n-button>
<n-button v-else-if="userJwt" style="margin-left: 10px" @click="showChangeAddress = true"
size="small" tertiary type="primary">

View File

@@ -1,6 +1,5 @@
<script setup lang="ts">
import { ref, h, onMounted } from 'vue';
import { useSessionStorage } from '@vueuse/core';
import { useI18n } from 'vue-i18n'
import { NPopconfirm, NButton } from 'naive-ui'
@@ -8,6 +7,8 @@ import { NPopconfirm, NButton } from 'naive-ui'
import { useGlobalState } from '../../store'
// @ts-ignore
import { api } from '../../api'
// @ts-ignore
import Login from '../common/Login.vue';
const { localeCache, jwt, telegramApp } = useGlobalState()
// @ts-ignore
@@ -21,21 +22,27 @@ const { t } = useI18n({
address: 'Address',
actions: 'Actions',
changeMailAddress: 'Change Mail Address',
unbindMailAddress: 'Unbind Mail Address',
bind: 'Bind',
bindAddressSuccess: 'Bind Address Success',
},
zh: {
success: '成功',
address: '地址',
actions: '操作',
changeMailAddress: '切换邮箱地址',
unbindMailAddress: '解绑邮箱地址',
bind: '绑定',
bindAddressSuccess: '绑定地址成功',
}
}
});
const data = useSessionStorage("telegram-bind-address", [])
const data = ref([]);
const fetchData = async () => {
try {
data.value = await api.fetch(`/telegram/bind_address`, {
data.value = await api.fetch(`/telegram/get_bind_address`, {
method: 'POST',
body: JSON.stringify({
initData: telegramApp.value.initData
@@ -46,6 +53,32 @@ const fetchData = async () => {
}
}
const newAddressPath = async (address_name: string, domain: string, cf_token: string) => {
return await api.fetch("/telegram/new_address", {
method: "POST",
body: JSON.stringify({
initData: telegramApp.value.initData,
address: `${address_name}@${domain}`,
cf_token: cf_token,
}),
});
}
const bindAddress = async () => {
try {
await api.fetch(`/telegram/bind_address`, {
method: 'POST',
body: JSON.stringify({
initData: telegramApp.value.initData,
jwt: jwt.value
})
});
message.success(t('bindAddressSuccess'));
} catch (error) {
message.error((error as Error).message || "error");
}
}
const columns = [
{
title: t('address'),
@@ -73,6 +106,31 @@ const columns = [
),
default: () => `${t('changeMailAddress')}?`
}
),
h(NPopconfirm,
{
onPositiveClick: () => {
api.fetch(`/telegram/unbind_address`, {
method: 'POST',
body: JSON.stringify({
initData: telegramApp.value.initData,
address: row.address
})
});
jwt.value = ""
location.reload()
}
},
{
trigger: () => h(NButton,
{
tertiary: true,
type: "warning",
},
{ default: () => t('unbindMailAddress') }
),
default: () => `${t('unbindMailAddress')}?`
}
)
])
}
@@ -89,6 +147,13 @@ onMounted(async () => {
<template>
<div>
<n-data-table :columns="columns" :data="data" :bordered="false" />
<n-tabs type="segment">
<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 :newAddressPath="newAddressPath" :bindUserAddress="bindAddress" />
</n-tab-pane>
</n-tabs>
</div>
</template>

View File

@@ -0,0 +1,70 @@
<script setup>
import { useRoute } from 'vue-router'
import { useGlobalState } from '../../store'
import { api } from '../../api'
import { onMounted, watch } from 'vue';
import { processItem } from '../../utils/email-parser'
const { telegramApp } = useGlobalState()
const route = useRoute()
const curMail = ref({});
watch(telegramApp, async () => {
if (telegramApp.value.initData) {
curMail.value = await fetchMailData();
}
});
const fetchMailData = async () => {
try {
const res = await api.fetch(`/telegram/get_mail`, {
method: 'POST',
body: JSON.stringify({
initData: telegramApp.value.initData,
mailId: route.query.mail_id
})
});
return await processItem(res);
}
catch (error) {
console.error(error);
return {};
}
};
onMounted(async () => {
curMail.value = await fetchMailData();
});
</script>
<template>
<div class="center">
<n-card v-if="curMail.message" style="max-width: 800px; overflow: auto;">
<n-tag type="info">
ID: {{ curMail.id }}
</n-tag>
<n-tag type="info">
Date: {{ curMail.created_at }}
</n-tag>
<n-tag type="info">
FROM: {{ curMail.source }}
</n-tag>
<n-tag v-if="showEMailTo" type="info">
TO: {{ curMail.address }}
</n-tag>
<div v-html="curMail.message" style="margin-top: 10px;"></div>
</n-card>
</div>
</template>
<style scoped>
.center {
display: flex;
text-align: left;
place-items: center;
justify-content: center;
}
</style>