mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-08 17:08:59 +08:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce937fc4a9 | ||
|
|
5162f0a478 | ||
|
|
e06b7ccbb2 | ||
|
|
24016a0051 | ||
|
|
3f1d800e90 | ||
|
|
acd9a8f099 | ||
|
|
148fb09754 | ||
|
|
5951998aad | ||
|
|
01aa97e384 | ||
|
|
70b30c2494 | ||
|
|
1a1dd720c8 | ||
|
|
2d501d82cf | ||
|
|
7c57592742 | ||
|
|
41105ed803 | ||
|
|
c924b71a5c | ||
|
|
4f0b44de2e | ||
|
|
f6fcbe793c |
@@ -28,6 +28,7 @@ Upgrade the version number of the cloudflare_temp_email project.
|
|||||||
4. Update the `VERSION` constant in `worker/src/constants.ts`.
|
4. Update the `VERSION` constant in `worker/src/constants.ts`.
|
||||||
5. Insert a new version placeholder at the top of `CHANGELOG.md`.
|
5. Insert a new version placeholder at the top of `CHANGELOG.md`.
|
||||||
6. Insert a new version placeholder at the top of `CHANGELOG_EN.md`.
|
6. Insert a new version placeholder at the top of `CHANGELOG_EN.md`.
|
||||||
|
7. Rename the previous version heading in both changelogs from `## v{OLD_VERSION}(main)` to `## v{OLD_VERSION}` so only the new active development version keeps `(main)`.
|
||||||
|
|
||||||
## CHANGELOG format
|
## CHANGELOG format
|
||||||
|
|
||||||
@@ -44,7 +45,14 @@ In `CHANGELOG.md`, insert before the existing `## v{OLD_VERSION}(main)` line (i.
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`CHANGELOG_EN.md` uses the same format.
|
After inserting the new placeholder, update the old heading:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
-## v{OLD_VERSION}(main)
|
||||||
|
+## v{OLD_VERSION}
|
||||||
|
```
|
||||||
|
|
||||||
|
`CHANGELOG_EN.md` uses the same format and must receive the same old-heading update.
|
||||||
|
|
||||||
## Commit message format
|
## Commit message format
|
||||||
|
|
||||||
@@ -53,4 +61,5 @@ feat: upgrade version to v{VERSION}
|
|||||||
|
|
||||||
- Update version number to {VERSION} in all package.json files
|
- Update version number to {VERSION} in all package.json files
|
||||||
- Add v{VERSION} placeholder in CHANGELOG.md
|
- Add v{VERSION} placeholder in CHANGELOG.md
|
||||||
|
- Move (main) marker from v{OLD_VERSION} to v{VERSION}
|
||||||
```
|
```
|
||||||
|
|||||||
+15
-1
@@ -6,7 +6,21 @@
|
|||||||
<a href="CHANGELOG_EN.md">English</a>
|
<a href="CHANGELOG_EN.md">English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## v1.9.0(main)
|
## v1.10.0(main)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- feat: |Frontend| 外观设置新增“自动加载远程内容”开关,默认关闭;关闭后邮件 HTML 中的远程图片默认以占位图显示,并支持单封邮件临时加载远程内容(issue #1073)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- fix: |AI 提取| 强化提示词,要求 AI 保持邮件原始链接域名,避免小模型改写验证链接域名导致错误跳转(issue #1072)
|
||||||
|
- fix: |AI 提取| HTML-only 邮件在发送给 Workers AI 前会先压缩为可读文本,避免样式模板过长导致验证码位于 4000 字截断之后而无法识别
|
||||||
|
- fix: |Frontend| 移动端 Header 增加页头内边距,避免标题、菜单按钮与屏幕边缘过近
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
## v1.9.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -6,7 +6,21 @@
|
|||||||
<a href="CHANGELOG_EN.md">English</a>
|
<a href="CHANGELOG_EN.md">English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## v1.9.0(main)
|
## v1.10.0(main)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- feat: |Frontend| Add an Appearance toggle for automatically loading remote content, disabled by default; when disabled, HTML mail shows blocked-image placeholders by default and supports temporarily loading remote content for a single mail (issue #1073)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- fix: |AI Extract| Strengthen the prompt to keep original link domains from the email, preventing small models from rewriting verification-link domains (issue #1072)
|
||||||
|
- fix: |AI Extract| Convert HTML-only mail bodies into compact readable text before sending them to Workers AI, preventing long templates from pushing verification codes past the 4000-character truncation window
|
||||||
|
- fix: |Frontend| Add mobile Header page padding so the title and menu button no longer sit too close to the screen edge
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
## v1.9.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|||||||
Generated
+26
-50
@@ -6,8 +6,8 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "cloudflare-temp-email-e2e",
|
"name": "cloudflare-temp-email-e2e",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imapflow": "^1.3.1",
|
"imapflow": "^1.4.2",
|
||||||
"nodemailer": "^8.0.5"
|
"nodemailer": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.58.2",
|
"@playwright/test": "1.58.2",
|
||||||
@@ -69,13 +69,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@zone-eu/mailsplit": {
|
"node_modules/@zone-eu/mailsplit": {
|
||||||
"version": "5.4.8",
|
"version": "5.4.12",
|
||||||
"resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.12.tgz",
|
||||||
"integrity": "sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==",
|
"integrity": "sha512-w7Gy+NvjZ0MiXm8F6zfjImAqcTONKDImgWVBjDKQVFUXWuz3VFM5levNArkL2M877ajql5+bkS2pDV56injlmg==",
|
||||||
"license": "(MIT OR EUPL-1.1+)",
|
"license": "(MIT OR EUPL-1.1+)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libmime": "5.3.7",
|
"libmime": "5.3.8",
|
||||||
"libqp": "2.1.1"
|
"libqp": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -129,38 +129,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/imapflow": {
|
"node_modules/imapflow": {
|
||||||
"version": "1.3.1",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.4.2.tgz",
|
||||||
"integrity": "sha512-DKwpMDR1EWXpV5T7adqQAccN7n684AX3poEZ5F3YoPlm2MyGeKavpRgNr3qptdEQaK+x5SlZ9jigT+cMs4geBA==",
|
"integrity": "sha512-73CGfb5+W0FkZ5CY4GfSdsoXyQ+17wdKkpMN2vwJHdLtOOFQWxv0ilG7KYY79XHBYg5njjqxXYB2FPw5Tl81zQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zone-eu/mailsplit": "5.4.8",
|
"@zone-eu/mailsplit": "5.4.12",
|
||||||
"encoding-japanese": "2.2.0",
|
"encoding-japanese": "2.2.0",
|
||||||
"iconv-lite": "0.7.2",
|
"iconv-lite": "0.7.2",
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libmime": "5.3.8",
|
"libmime": "5.3.8",
|
||||||
"libqp": "2.1.1",
|
"libqp": "2.1.1",
|
||||||
"nodemailer": "8.0.5",
|
"nodemailer": "9.0.1",
|
||||||
"pino": "10.3.1",
|
"pino": "10.3.1",
|
||||||
"socks": "2.8.7"
|
"socks": "2.8.9"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/imapflow/node_modules/libmime": {
|
|
||||||
"version": "5.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.8.tgz",
|
|
||||||
"integrity": "sha512-ZrCY+Q66mPvasAfjsQ/IgahzoBvfE1VdtGRpo1hwRB1oK3wJKxhKA3GOcd2a6j7AH5eMFccxK9fBoCpRZTf8ng==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"encoding-japanese": "2.2.0",
|
|
||||||
"iconv-lite": "0.7.2",
|
|
||||||
"libbase64": "1.3.0",
|
|
||||||
"libqp": "2.1.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ip-address": {
|
"node_modules/ip-address": {
|
||||||
"version": "10.1.0",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
|
||||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
@@ -173,29 +161,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/libmime": {
|
"node_modules/libmime": {
|
||||||
"version": "5.3.7",
|
"version": "5.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.8.tgz",
|
||||||
"integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==",
|
"integrity": "sha512-ZrCY+Q66mPvasAfjsQ/IgahzoBvfE1VdtGRpo1hwRB1oK3wJKxhKA3GOcd2a6j7AH5eMFccxK9fBoCpRZTf8ng==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encoding-japanese": "2.2.0",
|
"encoding-japanese": "2.2.0",
|
||||||
"iconv-lite": "0.6.3",
|
"iconv-lite": "0.7.2",
|
||||||
"libbase64": "1.3.0",
|
"libbase64": "1.3.0",
|
||||||
"libqp": "2.1.1"
|
"libqp": "2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/libmime/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/libqp": {
|
"node_modules/libqp": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
|
||||||
@@ -203,9 +179,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "8.0.5",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz",
|
||||||
"integrity": "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==",
|
"integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==",
|
||||||
"license": "MIT-0",
|
"license": "MIT-0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -346,12 +322,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socks": {
|
"node_modules/socks": {
|
||||||
"version": "2.8.7",
|
"version": "2.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
|
||||||
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
"integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ip-address": "^10.0.1",
|
"ip-address": "^10.1.1",
|
||||||
"smart-buffer": "^4.2.0"
|
"smart-buffer": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
+2
-2
@@ -13,7 +13,7 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imapflow": "^1.3.1",
|
"imapflow": "^1.4.2",
|
||||||
"nodemailer": "^8.0.5"
|
"nodemailer": "^9.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"@vueuse/core": "^14.3.0",
|
"@vueuse/core": "^14.3.0",
|
||||||
"@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.16.1",
|
"axios": "^1.18.1",
|
||||||
"dompurify": "^3.4.7",
|
"dompurify": "^3.4.11",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mail-parser-wasm": "^0.2.2",
|
"mail-parser-wasm": "^0.2.2",
|
||||||
"naive-ui": "^2.44.1",
|
"naive-ui": "^2.44.1",
|
||||||
"postal-mime": "^2.7.4",
|
"postal-mime": "^2.7.4",
|
||||||
"vooks": "^0.2.12",
|
"vooks": "^0.2.12",
|
||||||
"vue": "^3.5.35",
|
"vue": "^3.5.38",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-i18n": "^11.4.4",
|
"vue-i18n": "^11.4.6",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -50,10 +50,10 @@
|
|||||||
"vite": "^7.3.5",
|
"vite": "^7.3.5",
|
||||||
"vite-plugin-pwa": "^1.3.0",
|
"vite-plugin-pwa": "^1.3.0",
|
||||||
"vite-plugin-wasm": "^3.6.0",
|
"vite-plugin-wasm": "^3.6.0",
|
||||||
"vitest": "^3.2.6",
|
"vitest": "^4.1.9",
|
||||||
"workbox-build": "^7.4.1",
|
"workbox-build": "^7.4.1",
|
||||||
"workbox-window": "^7.4.1",
|
"workbox-window": "^7.4.1",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+728
-772
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { useScopedI18n } from '@/i18n/app'
|
import { useScopedI18n } from '@/i18n/app'
|
||||||
import { CloudDownloadRound, ReplyFilled, ForwardFilled, FullscreenRound } from '@vicons/material'
|
import { CloudDownloadRound, ReplyFilled, ForwardFilled, FullscreenRound } from '@vicons/material'
|
||||||
import ShadowHtmlComponent from "./ShadowHtmlComponent.vue";
|
import ShadowHtmlComponent from "./ShadowHtmlComponent.vue";
|
||||||
@@ -8,10 +8,39 @@ import { getDownloadEmlUrl } from '../utils/email-parser';
|
|||||||
import { utcToLocalDate } from '../utils';
|
import { utcToLocalDate } from '../utils';
|
||||||
import { useGlobalState } from '../store';
|
import { useGlobalState } from '../store';
|
||||||
|
|
||||||
const { preferShowTextMail, useIframeShowMail, useUTCDate, isDark } = useGlobalState();
|
const { preferShowTextMail, useIframeShowMail, useUTCDate, isDark, autoLoadRemoteContent } = useGlobalState();
|
||||||
|
|
||||||
const { t } = useScopedI18n('components.MailContentRenderer')
|
const { t } = useScopedI18n('components.MailContentRenderer')
|
||||||
|
|
||||||
|
const blockedRemoteContentPlaceholder = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="80" viewBox="0 0 320 80">
|
||||||
|
<rect width="320" height="80" rx="8" fill="#f3f4f6"/>
|
||||||
|
<text x="160" y="45" text-anchor="middle" font-family="sans-serif" font-size="14" fill="#6b7280">Remote content blocked</text>
|
||||||
|
</svg>
|
||||||
|
`)}`;
|
||||||
|
|
||||||
|
const hasRemoteUrl = (value: string | null) => /(?:^|,)\s*(?:https?:)?\/\//i.test(value || '');
|
||||||
|
|
||||||
|
const blockRemoteImages = (html?: string | null) => {
|
||||||
|
if (!html || typeof DOMParser !== 'function') return html || '';
|
||||||
|
|
||||||
|
const hasDocumentShell = /<!doctype|<html[\s>]/i.test(html);
|
||||||
|
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
for (const image of doc.querySelectorAll('img')) {
|
||||||
|
const src = image.getAttribute('src');
|
||||||
|
const srcset = image.getAttribute('srcset');
|
||||||
|
if (!hasRemoteUrl(src) && !hasRemoteUrl(srcset)) continue;
|
||||||
|
|
||||||
|
if (src) image.setAttribute('data-blocked-src', src);
|
||||||
|
if (srcset) image.setAttribute('data-blocked-srcset', srcset);
|
||||||
|
image.removeAttribute('srcset');
|
||||||
|
image.setAttribute('src', blockedRemoteContentPlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasDocumentShell ? `<!doctype html>\n${doc.documentElement.outerHTML}` : doc.body.innerHTML;
|
||||||
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
mail: {
|
mail: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -57,6 +86,15 @@ const showAttachments = ref(false);
|
|||||||
const curAttachments = ref([]);
|
const curAttachments = ref([]);
|
||||||
const attachmentLoding = ref(false);
|
const attachmentLoding = ref(false);
|
||||||
const showFullscreen = ref(false);
|
const showFullscreen = ref(false);
|
||||||
|
const loadRemoteContentForCurrentMail = ref(false);
|
||||||
|
const shouldLoadRemoteContent = computed(() => autoLoadRemoteContent.value || loadRemoteContentForCurrentMail.value);
|
||||||
|
const mailHtmlContent = computed(() => shouldLoadRemoteContent.value
|
||||||
|
? (props.mail.message || '')
|
||||||
|
: blockRemoteImages(props.mail.message));
|
||||||
|
|
||||||
|
watch(() => props.mail.id, () => {
|
||||||
|
loadRemoteContentForCurrentMail.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
props.onDelete();
|
props.onDelete();
|
||||||
@@ -149,6 +187,12 @@ const handleSaveToS3 = async (filename, blob) => {
|
|||||||
</template>
|
</template>
|
||||||
{{ t('fullscreen') }}
|
{{ t('fullscreen') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
|
|
||||||
|
<n-button v-if="!showTextMail && !shouldLoadRemoteContent" size="small" tertiary type="info"
|
||||||
|
@click="loadRemoteContentForCurrentMail = true">
|
||||||
|
{{ t('loadRemoteContent') }}
|
||||||
|
</n-button>
|
||||||
|
|
||||||
</n-space>
|
</n-space>
|
||||||
|
|
||||||
<!-- AI 提取信息 -->
|
<!-- AI 提取信息 -->
|
||||||
@@ -157,9 +201,9 @@ const handleSaveToS3 = async (filename, blob) => {
|
|||||||
<!-- 邮件内容 -->
|
<!-- 邮件内容 -->
|
||||||
<div class="mail-content" :class="{ 'dark-mode': isDark }">
|
<div class="mail-content" :class="{ 'dark-mode': isDark }">
|
||||||
<pre v-if="showTextMail" class="mail-text">{{ mail.text }}</pre>
|
<pre v-if="showTextMail" class="mail-text">{{ mail.text }}</pre>
|
||||||
<iframe v-else-if="useIframeShowMail" :srcdoc="mail.message" class="mail-iframe">
|
<iframe v-else-if="useIframeShowMail" :srcdoc="mailHtmlContent" class="mail-iframe">
|
||||||
</iframe>
|
</iframe>
|
||||||
<ShadowHtmlComponent v-else :key="mail.id" :htmlContent="mail.message" :isDark="isDark" class="mail-html" />
|
<ShadowHtmlComponent v-else :key="mail.id" :htmlContent="mailHtmlContent" :isDark="isDark" class="mail-html" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -168,9 +212,9 @@ const handleSaveToS3 = async (filename, blob) => {
|
|||||||
<n-drawer-content :title="mail.subject" closable>
|
<n-drawer-content :title="mail.subject" closable>
|
||||||
<div class="fullscreen-mail-content" :class="{ 'dark-mode': isDark }">
|
<div class="fullscreen-mail-content" :class="{ 'dark-mode': isDark }">
|
||||||
<pre v-if="showTextMail" class="mail-text">{{ mail.text }}</pre>
|
<pre v-if="showTextMail" class="mail-text">{{ mail.text }}</pre>
|
||||||
<iframe v-else-if="useIframeShowMail" :srcdoc="mail.message" class="mail-iframe">
|
<iframe v-else-if="useIframeShowMail" :srcdoc="mailHtmlContent" class="mail-iframe">
|
||||||
</iframe>
|
</iframe>
|
||||||
<ShadowHtmlComponent v-else :key="mail.id" :htmlContent="mail.message" :isDark="isDark" class="mail-html" />
|
<ShadowHtmlComponent v-else :key="mail.id" :htmlContent="mailHtmlContent" :isDark="isDark" class="mail-html" />
|
||||||
</div>
|
</div>
|
||||||
</n-drawer-content>
|
</n-drawer-content>
|
||||||
</n-drawer>
|
</n-drawer>
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export const deMessages = {
|
|||||||
"views.admin.SenderAccess.disable": "Deaktivieren",
|
"views.admin.SenderAccess.disable": "Deaktivieren",
|
||||||
"views.Admin.loginViaDisabledCheck": "Passwortprüfung deaktiviert",
|
"views.Admin.loginViaDisabledCheck": "Passwortprüfung deaktiviert",
|
||||||
"views.common.Appearance.preferShowTextMail": "Text-Mail standardmäßig anzeigen",
|
"views.common.Appearance.preferShowTextMail": "Text-Mail standardmäßig anzeigen",
|
||||||
|
"views.common.Appearance.autoLoadRemoteContent": "Remote-Inhalte automatisch laden",
|
||||||
"views.admin.AccountSettings.domain_list": "Domain-Liste (optional)",
|
"views.admin.AccountSettings.domain_list": "Domain-Liste (optional)",
|
||||||
"views.admin.AccountSettings.source_patterns_tip": "Die Domain-Liste filtert nach Empfängeradresse, die Quell-Regex nach Absenderadresse. Für die Weiterleitung müssen beide Bedingungen erfüllt sein (UND-Logik). Leer lassen, um den jeweiligen Filter zu überspringen.",
|
"views.admin.AccountSettings.source_patterns_tip": "Die Domain-Liste filtert nach Empfängeradresse, die Quell-Regex nach Absenderadresse. Für die Weiterleitung müssen beide Bedingungen erfüllt sein (UND-Logik). Leer lassen, um den jeweiligen Filter zu überspringen.",
|
||||||
"views.admin.UserManagement.domains": "Domains",
|
"views.admin.UserManagement.domains": "Domains",
|
||||||
@@ -237,6 +238,7 @@ export const deMessages = {
|
|||||||
"views.admin.AccountSettings.forward_address_required": "Eine Weiterleitungsadresse ist erforderlich",
|
"views.admin.AccountSettings.forward_address_required": "Eine Weiterleitungsadresse ist erforderlich",
|
||||||
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
||||||
"components.MailContentRenderer.fullscreen": "Vollbild",
|
"components.MailContentRenderer.fullscreen": "Vollbild",
|
||||||
|
"components.MailContentRenderer.loadRemoteContent": "Remote-Inhalte laden",
|
||||||
"views.common.Login.generateName": "Zufälligen Namen erzeugen",
|
"views.common.Login.generateName": "Zufälligen Namen erzeugen",
|
||||||
"views.admin.Telegram.globalMailPushList": "Globale Mail-Push-Chat-ID-Liste",
|
"views.admin.Telegram.globalMailPushList": "Globale Mail-Push-Chat-ID-Liste",
|
||||||
"views.common.Appearance.globalTabplacement": "Globale Tab-Position",
|
"views.common.Appearance.globalTabplacement": "Globale Tab-Position",
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export const esMessages = {
|
|||||||
"views.admin.SenderAccess.disable": "Deshabilitar",
|
"views.admin.SenderAccess.disable": "Deshabilitar",
|
||||||
"views.Admin.loginViaDisabledCheck": "Comprobación de contraseña deshabilitada",
|
"views.Admin.loginViaDisabledCheck": "Comprobación de contraseña deshabilitada",
|
||||||
"views.common.Appearance.preferShowTextMail": "Mostrar correo en texto por defecto",
|
"views.common.Appearance.preferShowTextMail": "Mostrar correo en texto por defecto",
|
||||||
|
"views.common.Appearance.autoLoadRemoteContent": "Cargar contenido remoto automáticamente",
|
||||||
"views.admin.AccountSettings.domain_list": "Lista de dominios (opcional)",
|
"views.admin.AccountSettings.domain_list": "Lista de dominios (opcional)",
|
||||||
"views.admin.AccountSettings.source_patterns_tip": "La lista de dominios filtra por destinatario y la regex de origen por remitente. Ambas condiciones deben cumplirse para reenviar (lógica AND). Deja alguna vacía para omitirla.",
|
"views.admin.AccountSettings.source_patterns_tip": "La lista de dominios filtra por destinatario y la regex de origen por remitente. Ambas condiciones deben cumplirse para reenviar (lógica AND). Deja alguna vacía para omitirla.",
|
||||||
"views.admin.UserManagement.domains": "Dominios",
|
"views.admin.UserManagement.domains": "Dominios",
|
||||||
@@ -237,6 +238,7 @@ export const esMessages = {
|
|||||||
"views.admin.AccountSettings.forward_address_required": "La dirección de reenvío es obligatoria",
|
"views.admin.AccountSettings.forward_address_required": "La dirección de reenvío es obligatoria",
|
||||||
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
||||||
"components.MailContentRenderer.fullscreen": "Pantalla completa",
|
"components.MailContentRenderer.fullscreen": "Pantalla completa",
|
||||||
|
"components.MailContentRenderer.loadRemoteContent": "Cargar contenido remoto",
|
||||||
"views.common.Login.generateName": "Generar nombre aleatorio",
|
"views.common.Login.generateName": "Generar nombre aleatorio",
|
||||||
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envío de correos",
|
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envío de correos",
|
||||||
"views.common.Appearance.globalTabplacement": "Posición global de pestañas",
|
"views.common.Appearance.globalTabplacement": "Posición global de pestañas",
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export const jaMessages = {
|
|||||||
"views.admin.SenderAccess.disable": "無効化",
|
"views.admin.SenderAccess.disable": "無効化",
|
||||||
"views.Admin.loginViaDisabledCheck": "パスワードチェックを無効化",
|
"views.Admin.loginViaDisabledCheck": "パスワードチェックを無効化",
|
||||||
"views.common.Appearance.preferShowTextMail": "既定でテキストメールを表示",
|
"views.common.Appearance.preferShowTextMail": "既定でテキストメールを表示",
|
||||||
|
"views.common.Appearance.autoLoadRemoteContent": "リモートコンテンツを自動で読み込む",
|
||||||
"views.admin.AccountSettings.domain_list": "ドメイン一覧(任意)",
|
"views.admin.AccountSettings.domain_list": "ドメイン一覧(任意)",
|
||||||
"views.admin.AccountSettings.source_patterns_tip": "ドメイン一覧は受信先アドレスで、送信元正規表現は送信者アドレスでフィルタします。転送には両方の条件を満たす必要があります(AND)。どちらかを空欄にするとその条件は無視されます。",
|
"views.admin.AccountSettings.source_patterns_tip": "ドメイン一覧は受信先アドレスで、送信元正規表現は送信者アドレスでフィルタします。転送には両方の条件を満たす必要があります(AND)。どちらかを空欄にするとその条件は無視されます。",
|
||||||
"views.admin.UserManagement.domains": "ドメイン",
|
"views.admin.UserManagement.domains": "ドメイン",
|
||||||
@@ -237,6 +238,7 @@ export const jaMessages = {
|
|||||||
"views.admin.AccountSettings.forward_address_required": "転送先アドレスは必須です",
|
"views.admin.AccountSettings.forward_address_required": "転送先アドレスは必須です",
|
||||||
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
||||||
"components.MailContentRenderer.fullscreen": "全画面",
|
"components.MailContentRenderer.fullscreen": "全画面",
|
||||||
|
"components.MailContentRenderer.loadRemoteContent": "リモートコンテンツを読み込む",
|
||||||
"views.common.Login.generateName": "ランダム名を生成",
|
"views.common.Login.generateName": "ランダム名を生成",
|
||||||
"views.admin.Telegram.globalMailPushList": "グローバルメール通知 Chat ID 一覧",
|
"views.admin.Telegram.globalMailPushList": "グローバルメール通知 Chat ID 一覧",
|
||||||
"views.common.Appearance.globalTabplacement": "全体タブ位置",
|
"views.common.Appearance.globalTabplacement": "全体タブ位置",
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export const ptBRMessages = {
|
|||||||
"views.admin.SenderAccess.disable": "Desativar",
|
"views.admin.SenderAccess.disable": "Desativar",
|
||||||
"views.Admin.loginViaDisabledCheck": "Verificação de senha desativada",
|
"views.Admin.loginViaDisabledCheck": "Verificação de senha desativada",
|
||||||
"views.common.Appearance.preferShowTextMail": "Exibir e-mail em texto por padrão",
|
"views.common.Appearance.preferShowTextMail": "Exibir e-mail em texto por padrão",
|
||||||
|
"views.common.Appearance.autoLoadRemoteContent": "Carregar conteúdo remoto automaticamente",
|
||||||
"views.admin.AccountSettings.domain_list": "Lista de domínios (opcional)",
|
"views.admin.AccountSettings.domain_list": "Lista de domínios (opcional)",
|
||||||
"views.admin.AccountSettings.source_patterns_tip": "A lista de domínios filtra pelo destinatário e o regex de origem filtra pelo remetente. Ambas as condições precisam corresponder para encaminhar (lógica AND). Deixe qualquer uma vazia para ignorá-la.",
|
"views.admin.AccountSettings.source_patterns_tip": "A lista de domínios filtra pelo destinatário e o regex de origem filtra pelo remetente. Ambas as condições precisam corresponder para encaminhar (lógica AND). Deixe qualquer uma vazia para ignorá-la.",
|
||||||
"views.admin.UserManagement.domains": "Domínios",
|
"views.admin.UserManagement.domains": "Domínios",
|
||||||
@@ -237,6 +238,7 @@ export const ptBRMessages = {
|
|||||||
"views.admin.AccountSettings.forward_address_required": "O endereço de encaminhamento é obrigatório",
|
"views.admin.AccountSettings.forward_address_required": "O endereço de encaminhamento é obrigatório",
|
||||||
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
|
||||||
"components.MailContentRenderer.fullscreen": "Tela cheia",
|
"components.MailContentRenderer.fullscreen": "Tela cheia",
|
||||||
|
"components.MailContentRenderer.loadRemoteContent": "Carregar conteúdo remoto",
|
||||||
"views.common.Login.generateName": "Gerar nome aleatório",
|
"views.common.Login.generateName": "Gerar nome aleatório",
|
||||||
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envio de e-mails",
|
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envio de e-mails",
|
||||||
"views.common.Appearance.globalTabplacement": "Posição global das abas",
|
"views.common.Appearance.globalTabplacement": "Posição global das abas",
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ export const MESSAGE_REGISTRY = {
|
|||||||
"en": "Fullscreen",
|
"en": "Fullscreen",
|
||||||
"zh": "全屏"
|
"zh": "全屏"
|
||||||
},
|
},
|
||||||
|
"loadRemoteContent": {
|
||||||
|
"en": "Load Remote Content",
|
||||||
|
"zh": "加载远程内容"
|
||||||
|
},
|
||||||
"reply": {
|
"reply": {
|
||||||
"en": "Reply",
|
"en": "Reply",
|
||||||
"zh": "回复"
|
"zh": "回复"
|
||||||
@@ -2114,6 +2118,10 @@ export const MESSAGE_REGISTRY = {
|
|||||||
"en": "Display text Mail by default",
|
"en": "Display text Mail by default",
|
||||||
"zh": "默认以文本显示邮件"
|
"zh": "默认以文本显示邮件"
|
||||||
},
|
},
|
||||||
|
"autoLoadRemoteContent": {
|
||||||
|
"en": "Automatically load remote content",
|
||||||
|
"zh": "自动加载远程内容"
|
||||||
|
},
|
||||||
"right": {
|
"right": {
|
||||||
"en": "right",
|
"en": "right",
|
||||||
"zh": "右侧"
|
"zh": "右侧"
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export const useGlobalState = createGlobalState(
|
|||||||
const mailboxSplitSize = useStorage('mailboxSplitSize', 0.25);
|
const mailboxSplitSize = useStorage('mailboxSplitSize', 0.25);
|
||||||
const useIframeShowMail = useStorage('useIframeShowMail', false);
|
const useIframeShowMail = useStorage('useIframeShowMail', false);
|
||||||
const preferShowTextMail = useStorage('preferShowTextMail', false);
|
const preferShowTextMail = useStorage('preferShowTextMail', false);
|
||||||
|
const autoLoadRemoteContent = useStorage('autoLoadRemoteContent', false);
|
||||||
const userJwt = useStorage('userJwt', '');
|
const userJwt = useStorage('userJwt', '');
|
||||||
const preferredLocale = useStorage('preferredLocale', '');
|
const preferredLocale = useStorage('preferredLocale', '');
|
||||||
const userTab = useSessionStorage('userTab', 'address_management');
|
const userTab = useSessionStorage('userTab', 'address_management');
|
||||||
@@ -162,6 +163,7 @@ export const useGlobalState = createGlobalState(
|
|||||||
mailboxSplitSize,
|
mailboxSplitSize,
|
||||||
useIframeShowMail,
|
useIframeShowMail,
|
||||||
preferShowTextMail,
|
preferShowTextMail,
|
||||||
|
autoLoadRemoteContent,
|
||||||
userJwt,
|
userJwt,
|
||||||
preferredLocale,
|
preferredLocale,
|
||||||
userTab,
|
userTab,
|
||||||
|
|||||||
@@ -435,6 +435,10 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
:deep(.n-page-header) {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.n-page-header__title) {
|
:deep(.n-page-header__title) {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
mailboxSplitSize, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
mailboxSplitSize, useIframeShowMail, preferShowTextMail, configAutoRefreshInterval,
|
||||||
globalTabplacement, useSideMargin, useUTCDate, useSimpleIndex
|
globalTabplacement, useSideMargin, useUTCDate, useSimpleIndex, autoLoadRemoteContent
|
||||||
} = useGlobalState()
|
} = useGlobalState()
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
|
|
||||||
@@ -43,6 +43,9 @@ const { t } = useScopedI18n('views.common.Appearance')
|
|||||||
<n-form-item-row :label="t('useIframeShowMail')">
|
<n-form-item-row :label="t('useIframeShowMail')">
|
||||||
<n-switch v-model:value="useIframeShowMail" :round="false" />
|
<n-switch v-model:value="useIframeShowMail" :round="false" />
|
||||||
</n-form-item-row>
|
</n-form-item-row>
|
||||||
|
<n-form-item-row :label="t('autoLoadRemoteContent')">
|
||||||
|
<n-switch v-model:value="autoLoadRemoteContent" :round="false" />
|
||||||
|
</n-form-item-row>
|
||||||
<n-form-item-row :label="t('useUTCDate')">
|
<n-form-item-row :label="t('useUTCDate')">
|
||||||
<n-switch v-model:value="useUTCDate" :round="false" />
|
<n-switch v-model:value="useUTCDate" :round="false" />
|
||||||
</n-form-item-row>
|
</n-form-item-row>
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-email-pages",
|
"name": "temp-email-pages",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
aiosmtpd==1.4.6
|
aiosmtpd==1.4.6
|
||||||
pydantic-settings==2.13.1
|
pydantic-settings==2.14.2
|
||||||
Twisted==25.5.0
|
Twisted==26.4.0
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
pyOpenSSL==26.0.0
|
pyOpenSSL==26.0.0
|
||||||
service-identity==24.2.0
|
service-identity==24.2.0
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ No additional env var is required.
|
|||||||
|
|
||||||
Register at `https://resend.com/domains` and add DNS records according to the instructions.
|
Register at `https://resend.com/domains` and add DNS records according to the instructions.
|
||||||
|
|
||||||
|
> [!WARNING] DNS record proxy status on Cloudflare
|
||||||
|
> Resend domain verification CNAME records **must be set to DNS-only** (gray cloud) in
|
||||||
|
> the Cloudflare DNS dashboard. Proxied (orange cloud) records will prevent Resend from
|
||||||
|
> completing verification, and a single failed attempt can take several hours before
|
||||||
|
> Retry becomes available. See [#515](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/515).
|
||||||
|
|
||||||
Create an `api key` on the `API KEYS` page.
|
Create an `api key` on the `API KEYS` page.
|
||||||
|
|
||||||
Then execute the following command to add `RESEND_TOKEN` to secrets:
|
Then execute the following command to add `RESEND_TOKEN` to secrets:
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ send_email = [
|
|||||||
|
|
||||||
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
||||||
|
|
||||||
|
> [!WARNING] Cloudflare 上 DNS 记录的代理状态
|
||||||
|
> Resend 域名验证的 CNAME 记录**必须设置为 仅 DNS**(灰云),在
|
||||||
|
> Cloudflare DNS 控制面板中代理(橙云)记录会阻止 Resend
|
||||||
|
> 完成验证,且一次失败的尝试可能需要数小时才能重试。
|
||||||
|
> 参见 [#515](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/515)。
|
||||||
|
|
||||||
`API KEYS` 页面创建 `api key`
|
`API KEYS` 页面创建 `api key`
|
||||||
|
|
||||||
然后执行下面的命令,将 `RESEND_TOKEN` 添加到 secrets 中
|
然后执行下面的命令,将 `RESEND_TOKEN` 添加到 secrets 中
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "temp-mail-docs",
|
"name": "temp-mail-docs",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^26.0.0",
|
||||||
"vitepress": "^1.6.4",
|
"vitepress": "^1.6.4",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vitepress dev docs",
|
"dev": "vitepress dev docs",
|
||||||
|
|||||||
Generated
+484
-484
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudflare_temp_email",
|
"name": "cloudflare_temp_email",
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,23 +11,23 @@
|
|||||||
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20260602.1",
|
"@cloudflare/workers-types": "^4.20260624.1",
|
||||||
"@eslint/js": "9.39.1",
|
"@eslint/js": "9.39.1",
|
||||||
"@types/node": "^25.9.1",
|
"@types/node": "^25.9.4",
|
||||||
"eslint": "9.39.1",
|
"eslint": "9.39.1",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
"typescript-eslint": "^8.60.1",
|
"typescript-eslint": "^8.62.0",
|
||||||
"wrangler": "^4.96.0"
|
"wrangler": "^4.104.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.888.0",
|
"@aws-sdk/client-s3": "3.888.0",
|
||||||
"@aws-sdk/s3-request-presigner": "3.888.0",
|
"@aws-sdk/s3-request-presigner": "3.888.0",
|
||||||
"@simplewebauthn/server": "^13.3.1",
|
"@simplewebauthn/server": "^13.3.2",
|
||||||
"hono": "^4.12.23",
|
"hono": "^4.12.27",
|
||||||
"jsonpath-plus": "^10.4.0",
|
"jsonpath-plus": "^10.4.0",
|
||||||
"mimetext": "^3.0.28",
|
"mimetext": "^3.0.28",
|
||||||
"postal-mime": "^2.7.4",
|
"postal-mime": "^2.7.4",
|
||||||
"resend": "^6.12.4",
|
"resend": "^6.14.0",
|
||||||
"telegraf": "4.16.3",
|
"telegraf": "4.16.3",
|
||||||
"worker-mailer": "^1.2.1"
|
"worker-mailer": "^1.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+694
-687
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VERSION: 'v' + '1.9.0',
|
VERSION: 'v' + '1.10.0',
|
||||||
|
|
||||||
// DB Version
|
// DB Version
|
||||||
DB_VERSION_KEY: 'db_version',
|
DB_VERSION_KEY: 'db_version',
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ If the extracted content is in markdown link format [text](url):
|
|||||||
2. **Single Selection**: Choose ONLY ONE type based on the highest priority match
|
2. **Single Selection**: Choose ONLY ONE type based on the highest priority match
|
||||||
3. **Real Data Only**: Never invent, guess, or fabricate content
|
3. **Real Data Only**: Never invent, guess, or fabricate content
|
||||||
4. **Complete URLs**: Links must be full, valid URLs as they appear in the email
|
4. **Complete URLs**: Links must be full, valid URLs as they appear in the email
|
||||||
5. **Clean Extraction**: Return only the raw extracted content, no extra text
|
5. **No Domain Modification**: Never modify, rewrite, or substitute URL domains. If the exact URL domain is uncertain, return none
|
||||||
|
6. **Clean Extraction**: Return only the raw extracted content, no extra text
|
||||||
|
|
||||||
# Output Format (JSON only)
|
# Output Format (JSON only)
|
||||||
{
|
{
|
||||||
@@ -166,6 +167,65 @@ async function saveExtractMetadata(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeHtmlEntities(text: string): string {
|
||||||
|
const entities: Record<string, string> = {
|
||||||
|
amp: '&',
|
||||||
|
lt: '<',
|
||||||
|
gt: '>',
|
||||||
|
quot: '"',
|
||||||
|
apos: "'",
|
||||||
|
nbsp: ' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
const decodeCodePoint = (value: number, fallback: string) => {
|
||||||
|
if (!Number.isFinite(value) || value < 0 || value > 0x10ffff) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return String.fromCodePoint(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]+);/gi, (match, entity) => {
|
||||||
|
const normalized = entity.toLowerCase();
|
||||||
|
if (normalized.startsWith('#x')) {
|
||||||
|
const value = Number.parseInt(normalized.slice(2), 16);
|
||||||
|
return decodeCodePoint(value, match);
|
||||||
|
}
|
||||||
|
if (normalized.startsWith('#')) {
|
||||||
|
const value = Number.parseInt(normalized.slice(1), 10);
|
||||||
|
return decodeCodePoint(value, match);
|
||||||
|
}
|
||||||
|
return entities[normalized] ?? match;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlToTextForAi(html: string): string {
|
||||||
|
return decodeHtmlEntities(
|
||||||
|
html
|
||||||
|
.replace(/<\s*(script|style|head|svg)[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, ' ')
|
||||||
|
.replace(/<!--[\s\S]*?-->/g, ' ')
|
||||||
|
.replace(/<a\b[^>]*\bhref=(["'])(.*?)\1[^>]*>([\s\S]*?)<\/a>/gi, ' $3 $2 ')
|
||||||
|
.replace(/<\s*br\s*\/?>/gi, '\n')
|
||||||
|
.replace(/<\/\s*(p|div|tr|td|th|li|table|section|article|header|footer|h[1-6])\s*>/gi, '\n')
|
||||||
|
.replace(/<[^>]+>/g, ' ')
|
||||||
|
)
|
||||||
|
.replace(/[ \t\r\f\v]+/g, ' ')
|
||||||
|
.replace(/\n\s+/g, '\n')
|
||||||
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEmailContentForExtract(parsedEmail: Awaited<ReturnType<typeof commonParseMail>>): string {
|
||||||
|
if (parsedEmail?.text) {
|
||||||
|
return parsedEmail.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsedEmail?.html) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return htmlToTextForAi(parsedEmail.html) || parsedEmail.html;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main extraction function
|
* Main extraction function
|
||||||
* Checks if extraction is enabled, processes the email content, and saves to database.
|
* Checks if extraction is enabled, processes the email content, and saves to database.
|
||||||
@@ -219,7 +279,7 @@ export async function extractEmailInfo(
|
|||||||
|
|
||||||
// Parse email to get content (shared by the AI path and the regex fallback)
|
// Parse email to get content (shared by the AI path and the regex fallback)
|
||||||
const parsedEmail = await commonParseMail(parsedEmailContext);
|
const parsedEmail = await commonParseMail(parsedEmailContext);
|
||||||
const emailContent = parsedEmail?.text || parsedEmail?.html || "";
|
const emailContent = getEmailContentForExtract(parsedEmail);
|
||||||
|
|
||||||
if (!emailContent) {
|
if (!emailContent) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user