mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-10 18:07:00 +08:00
Compare commits
9
Commits
v1.0.0
...
840496c48f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
840496c48f | ||
|
|
9843b35f54 | ||
|
|
bfd66f5019 | ||
|
|
0bc31360b0 | ||
|
|
267d9bb93e | ||
|
|
2cc84d565c | ||
|
|
c96d180591 | ||
|
|
1303b0f2a9 | ||
|
|
9f535a0a90 |
@@ -1,6 +1,20 @@
|
|||||||
<!-- markdownlint-disable-file MD004 MD024 MD034 MD036 -->
|
<!-- markdownlint-disable-file MD004 MD024 MD034 MD036 -->
|
||||||
# CHANGE LOG
|
# CHANGE LOG
|
||||||
|
|
||||||
|
## v1.0.3
|
||||||
|
|
||||||
|
- fix: 修复 github actions 部署问题
|
||||||
|
- feat: telegram /new 不指定域名时, 使用随机地址
|
||||||
|
|
||||||
|
## v1.0.2
|
||||||
|
|
||||||
|
- fix: 修复 oauth2 登录失败的问题
|
||||||
|
|
||||||
|
## v1.0.1
|
||||||
|
|
||||||
|
- feat: |UI| 增加极简模式主页, 可在 `外观` 中切换
|
||||||
|
- fix: 修复 oauth2 登录时,default role 不生效的问题
|
||||||
|
|
||||||
## v1.0.0
|
## v1.0.0
|
||||||
|
|
||||||
- fix: |UI| 修复 User 查看收件箱,不选择地址时,关键词查询不生效
|
- fix: |UI| 修复 User 查看收件箱,不选择地址时,关键词查询不生效
|
||||||
|
|||||||
+10
-10
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -24,15 +24,15 @@
|
|||||||
"@vueuse/core": "^12.8.2",
|
"@vueuse/core": "^12.8.2",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.11.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mail-parser-wasm": "^0.2.1",
|
"mail-parser-wasm": "^0.2.1",
|
||||||
"naive-ui": "^2.42.0",
|
"naive-ui": "^2.42.0",
|
||||||
"postal-mime": "^2.4.3",
|
"postal-mime": "^2.4.4",
|
||||||
"vooks": "^0.2.12",
|
"vooks": "^0.2.12",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.18",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-i18n": "^11.1.6",
|
"vue-i18n": "^11.1.11",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -40,14 +40,14 @@
|
|||||||
"@vicons/material": "^0.13.0",
|
"@vicons/material": "^0.13.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.4",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"unplugin-auto-import": "^19.3.0",
|
"unplugin-auto-import": "^19.3.0",
|
||||||
"unplugin-vue-components": "^28.7.0",
|
"unplugin-vue-components": "^28.8.0",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
"vite-plugin-pwa": "^1.0.0",
|
"vite-plugin-pwa": "^1.0.2",
|
||||||
"vite-plugin-top-level-await": "^1.5.0",
|
"vite-plugin-top-level-await": "^1.6.0",
|
||||||
"vite-plugin-wasm": "^3.4.1",
|
"vite-plugin-wasm": "^3.5.0",
|
||||||
"workbox-build": "^7.3.0",
|
"workbox-build": "^7.3.0",
|
||||||
"workbox-window": "^7.3.0",
|
"workbox-window": "^7.3.0",
|
||||||
"wrangler": "^4.20.4"
|
"wrangler": "^4.28.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+974
-964
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ export const useGlobalState = createGlobalState(
|
|||||||
const toggleDark = useToggle(isDark)
|
const toggleDark = useToggle(isDark)
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const announcement = useLocalStorage('announcement', '');
|
const announcement = useLocalStorage('announcement', '');
|
||||||
|
const useSimpleIndex = useLocalStorage('useSimpleIndex', false);
|
||||||
const openSettings = ref({
|
const openSettings = ref({
|
||||||
fetched: false,
|
fetched: false,
|
||||||
title: '',
|
title: '',
|
||||||
@@ -142,6 +143,7 @@ export const useGlobalState = createGlobalState(
|
|||||||
showAdminPage,
|
showAdminPage,
|
||||||
userOauth2SessionState,
|
userOauth2SessionState,
|
||||||
userOauth2SessionClientID,
|
userOauth2SessionClientID,
|
||||||
|
useSimpleIndex,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ import MailBox from '../components/MailBox.vue';
|
|||||||
import SendBox from '../components/SendBox.vue';
|
import SendBox from '../components/SendBox.vue';
|
||||||
import AutoReply from './index/AutoReply.vue';
|
import AutoReply from './index/AutoReply.vue';
|
||||||
import AccountSettings from './index/AccountSettings.vue';
|
import AccountSettings from './index/AccountSettings.vue';
|
||||||
|
import Appearance from './common/Appearance.vue';
|
||||||
import Webhook from './index/Webhook.vue';
|
import Webhook from './index/Webhook.vue';
|
||||||
import Attachment from './index/Attachment.vue';
|
import Attachment from './index/Attachment.vue';
|
||||||
import About from './common/About.vue';
|
import About from './common/About.vue';
|
||||||
|
|
||||||
const { loading, settings, openSettings, indexTab, globalTabplacement } = useGlobalState()
|
import SimpleIndex from './index/SimpleIndex.vue';
|
||||||
|
|
||||||
|
const { loading, settings, openSettings, indexTab, globalTabplacement, useSimpleIndex } = useGlobalState()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -33,6 +36,7 @@ const { t } = useI18n({
|
|||||||
sendmail: 'Send Mail',
|
sendmail: 'Send Mail',
|
||||||
auto_reply: 'Auto Reply',
|
auto_reply: 'Auto Reply',
|
||||||
accountSettings: 'Account Settings',
|
accountSettings: 'Account Settings',
|
||||||
|
appearance: 'Appearance',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
s3Attachment: 'S3 Attachment',
|
s3Attachment: 'S3 Attachment',
|
||||||
saveToS3Success: 'save to s3 success',
|
saveToS3Success: 'save to s3 success',
|
||||||
@@ -44,7 +48,8 @@ const { t } = useI18n({
|
|||||||
sendbox: '发件箱',
|
sendbox: '发件箱',
|
||||||
sendmail: '发送邮件',
|
sendmail: '发送邮件',
|
||||||
auto_reply: '自动回复',
|
auto_reply: '自动回复',
|
||||||
accountSettings: '账户设置',
|
accountSettings: '账户',
|
||||||
|
appearance: '外观',
|
||||||
about: '关于',
|
about: '关于',
|
||||||
s3Attachment: 'S3附件',
|
s3Attachment: 'S3附件',
|
||||||
saveToS3Success: '保存到s3成功',
|
saveToS3Success: '保存到s3成功',
|
||||||
@@ -122,43 +127,51 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<AddressBar />
|
<div v-if="useSimpleIndex">
|
||||||
<n-tabs v-if="settings.address" type="card" v-model:value="indexTab" :placement="globalTabplacement">
|
<SimpleIndex />
|
||||||
<n-tab-pane name="mailbox" :tab="t('mailbox')">
|
</div>
|
||||||
<div v-if="showMailIdQuery" style="margin-bottom: 10px;">
|
<div v-else>
|
||||||
<n-input-group>
|
<AddressBar />
|
||||||
<n-input v-model:value="mailIdQuery" />
|
<n-tabs v-if="settings.address" type="card" v-model:value="indexTab" :placement="globalTabplacement">
|
||||||
<n-button @click="queryMail" type="primary" tertiary>
|
<n-tab-pane name="mailbox" :tab="t('mailbox')">
|
||||||
{{ t('query') }}
|
<div v-if="showMailIdQuery" style="margin-bottom: 10px;">
|
||||||
</n-button>
|
<n-input-group>
|
||||||
</n-input-group>
|
<n-input v-model:value="mailIdQuery" />
|
||||||
</div>
|
<n-button @click="queryMail" type="primary" tertiary>
|
||||||
<MailBox :key="mailBoxKey" :showEMailTo="false" :showReply="true" :showSaveS3="openSettings.isS3Enabled"
|
{{ t('query') }}
|
||||||
:saveToS3="saveToS3" :enableUserDeleteEmail="openSettings.enableUserDeleteEmail"
|
</n-button>
|
||||||
:fetchMailData="fetchMailData" :deleteMail="deleteMail" />
|
</n-input-group>
|
||||||
</n-tab-pane>
|
</div>
|
||||||
<n-tab-pane name="sendbox" :tab="t('sendbox')">
|
<MailBox :key="mailBoxKey" :showEMailTo="false" :showReply="true" :showSaveS3="openSettings.isS3Enabled"
|
||||||
<SendBox :fetchMailData="fetchSenboxData" :enableUserDeleteEmail="openSettings.enableUserDeleteEmail"
|
:saveToS3="saveToS3" :enableUserDeleteEmail="openSettings.enableUserDeleteEmail"
|
||||||
:deleteMail="deleteSenboxMail" />
|
:fetchMailData="fetchMailData" :deleteMail="deleteMail" />
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="sendmail" :tab="t('sendmail')">
|
<n-tab-pane name="sendbox" :tab="t('sendbox')">
|
||||||
<SendMail />
|
<SendBox :fetchMailData="fetchSenboxData" :enableUserDeleteEmail="openSettings.enableUserDeleteEmail"
|
||||||
</n-tab-pane>
|
:deleteMail="deleteSenboxMail" />
|
||||||
<n-tab-pane name="accountSettings" :tab="t('accountSettings')">
|
</n-tab-pane>
|
||||||
<AccountSettings />
|
<n-tab-pane name="sendmail" :tab="t('sendmail')">
|
||||||
</n-tab-pane>
|
<SendMail />
|
||||||
<n-tab-pane v-if="openSettings.enableAutoReply" name="auto_reply" :tab="t('auto_reply')">
|
</n-tab-pane>
|
||||||
<AutoReply />
|
<n-tab-pane name="accountSettings" :tab="t('accountSettings')">
|
||||||
</n-tab-pane>
|
<AccountSettings />
|
||||||
<n-tab-pane v-if="openSettings.enableWebhook" name="webhook" :tab="t('webhookSettings')">
|
</n-tab-pane>
|
||||||
<Webhook />
|
<n-tab-pane name="appearance" :tab="t('appearance')">
|
||||||
</n-tab-pane>
|
<Appearance :showUseSimpleIndex="true" />
|
||||||
<n-tab-pane v-if="openSettings.isS3Enabled" name="s3_attachment" :tab="t('s3Attachment')">
|
</n-tab-pane>
|
||||||
<Attachment />
|
<n-tab-pane v-if="openSettings.enableAutoReply" name="auto_reply" :tab="t('auto_reply')">
|
||||||
</n-tab-pane>
|
<AutoReply />
|
||||||
<n-tab-pane v-if="openSettings.enableIndexAbout" name="about" :tab="t('about')">
|
</n-tab-pane>
|
||||||
<About />
|
<n-tab-pane v-if="openSettings.enableWebhook" name="webhook" :tab="t('webhookSettings')">
|
||||||
</n-tab-pane>
|
<Webhook />
|
||||||
</n-tabs>
|
</n-tab-pane>
|
||||||
|
<n-tab-pane v-if="openSettings.isS3Enabled" name="s3_attachment" :tab="t('s3Attachment')">
|
||||||
|
<Attachment />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane v-if="openSettings.enableIndexAbout" name="about" :tab="t('about')">
|
||||||
|
<About />
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const { t } = useI18n({
|
|||||||
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login to your email account.',
|
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login to your email account.',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
deleteTip: 'Are you sure to delete this email?',
|
deleteTip: 'Are you sure to delete this email?',
|
||||||
delteAccount: 'Delete Account',
|
deleteAccount: 'Delete Account',
|
||||||
viewMails: 'View Mails',
|
viewMails: 'View Mails',
|
||||||
viewSendBox: 'View SendBox',
|
viewSendBox: 'View SendBox',
|
||||||
itemCount: 'itemCount',
|
itemCount: 'itemCount',
|
||||||
@@ -46,7 +46,7 @@ const { t } = useI18n({
|
|||||||
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
|
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
deleteTip: '确定要删除这个邮箱吗?',
|
deleteTip: '确定要删除这个邮箱吗?',
|
||||||
delteAccount: '删除邮箱',
|
deleteAccount: '删除邮箱',
|
||||||
viewMails: '查看邮件',
|
viewMails: '查看邮件',
|
||||||
viewSendBox: '查看发件箱',
|
viewSendBox: '查看发件箱',
|
||||||
itemCount: '总数',
|
itemCount: '总数',
|
||||||
@@ -273,11 +273,11 @@ onMounted(async () => {
|
|||||||
<template #action>
|
<template #action>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
<n-modal v-model:show="showDeleteAccount" preset="dialog" :title="t('delteAccount')">
|
<n-modal v-model:show="showDeleteAccount" preset="dialog" :title="t('deleteAccount')">
|
||||||
<p>{{ t('deleteTip') }}</p>
|
<p>{{ t('deleteTip') }}</p>
|
||||||
<template #action>
|
<template #action>
|
||||||
<n-button :loading="loading" @click="deleteEmail" size="small" tertiary type="error">
|
<n-button :loading="loading" @click="deleteEmail" size="small" tertiary type="error">
|
||||||
{{ t('delteAccount') }}
|
{{ t('deleteAccount') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|||||||
@@ -3,16 +3,23 @@ import { useI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
import { useIsMobile } from '../../utils/composables'
|
import { useIsMobile } from '../../utils/composables'
|
||||||
import { useGlobalState } from '../../store'
|
import { useGlobalState } from '../../store'
|
||||||
|
const props = defineProps({
|
||||||
|
showUseSimpleIndex: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mailboxSplitSize, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
mailboxSplitSize, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
||||||
globalTabplacement, useSideMargin, useUTCDate
|
globalTabplacement, useSideMargin, useUTCDate, useSimpleIndex
|
||||||
} = useGlobalState()
|
} = useGlobalState()
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
|
|
||||||
const { t } = useI18n({
|
const { t } = useI18n({
|
||||||
messages: {
|
messages: {
|
||||||
en: {
|
en: {
|
||||||
|
useSimpleIndex: 'Use Simple Index',
|
||||||
mailboxSplitSize: 'Mailbox Split Size',
|
mailboxSplitSize: 'Mailbox Split Size',
|
||||||
useIframeShowMail: 'Use iframe Show HTML Mail',
|
useIframeShowMail: 'Use iframe Show HTML Mail',
|
||||||
preferShowTextMail: 'Display text Mail by default',
|
preferShowTextMail: 'Display text Mail by default',
|
||||||
@@ -26,6 +33,7 @@ const { t } = useI18n({
|
|||||||
autoRefreshInterval: 'Auto Refresh Interval(Sec)',
|
autoRefreshInterval: 'Auto Refresh Interval(Sec)',
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
|
useSimpleIndex: '使用极简主页',
|
||||||
mailboxSplitSize: '邮箱界面分栏大小',
|
mailboxSplitSize: '邮箱界面分栏大小',
|
||||||
preferShowTextMail: '默认以文本显示邮件',
|
preferShowTextMail: '默认以文本显示邮件',
|
||||||
useIframeShowMail: '使用iframe显示HTML邮件',
|
useIframeShowMail: '使用iframe显示HTML邮件',
|
||||||
@@ -57,6 +65,9 @@ const { t } = useI18n({
|
|||||||
60: '60', 120: '120', 180: '180', 240: '240'
|
60: '60', 120: '120', 180: '180', 240: '240'
|
||||||
}" />
|
}" />
|
||||||
</n-form-item-row>
|
</n-form-item-row>
|
||||||
|
<n-form-item-row v-if="props.showUseSimpleIndex" :label="t('useSimpleIndex')">
|
||||||
|
<n-switch v-model:value="useSimpleIndex" :round="false" />
|
||||||
|
</n-form-item-row>
|
||||||
<n-form-item-row :label="t('preferShowTextMail')">
|
<n-form-item-row :label="t('preferShowTextMail')">
|
||||||
<n-switch v-model:value="preferShowTextMail" :round="false" />
|
<n-switch v-model:value="preferShowTextMail" :round="false" />
|
||||||
</n-form-item-row>
|
</n-form-item-row>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useRouter } from 'vue-router'
|
|||||||
|
|
||||||
import { useGlobalState } from '../../store'
|
import { useGlobalState } from '../../store'
|
||||||
import { api } from '../../api'
|
import { api } from '../../api'
|
||||||
import Appearance from '../common/Appearance.vue'
|
|
||||||
import { getRouterPathWithLang } from '../../utils'
|
import { getRouterPathWithLang } from '../../utils'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -15,24 +14,24 @@ const router = useRouter()
|
|||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const showLogout = ref(false)
|
const showLogout = ref(false)
|
||||||
const showDelteAccount = ref(false)
|
const showDeleteAccount = ref(false)
|
||||||
const { locale, t } = useI18n({
|
const { locale, t } = useI18n({
|
||||||
messages: {
|
messages: {
|
||||||
en: {
|
en: {
|
||||||
logout: "Logout",
|
logout: "Logout",
|
||||||
delteAccount: "Delete Account",
|
deleteAccount: "Delete Account",
|
||||||
showAddressCredential: 'Show Address Credential',
|
showAddressCredential: 'Show Address Credential',
|
||||||
logoutConfirm: 'Are you sure to logout?',
|
logoutConfirm: 'Are you sure to logout?',
|
||||||
delteAccount: "Delete Account",
|
deleteAccount: "Delete Account",
|
||||||
delteAccountConfirm: "Are you sure to delete your account and all emails for this account?",
|
deleteAccountConfirm: "Are you sure to delete your account and all emails for this account?",
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
logout: '退出登录',
|
logout: '退出登录',
|
||||||
delteAccount: "删除账户",
|
deleteAccount: "删除账户",
|
||||||
showAddressCredential: '查看邮箱地址凭证',
|
showAddressCredential: '查看邮箱地址凭证',
|
||||||
logoutConfirm: '确定要退出登录吗?',
|
logoutConfirm: '确定要退出登录吗?',
|
||||||
delteAccount: "删除账户",
|
deleteAccount: "删除账户",
|
||||||
delteAccountConfirm: "确定要删除你的账户和其中的所有邮件吗?",
|
deleteAccountConfirm: "确定要删除你的账户和其中的所有邮件吗?",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -60,15 +59,14 @@ const deleteAccount = async () => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="center" v-if="settings.address">
|
<div class="center" v-if="settings.address">
|
||||||
<n-card :bordered="false" embedded>
|
<n-card :bordered="false" embedded>
|
||||||
<Appearance />
|
|
||||||
<n-button @click="showAddressCredential = true" type="primary" secondary block strong>
|
<n-button @click="showAddressCredential = true" type="primary" secondary block strong>
|
||||||
{{ t('showAddressCredential') }}
|
{{ t('showAddressCredential') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button @click="showLogout = true" secondary block strong>
|
<n-button @click="showLogout = true" secondary block strong>
|
||||||
{{ t('logout') }}
|
{{ t('logout') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button @click="showDelteAccount = true" type="error" secondary block strong>
|
<n-button @click="showDeleteAccount = true" type="error" secondary block strong>
|
||||||
{{ t('delteAccount') }}
|
{{ t('deleteAccount') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-modal v-model:show="showLogout" preset="dialog" :title="t('logout')">
|
<n-modal v-model:show="showLogout" preset="dialog" :title="t('logout')">
|
||||||
@@ -79,11 +77,11 @@ const deleteAccount = async () => {
|
|||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
<n-modal v-model:show="showDelteAccount" preset="dialog" :title="t('delteAccount')">
|
<n-modal v-model:show="showDeleteAccount" preset="dialog" :title="t('deleteAccount')">
|
||||||
<p>{{ t('delteAccountConfirm') }}</p>
|
<p>{{ t('deleteAccountConfirm') }}</p>
|
||||||
<template #action>
|
<template #action>
|
||||||
<n-button :loading="loading" @click="deleteAccount" size="small" tertiary type="error">
|
<n-button :loading="loading" @click="deleteAccount" size="small" tertiary type="error">
|
||||||
{{ t('delteAccount') }}
|
{{ t('deleteAccount') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, computed, watch } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
import {
|
||||||
|
ExitToAppFilled,
|
||||||
|
ContentCopyFilled,
|
||||||
|
RefreshFilled,
|
||||||
|
ArrowBackIosNewFilled,
|
||||||
|
ArrowForwardIosFilled,
|
||||||
|
SettingsFilled
|
||||||
|
} from '@vicons/material'
|
||||||
|
|
||||||
|
import { useGlobalState } from '../../store'
|
||||||
|
import { api } from '../../api'
|
||||||
|
import Login from '../common/Login.vue'
|
||||||
|
import AccountSettings from './AccountSettings.vue'
|
||||||
|
import { processItem } from '../../utils/email-parser'
|
||||||
|
import { utcToLocalDate } from '../../utils'
|
||||||
|
import ShadowHtmlComponent from '../../components/ShadowHtmlComponent.vue'
|
||||||
|
|
||||||
|
const { jwt, settings, useSimpleIndex, useUTCDate, showAddressCredential } = useGlobalState()
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
// 邮件数据
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const totalCount = ref(0)
|
||||||
|
const loading = ref(false)
|
||||||
|
const currentMail = ref(null)
|
||||||
|
const showAccountSettingsCard = ref(false)
|
||||||
|
|
||||||
|
const { t } = useI18n({
|
||||||
|
messages: {
|
||||||
|
en: {
|
||||||
|
exitSimpleIndex: 'Exit Simple',
|
||||||
|
copyAddress: 'Copy',
|
||||||
|
addressCopied: 'Address copied successfully',
|
||||||
|
refreshMails: 'Refresh',
|
||||||
|
noMails: 'No mails found',
|
||||||
|
prevPage: 'Previous',
|
||||||
|
nextPage: 'Next',
|
||||||
|
refreshSuccess: 'Mails refreshed successfully',
|
||||||
|
mailCount: '{current} / {total} emails',
|
||||||
|
accountSettings: "Account Settings",
|
||||||
|
addressCredential: 'Mail Address Credential',
|
||||||
|
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login',
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
exitSimpleIndex: '退出极简',
|
||||||
|
copyAddress: '复制',
|
||||||
|
addressCopied: '地址复制成功',
|
||||||
|
refreshMails: '刷新',
|
||||||
|
noMails: '暂无邮件',
|
||||||
|
prevPage: '上一页',
|
||||||
|
nextPage: '下一页',
|
||||||
|
refreshSuccess: '邮件刷新成功',
|
||||||
|
mailCount: '{current} / {total} 封邮件',
|
||||||
|
accountSettings: "账户设置",
|
||||||
|
addressCredential: '邮箱地址凭证',
|
||||||
|
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 复制地址
|
||||||
|
const copyAddress = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(settings.value.address)
|
||||||
|
message.success(t('addressCopied'))
|
||||||
|
} catch (error) {
|
||||||
|
message.error('复制失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取邮件数据
|
||||||
|
const fetchMails = async () => {
|
||||||
|
if (!settings.value.address) return
|
||||||
|
try {
|
||||||
|
const { results, count } = await api.fetch(`/api/mails?limit=1&offset=${currentPage.value - 1}`)
|
||||||
|
totalCount.value = count > 0 ? count : totalCount.value;
|
||||||
|
const rawMail = results && results.length > 0 ? results[0] : null
|
||||||
|
currentMail.value = rawMail ? await processItem(rawMail) : null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch mails:', error)
|
||||||
|
message.error('获取邮件失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新邮件
|
||||||
|
const refreshMails = async () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
await fetchMails()
|
||||||
|
message.success(t('refreshSuccess'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页控制
|
||||||
|
const currentPageDisplay = computed(() => currentPage.value)
|
||||||
|
const totalPages = computed(() => Math.max(1, totalCount.value))
|
||||||
|
const canGoPrev = computed(() => currentPage.value > 1)
|
||||||
|
const canGoNext = computed(() => currentPage.value < totalPages.value)
|
||||||
|
|
||||||
|
const prevPage = async () => {
|
||||||
|
if (canGoPrev.value) {
|
||||||
|
currentPage.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextPage = async () => {
|
||||||
|
if (canGoNext.value) {
|
||||||
|
currentPage.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听页面变化
|
||||||
|
watch(currentPage, () => {
|
||||||
|
fetchMails()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await api.getSettings()
|
||||||
|
await fetchMails()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="center">
|
||||||
|
<div v-if="!settings.address">
|
||||||
|
<n-card :bordered="false" embedded>
|
||||||
|
<Login />
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<n-card :bordered="false" embedded>
|
||||||
|
<div style="text-align: center; margin-bottom: 16px; font-size: 18px;">
|
||||||
|
<n-text strong size="large">{{ settings.address }}</n-text>
|
||||||
|
</div>
|
||||||
|
<n-flex justify="center">
|
||||||
|
<n-button @click="refreshMails" :loading="loading" type="primary" tertiary size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<RefreshFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('refreshMails') }}
|
||||||
|
</n-button>
|
||||||
|
<n-button @click="copyAddress" tertiary size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<ContentCopyFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('copyAddress') }}
|
||||||
|
</n-button>
|
||||||
|
<n-button @click="useSimpleIndex = false" tertiary size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<ExitToAppFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('exitSimpleIndex') }}
|
||||||
|
</n-button>
|
||||||
|
<n-button @click="showAccountSettingsCard = true" tertiary size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<SettingsFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('accountSettings') }}
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<!-- 账户设置卡片 -->
|
||||||
|
<n-card v-if="showAccountSettingsCard" :bordered="false" embedded closable
|
||||||
|
@close="showAccountSettingsCard = false" :title="t('accountSettings')">
|
||||||
|
<AccountSettings />
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<n-card :bordered="false" embedded style="text-align: left;">
|
||||||
|
|
||||||
|
<div v-if="totalCount > 1">
|
||||||
|
<n-flex justify="space-between">
|
||||||
|
<n-button @click="prevPage" :disabled="!canGoPrev" text size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<ArrowBackIosNewFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('prevPage') }}
|
||||||
|
</n-button>
|
||||||
|
<n-text size="small">
|
||||||
|
{{ t('mailCount', { current: currentPageDisplay, total: totalCount }) }}
|
||||||
|
</n-text>
|
||||||
|
<n-button @click="nextPage" :disabled="!canGoNext" text size="small" icon-placement="right">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<ArrowForwardIosFilled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
{{ t('nextPage') }}
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!currentMail" class="no-mail">
|
||||||
|
<n-empty :description="t('noMails')" />
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<h3 v-if="currentMail.subject">{{ currentMail.subject }}</h3>
|
||||||
|
|
||||||
|
<n-space>
|
||||||
|
<n-tag type="info">
|
||||||
|
ID: {{ currentMail.id }}
|
||||||
|
</n-tag>
|
||||||
|
<n-tag type="info">
|
||||||
|
{{ utcToLocalDate(currentMail.created_at, useUTCDate.value) }}
|
||||||
|
</n-tag>
|
||||||
|
<n-tag type="info">
|
||||||
|
FROM: {{ currentMail.source }}
|
||||||
|
</n-tag>
|
||||||
|
</n-space>
|
||||||
|
|
||||||
|
<div style="margin-top: 16px;">
|
||||||
|
<ShadowHtmlComponent v-if="currentMail.message" :htmlContent="currentMail.message" />
|
||||||
|
<pre v-else>{{ currentMail.text }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<n-modal v-model:show="showAddressCredential" preset="dialog" :title="t('addressCredential')">
|
||||||
|
<span>
|
||||||
|
<p>{{ t("addressCredentialTip") }}</p>
|
||||||
|
</span>
|
||||||
|
<n-card embedded>
|
||||||
|
<b>{{ jwt }}</b>
|
||||||
|
</n-card>
|
||||||
|
</n-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.center {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-card {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-email-pages",
|
"name": "temp-email-pages",
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.20.4"
|
"wrangler": "^4.28.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-mail-docs",
|
"name": "temp-mail-docs",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.3",
|
"@types/node": "^24.2.0",
|
||||||
"vitepress": "^1.6.3",
|
"vitepress": "^1.6.4",
|
||||||
"wrangler": "^4.20.4"
|
"wrangler": "^4.28.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vitepress dev docs",
|
"dev": "vitepress dev docs",
|
||||||
|
|||||||
Generated
+445
-427
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,26 +11,26 @@
|
|||||||
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20250620.0",
|
"@cloudflare/workers-types": "^4.20250807.0",
|
||||||
"@eslint/js": "9.18.0",
|
"@eslint/js": "9.18.0",
|
||||||
"@simplewebauthn/types": "10.0.0",
|
"@simplewebauthn/types": "10.0.0",
|
||||||
"@types/node": "^22.15.32",
|
"@types/node": "^22.17.0",
|
||||||
"eslint": "9.18.0",
|
"eslint": "9.18.0",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"typescript-eslint": "^8.34.1",
|
"typescript-eslint": "^8.39.0",
|
||||||
"wrangler": "^4.20.4"
|
"wrangler": "^4.28.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.832.0",
|
"@aws-sdk/client-s3": "^3.863.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.832.0",
|
"@aws-sdk/s3-request-presigner": "^3.863.0",
|
||||||
"@simplewebauthn/server": "10.0.1",
|
"@simplewebauthn/server": "10.0.1",
|
||||||
"hono": "^4.8.1",
|
"hono": "^4.9.0",
|
||||||
"jsonpath-plus": "^10.3.0",
|
"jsonpath-plus": "^10.3.0",
|
||||||
"mimetext": "^3.0.27",
|
"mimetext": "^3.0.27",
|
||||||
"postal-mime": "^2.4.3",
|
"postal-mime": "^2.4.4",
|
||||||
"resend": "^4.6.0",
|
"resend": "^4.8.0",
|
||||||
"telegraf": "4.16.3",
|
"telegraf": "4.16.3",
|
||||||
"worker-mailer": "^1.1.4"
|
"worker-mailer": "^1.1.5"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/lib/core/network/client.js b/lib/core/network/client.js
|
diff --git a/lib/core/network/client.js b/lib/core/network/client.js
|
||||||
index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..209d4a6bf05352f44eeb082eb327581d698de5ce 100644
|
index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..ab82c5391596ee2ed8dbe4128f3ba57a8cc998ca 100644
|
||||||
--- a/lib/core/network/client.js
|
--- a/lib/core/network/client.js
|
||||||
+++ b/lib/core/network/client.js
|
+++ b/lib/core/network/client.js
|
||||||
@@ -1,18 +1,18 @@
|
@@ -1,18 +1,18 @@
|
||||||
@@ -37,7 +37,17 @@ index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..209d4a6bf05352f44eeb082eb327581d
|
|||||||
const path = __importStar(require("path"));
|
const path = __importStar(require("path"));
|
||||||
const node_fetch_1 = __importDefault(require("node-fetch"));
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
||||||
const check_1 = require("../helpers/check");
|
const check_1 = require("../helpers/check");
|
||||||
@@ -61,10 +61,10 @@ const DEFAULT_OPTIONS = {
|
@@ -39,7 +39,8 @@ const multipart_stream_1 = __importDefault(require("./multipart-stream"));
|
||||||
|
const error_1 = __importDefault(require("./error"));
|
||||||
|
const url_1 = require("url");
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
-const debug = require('debug')('telegraf:client');
|
||||||
|
+// const debug = require('debug')('telegraf:client');
|
||||||
|
+const debug = (msg) => console.debug(`[telegraf:client] ${msg}`);
|
||||||
|
const { isStream } = multipart_stream_1.default;
|
||||||
|
const WEBHOOK_REPLY_METHOD_ALLOWLIST = new Set([
|
||||||
|
'answerCallbackQuery',
|
||||||
|
@@ -61,10 +62,10 @@ const DEFAULT_OPTIONS = {
|
||||||
apiRoot: 'https://api.telegram.org',
|
apiRoot: 'https://api.telegram.org',
|
||||||
apiMode: 'bot',
|
apiMode: 'bot',
|
||||||
webhookReply: true,
|
webhookReply: true,
|
||||||
@@ -52,7 +62,7 @@ index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..209d4a6bf05352f44eeb082eb327581d
|
|||||||
attachmentAgent: undefined,
|
attachmentAgent: undefined,
|
||||||
testEnv: false,
|
testEnv: false,
|
||||||
};
|
};
|
||||||
@@ -112,9 +112,9 @@ async function buildFormDataConfig(payload, agent) {
|
@@ -112,9 +113,9 @@ async function buildFormDataConfig(payload, agent) {
|
||||||
}
|
}
|
||||||
const boundary = crypto.randomBytes(32).toString('hex');
|
const boundary = crypto.randomBytes(32).toString('hex');
|
||||||
const formData = new multipart_stream_1.default(boundary);
|
const formData = new multipart_stream_1.default(boundary);
|
||||||
@@ -65,7 +75,7 @@ index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..209d4a6bf05352f44eeb082eb327581d
|
|||||||
return {
|
return {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
compress: true,
|
compress: true,
|
||||||
@@ -205,14 +205,15 @@ async function attachFormMedia(form, media, id, agent) {
|
@@ -205,14 +206,15 @@ async function attachFormMedia(form, media, id, agent) {
|
||||||
if ('source' in media && media.source) {
|
if ('source' in media && media.source) {
|
||||||
let mediaSource = media.source;
|
let mediaSource = media.source;
|
||||||
if (typeof media.source === 'string') {
|
if (typeof media.source === 'string') {
|
||||||
|
|||||||
Generated
+852
-840
File diff suppressed because it is too large
Load Diff
+20
-2
@@ -40,6 +40,23 @@ const getNameRegex = (c: Context<HonoCustomType>): RegExp => {
|
|||||||
return DEFAULT_NAME_REGEX;
|
return DEFAULT_NAME_REGEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateAddressUpdatedAt(
|
||||||
|
c: Context<HonoCustomType>,
|
||||||
|
address: string | undefined | null
|
||||||
|
): Promise<void> {
|
||||||
|
if (!address) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// update address updated_at
|
||||||
|
try {
|
||||||
|
await c.env.DB.prepare(
|
||||||
|
`UPDATE address SET updated_at = datetime('now') where name = ?`
|
||||||
|
).bind(address).run();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to update address updated_at", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const newAddress = async (
|
export const newAddress = async (
|
||||||
c: Context<HonoCustomType>,
|
c: Context<HonoCustomType>,
|
||||||
{
|
{
|
||||||
@@ -91,9 +108,9 @@ export const newAddress = async (
|
|||||||
}
|
}
|
||||||
// check domain
|
// check domain
|
||||||
const allowDomains = checkAllowDomains ? await getAllowDomains(c) : getDomains(c);
|
const allowDomains = checkAllowDomains ? await getAllowDomains(c) : getDomains(c);
|
||||||
// if domain is not set, use the first domain
|
// if domain is not set, use the random domain
|
||||||
if (!domain && allowDomains.length > 0) {
|
if (!domain && allowDomains.length > 0) {
|
||||||
domain = allowDomains[0];
|
domain = allowDomains[Math.floor(Math.random() * allowDomains.length)];
|
||||||
}
|
}
|
||||||
// check domain is valid
|
// check domain is valid
|
||||||
if (!domain || !allowDomains.includes(domain)) {
|
if (!domain || !allowDomains.includes(domain)) {
|
||||||
@@ -108,6 +125,7 @@ export const newAddress = async (
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error("Failed to create address")
|
throw new Error("Failed to create address")
|
||||||
}
|
}
|
||||||
|
await updateAddressUpdatedAt(c, name);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = (e as Error).message;
|
const message = (e as Error).message;
|
||||||
if (message && message.includes("UNIQUE")) {
|
if (message && message.includes("UNIQUE")) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VERSION: 'v' + '1.0.0',
|
VERSION: 'v' + '1.0.3',
|
||||||
|
|
||||||
// DB Version
|
// DB Version
|
||||||
DB_VERSION_KEY: 'db_version',
|
DB_VERSION_KEY: 'db_version',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Context, Hono } from 'hono'
|
|||||||
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue, getSplitStringListValue } from '../utils';
|
import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue, getSplitStringListValue } from '../utils';
|
||||||
import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains } from '../common'
|
import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains, updateAddressUpdatedAt } from '../common'
|
||||||
import { CONSTANTS } from '../constants'
|
import { CONSTANTS } from '../constants'
|
||||||
import auto_reply from './auto_reply'
|
import auto_reply from './auto_reply'
|
||||||
import webhook_settings from './webhook_settings';
|
import webhook_settings from './webhook_settings';
|
||||||
@@ -20,27 +20,6 @@ api.post('/api/attachment/delete', s3_attachment.deleteKey)
|
|||||||
api.post('/api/attachment/put_url', s3_attachment.getSignedPutUrl)
|
api.post('/api/attachment/put_url', s3_attachment.getSignedPutUrl)
|
||||||
api.post('/api/attachment/get_url', s3_attachment.getSignedGetUrl)
|
api.post('/api/attachment/get_url', s3_attachment.getSignedGetUrl)
|
||||||
|
|
||||||
|
|
||||||
export async function updateAddressUpdatedAt(
|
|
||||||
c: Context<HonoCustomType>,
|
|
||||||
address: string | undefined | null
|
|
||||||
): Promise<void> {
|
|
||||||
if (!address) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// update address updated_at
|
|
||||||
try {
|
|
||||||
if (address) {
|
|
||||||
await c.env.DB.prepare(
|
|
||||||
`UPDATE address SET updated_at = datetime('now') where name = ?`
|
|
||||||
).bind(address).run();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to update address updated_at")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
api.get('/api/mails', async (c) => {
|
api.get('/api/mails', async (c) => {
|
||||||
const { address } = c.get("jwtPayload")
|
const { address } = c.get("jwtPayload")
|
||||||
if (!address) {
|
if (!address) {
|
||||||
|
|||||||
@@ -51,6 +51,14 @@ export const jwtListToAddressData = async (
|
|||||||
for (const jwt of jwtList) {
|
for (const jwt of jwtList) {
|
||||||
try {
|
try {
|
||||||
const { address, address_id } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
|
const { address, address_id } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
|
||||||
|
const name = await c.env.DB.prepare(
|
||||||
|
`SELECT name FROM address WHERE id = ? `
|
||||||
|
).bind(address_id).first("name");
|
||||||
|
if (!name) {
|
||||||
|
addressList.push("无效地址");
|
||||||
|
invalidJwtList.push(jwt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
addressList.push(address as string);
|
addressList.push(address as string);
|
||||||
addressIdMap[address as string] = address_id as number;
|
addressIdMap[address as string] = address_id as number;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -75,7 +83,7 @@ export const bindTelegramAddress = async (
|
|||||||
return address as string;
|
return address as string;
|
||||||
}
|
}
|
||||||
if (jwtList.length >= getIntValue(c.env.TG_MAX_ADDRESS, 5)) {
|
if (jwtList.length >= getIntValue(c.env.TG_MAX_ADDRESS, 5)) {
|
||||||
throw Error("绑定地址数量已达上限");
|
throw Error("绑定地址数量已达上限, 请先 /cleaninvalidaddress");
|
||||||
}
|
}
|
||||||
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, JSON.stringify([...jwtList, jwt]));
|
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, JSON.stringify([...jwtList, jwt]));
|
||||||
// for mail push to telegram
|
// for mail push to telegram
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ const COMMANDS = [
|
|||||||
description: "查看邮件, 请输入 /mails <邮箱地址>, 不输入地址默认查看第一个地址"
|
description: "查看邮件, 请输入 /mails <邮箱地址>, 不输入地址默认查看第一个地址"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: "cleaninvalidadress",
|
command: "cleaninvalidaddress",
|
||||||
description: "清理无效地址, 请输入 /cleaninvalidadress"
|
description: "清理无效地址, 请输入 /cleaninvalidaddress"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ export function newTelegramBot(c: Context<HonoCustomType>, token: string): Teleg
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const address = ctx?.message?.text.slice("/unbind".length).trim();
|
const address = ctx?.message?.text.slice("/delete".length).trim();
|
||||||
if (!address) {
|
if (!address) {
|
||||||
return await ctx.reply("请输入地址");
|
return await ctx.reply("请输入地址");
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ export function newTelegramBot(c: Context<HonoCustomType>, token: string): Teleg
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.command("cleaninvalidadress", async (ctx: TgContext) => {
|
bot.command("cleaninvalidaddress", async (ctx: TgContext) => {
|
||||||
const userId = ctx?.message?.from?.id;
|
const userId = ctx?.message?.from?.id;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return await ctx.reply("无法获取用户信息");
|
return await ctx.reply("无法获取用户信息");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { getJsonSetting } from "../utils"
|
|||||||
import { CONSTANTS } from "../constants";
|
import { CONSTANTS } from "../constants";
|
||||||
import { unbindTelegramByAddress } from '../telegram_api/common';
|
import { unbindTelegramByAddress } from '../telegram_api/common';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
|
import { updateAddressUpdatedAt } from '../common';
|
||||||
|
|
||||||
const UserBindAddressModule = {
|
const UserBindAddressModule = {
|
||||||
bind: async (c: Context<HonoCustomType>) => {
|
bind: async (c: Context<HonoCustomType>) => {
|
||||||
@@ -237,6 +238,7 @@ const UserBindAddressModule = {
|
|||||||
if (!newAddressSuccess) {
|
if (!newAddressSuccess) {
|
||||||
throw new Error("Failed to create address")
|
throw new Error("Failed to create address")
|
||||||
}
|
}
|
||||||
|
await updateAddressUpdatedAt(c, address);
|
||||||
// find new address id
|
// find new address id
|
||||||
const new_address_id = await c.env.DB.prepare(
|
const new_address_id = await c.env.DB.prepare(
|
||||||
`SELECT id FROM address WHERE name = ?`
|
`SELECT id FROM address WHERE name = ?`
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Context } from 'hono';
|
|||||||
import { Jwt } from 'hono/utils/jwt'
|
import { Jwt } from 'hono/utils/jwt'
|
||||||
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { getJsonSetting } from '../utils';
|
import { getJsonSetting, getStringValue, getUserRoles } from '../utils';
|
||||||
import { UserOauth2Settings } from '../models';
|
import { UserOauth2Settings } from '../models';
|
||||||
import { CONSTANTS } from '../constants';
|
import { CONSTANTS } from '../constants';
|
||||||
|
|
||||||
@@ -110,6 +110,23 @@ export default {
|
|||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
return c.text(msgs.UserNotFoundMsg, 400)
|
return c.text(msgs.UserNotFoundMsg, 400)
|
||||||
}
|
}
|
||||||
|
// process user roles
|
||||||
|
const defaultRole = getStringValue(c.env.USER_DEFAULT_ROLE);
|
||||||
|
if (defaultRole) {
|
||||||
|
const user_roles = getUserRoles(c);
|
||||||
|
if (!user_roles.find((r) => r.role === defaultRole)) {
|
||||||
|
return c.text(msgs.InvalidUserDefaultRoleMsg, 500);
|
||||||
|
}
|
||||||
|
// update user roles
|
||||||
|
const { success: success2 } = await c.env.DB.prepare(
|
||||||
|
`INSERT INTO user_roles (user_id, role_text)`
|
||||||
|
+ ` VALUES (?, ?)`
|
||||||
|
+ ` ON CONFLICT(user_id) DO NOTHING`
|
||||||
|
).bind(user_id, defaultRole).run();
|
||||||
|
if (!success2) {
|
||||||
|
return c.text(msgs.FailedUpdateUserDefaultRoleMsg, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
// create jwt
|
// create jwt
|
||||||
const jwt = await Jwt.sign({
|
const jwt = await Jwt.sign({
|
||||||
user_email: email,
|
user_email: email,
|
||||||
|
|||||||
Reference in New Issue
Block a user