diff --git a/CHANGELOG b/CHANGELOG
index 53c0db1e..9e7d90db 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,20 @@
# CHANGE LOG
-## 2024-01-13
+## 2024-04-10 v0.0.1
+
+Breaking changes:
+
+- remove `ENABLE_ATTACHMENT` config
+- use rust wasm to parse email in frontend
+- deprecated api moved to `/api/v1`
DB changes
-- `db/2024-01-13-patch.sql`
+- `db/2024-04-09-patch.sql`
+
+## 2024-04-09 v0.0.0
+
+release v0.0.0
## 2024-04-03
@@ -16,3 +26,9 @@ Changes:
- add delete account
- add admin panel search
+
+## 2024-01-13
+
+DB changes
+
+- `db/2024-01-13-patch.sql`
diff --git a/README.md b/README.md
index fe5c9ed9..e2f65286 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@
- [x] 增加访问授权,可作为私人站点
- [x] 增加自动回复功能
- [x] 增加查看附件功能
-- [ ] 免费版附件过大会造成 Exceeded CPU Limit 错误
+- [x] 使用 rust wasm 解析邮件
---
@@ -137,8 +137,6 @@ PREFIX = "tmp" # 要处理的邮箱名称前缀
DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] # 你的域名
JWT_SECRET = "xxx" # 用于生成 jwt 的密钥
BLACK_LIST = "" # 黑名单,用于过滤发件人,逗号分隔
-# 免费版附件过大会造成 Exceeded CPU Limit 错误,如果不需要附件功能,可以关闭
-ENABLE_ATTACHMENT = true
[[d1_databases]]
binding = "DB"
diff --git a/README_EN.md b/README_EN.md
index 99f9d63e..dd3fee1b 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -20,7 +20,7 @@ This is a temporary email service that uses Cloudflare Workers to create a tempo
- [x] Add access authorization, which can be used as a private site
- [x] Add auto reply feature
- [x] Add attachment viewing function
-- [ ] Exceeded CPU Limit error caused by the free version of the attachment
+- [x] use rust wasm to parse email

@@ -56,8 +56,6 @@ pnpm install
# DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] you domain name
# JWT_SECRET = "xxx"
# BLACK_LIST = ""
-# free version attachment too large will cause Exceeded CPU Limit error, if you don't need attachment function, you can close
-# ENABLE_ATTACHMENT = true
cp wrangler.toml.template wrangler.toml
# deploy
pnpm run deploy
diff --git a/db/2024-04-09-patch.sql b/db/2024-04-09-patch.sql
new file mode 100644
index 00000000..27999d3d
--- /dev/null
+++ b/db/2024-04-09-patch.sql
@@ -0,0 +1,8 @@
+CREATE TABLE IF NOT EXISTS raw_mails (
+ id INTEGER PRIMARY KEY,
+ message_id TEXT,
+ source TEXT,
+ address TEXT,
+ raw TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
diff --git a/db/schema.sql b/db/schema.sql
index 5b70df5f..2d70d177 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -8,6 +8,15 @@ CREATE TABLE IF NOT EXISTS mails (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
+CREATE TABLE IF NOT EXISTS raw_mails (
+ id INTEGER PRIMARY KEY,
+ message_id TEXT,
+ source TEXT,
+ address TEXT,
+ raw TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
CREATE TABLE IF NOT EXISTS address (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
diff --git a/frontend/package.json b/frontend/package.json
index ca6ec184..c44574b8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -13,7 +13,9 @@
"@vicons/material": "^0.12.0",
"@vueuse/core": "^10.9.0",
"axios": "^1.6.8",
+ "mail-parser-wasm": "^0.1.6",
"naive-ui": "^2.38.1",
+ "postal-mime": "^2.2.1",
"vooks": "^0.2.12",
"vue": "^3.4.21",
"vue-clipboard3": "^2.0.0",
@@ -27,6 +29,8 @@
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.6",
"vite-plugin-pwa": "^0.19.7",
+ "vite-plugin-top-level-await": "^1.4.1",
+ "vite-plugin-wasm": "^3.3.0",
"workbox-window": "^7.0.0"
}
}
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index 2a526ee1..f965e458 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -14,9 +14,15 @@ dependencies:
axios:
specifier: ^1.6.8
version: 1.6.8
+ mail-parser-wasm:
+ specifier: ^0.1.6
+ version: 0.1.6
naive-ui:
specifier: ^2.38.1
version: 2.38.1(vue@3.4.21)
+ postal-mime:
+ specifier: ^2.2.1
+ version: 2.2.1
vooks:
specifier: ^0.2.12
version: 0.2.12(vue@3.4.21)
@@ -52,6 +58,12 @@ devDependencies:
vite-plugin-pwa:
specifier: ^0.19.7
version: 0.19.7(vite@5.2.6)(workbox-build@7.0.0)(workbox-window@7.0.0)
+ vite-plugin-top-level-await:
+ specifier: ^1.4.1
+ version: 1.4.1(rollup@2.79.1)(vite@5.2.6)
+ vite-plugin-wasm:
+ specifier: ^3.3.0
+ version: 3.3.0(vite@5.2.6)
workbox-window:
specifier: ^7.0.0
version: 7.0.0
@@ -1593,6 +1605,18 @@ packages:
rollup: 2.79.1
dev: true
+ /@rollup/plugin-virtual@3.0.2(rollup@2.79.1):
+ resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ rollup: 2.79.1
+ dev: true
+
/@rollup/pluginutils@3.1.0(rollup@2.79.1):
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
@@ -1741,6 +1765,131 @@ packages:
string.prototype.matchall: 4.0.11
dev: true
+ /@swc/core-darwin-arm64@1.4.12:
+ resolution: {integrity: sha512-BZUUq91LGJsLI2BQrhYL3yARkcdN4TS3YGNS6aRYUtyeWrGCTKHL90erF2BMU2rEwZLLkOC/U899R4o4oiSHfA==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-darwin-x64@1.4.12:
+ resolution: {integrity: sha512-Wkk8rq1RwCOgg5ybTlfVtOYXLZATZ+QjgiBNM7pIn03A5/zZicokNTYd8L26/mifly2e74Dz34tlIZBT4aTGDA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-linux-arm-gnueabihf@1.4.12:
+ resolution: {integrity: sha512-8jb/SN67oTQ5KSThWlKLchhU6xnlAlnmnLCCOKK1xGtFS6vD+By9uL+qeEY2krV98UCRTf68WSmC0SLZhVoz5A==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-linux-arm64-gnu@1.4.12:
+ resolution: {integrity: sha512-DhW47DQEZKCdSq92v5F03rqdpjRXdDMqxfu4uAlZ9Uo1wJEGvY23e1SNmhji2sVHsZbBjSvoXoBLk0v00nSG8w==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-linux-arm64-musl@1.4.12:
+ resolution: {integrity: sha512-PR57pT3TssnCRvdsaKNsxZy9N8rFg9AKA1U7W+LxbZ/7Z7PHc5PjxF0GgZpE/aLmU6xOn5VyQTlzjoamVkt05g==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-linux-x64-gnu@1.4.12:
+ resolution: {integrity: sha512-HLZIWNHWuFIlH+LEmXr1lBiwGQeCshKOGcqbJyz7xpqTh7m2IPAxPWEhr/qmMTMsjluGxeIsLrcsgreTyXtgNA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-linux-x64-musl@1.4.12:
+ resolution: {integrity: sha512-M5fBAtoOcpz2YQAFtNemrPod5BqmzAJc8pYtT3dVTn1MJllhmLHlphU8BQytvoGr1PHgJL8ZJBlBGdt70LQ7Mw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-win32-arm64-msvc@1.4.12:
+ resolution: {integrity: sha512-K8LjjgZ7VQFtM+eXqjfAJ0z+TKVDng3r59QYn7CL6cyxZI2brLU3lNknZcUFSouZD+gsghZI/Zb8tQjVk7aKDQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-win32-ia32-msvc@1.4.12:
+ resolution: {integrity: sha512-hflO5LCxozngoOmiQbDPyvt6ODc5Cu9AwTJP9uH/BSMPdEQ6PCnefuUOJLAKew2q9o+NmDORuJk+vgqQz9Uzpg==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core-win32-x64-msvc@1.4.12:
+ resolution: {integrity: sha512-3A4qMtddBDbtprV5edTB/SgJn9L+X5TL7RGgS3eWtEgn/NG8gA80X/scjf1v2MMeOsrcxiYhnemI2gXCKuQN2g==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@swc/core@1.4.12:
+ resolution: {integrity: sha512-QljRxTaUajSLB9ui93cZ38/lmThwIw/BPxjn+TphrYN6LPU3vu9/ykjgHtlpmaXDDcngL4K5i396E7iwwEUxYg==}
+ engines: {node: '>=10'}
+ requiresBuild: true
+ peerDependencies:
+ '@swc/helpers': ^0.5.0
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.6
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.4.12
+ '@swc/core-darwin-x64': 1.4.12
+ '@swc/core-linux-arm-gnueabihf': 1.4.12
+ '@swc/core-linux-arm64-gnu': 1.4.12
+ '@swc/core-linux-arm64-musl': 1.4.12
+ '@swc/core-linux-x64-gnu': 1.4.12
+ '@swc/core-linux-x64-musl': 1.4.12
+ '@swc/core-win32-arm64-msvc': 1.4.12
+ '@swc/core-win32-ia32-msvc': 1.4.12
+ '@swc/core-win32-x64-msvc': 1.4.12
+ dev: true
+
+ /@swc/counter@0.1.3:
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+ dev: true
+
+ /@swc/types@0.1.6:
+ resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==}
+ dependencies:
+ '@swc/counter': 0.1.3
+ dev: true
+
/@types/estree@0.0.39:
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
dev: true
@@ -2969,6 +3118,10 @@ packages:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
+ /mail-parser-wasm@0.1.6:
+ resolution: {integrity: sha512-RoPPXqpGcCe4BcnXmxH4Cl5u0AH8y0JUNutksg2xzK0qFGEVE3xipx90JHzUUZ3MuMxo7doQTRktcABTIb3aeg==}
+ dev: false
+
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
@@ -3131,6 +3284,10 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /postal-mime@2.2.1:
+ resolution: {integrity: sha512-YqGeFmiKXUxv32hOy2t47VX67mYydC47CTCc7+HKd3xlNKPDhivnO/ZovN3iWXxvyyL2TRTxusuuq3etWeCKsw==}
+ dev: false
+
/postcss@8.4.38:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
@@ -3742,6 +3899,11 @@ packages:
punycode: 2.3.1
dev: true
+ /uuid@9.0.1:
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+ hasBin: true
+ dev: true
+
/vdirs@0.1.8(vue@3.4.21):
resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==}
peerDependencies:
@@ -3773,6 +3935,28 @@ packages:
- supports-color
dev: true
+ /vite-plugin-top-level-await@1.4.1(rollup@2.79.1)(vite@5.2.6):
+ resolution: {integrity: sha512-hogbZ6yT7+AqBaV6lK9JRNvJDn4/IJvHLu6ET06arNfo0t2IsyCaon7el9Xa8OumH+ESuq//SDf8xscZFE0rWw==}
+ peerDependencies:
+ vite: '>=2.8'
+ dependencies:
+ '@rollup/plugin-virtual': 3.0.2(rollup@2.79.1)
+ '@swc/core': 1.4.12
+ uuid: 9.0.1
+ vite: 5.2.6
+ transitivePeerDependencies:
+ - '@swc/helpers'
+ - rollup
+ dev: true
+
+ /vite-plugin-wasm@3.3.0(vite@5.2.6):
+ resolution: {integrity: sha512-tVhz6w+W9MVsOCHzxo6SSMSswCeIw4HTrXEi6qL3IRzATl83jl09JVO1djBqPSwfjgnpVHNLYcaMbaDX5WB/pg==}
+ peerDependencies:
+ vite: ^2 || ^3 || ^4 || ^5
+ dependencies:
+ vite: 5.2.6
+ dev: true
+
/vite@5.2.6:
resolution: {integrity: sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==}
engines: {node: ^18.0.0 || >=20.0.0}
diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js
index 02fe0da8..bb545997 100644
--- a/frontend/src/api/index.js
+++ b/frontend/src/api/index.js
@@ -75,7 +75,8 @@ const getSettings = async () => {
const res = await apiFetch("/api/settings");;
settings.value = {
address: res["address"],
- auto_reply: res["auto_reply"]
+ auto_reply: res["auto_reply"],
+ has_v1_mails: res["has_v1_mails"],
};
} finally {
settings.value.fetched = true;
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index a12253c3..072ff7c5 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -14,6 +14,7 @@ export const useGlobalState = createGlobalState(
})
const settings = ref({
fetched: false,
+ has_v1_mails: false,
address: '',
auto_reply: {
subject: '',
diff --git a/frontend/src/utils/email-parser.js b/frontend/src/utils/email-parser.js
new file mode 100644
index 00000000..ec7426ab
--- /dev/null
+++ b/frontend/src/utils/email-parser.js
@@ -0,0 +1,71 @@
+import PostalMime from 'postal-mime';
+import { parse_message } from 'mail-parser-wasm'
+
+export async function processItem(item) {
+ // Try to parse the email using mail-parser-wasm
+ try {
+ const parsedEmail = parse_message(item.raw);
+ item.source = parsedEmail.sender || item.source;
+ item.subject = parsedEmail.subject || '';
+ item.message = parsedEmail.body_html || parsedEmail.text || '';
+ item.attachments = parsedEmail.attachments?.map((a_item) => {
+ const blob_url = URL.createObjectURL(
+ new Blob(
+ [a_item.content],
+ { type: a_item.content_type || 'application/octet-stream' }
+ ))
+ if (a_item.content_id && a_item.content_id.length > 0) {
+ item.message = item.message.replace(`cid:${a_item.content_id}`, blob_url);
+ }
+ return {
+ id: a_item.content_id || Math.random().toString(36).substring(2, 15),
+ filename: a_item.filename || a_item.content_id || "",
+ size: a_item.content?.length || 0,
+ url: blob_url
+ }
+ }) || [];
+ } catch (error) {
+ console.log('Error parsing email with mail-parser-wasm');
+ console.error(error);
+ }
+ if (item.subject && item.subject.length > 0 && item.message && item.message.length > 0) {
+ return item;
+ }
+ // Fallback to PostalMime
+ try {
+ const parsedEmail = await PostalMime.parse(item.raw);
+ item.source = parsedEmail.from.address || item.source;
+ if (parsedEmail.from.address && parsedEmail.from.name) {
+ item.source = `${parsedEmail.from.name} <${parsedEmail.from.address}>`;
+ }
+ item.subject = parsedEmail.subject || 'No Subject';
+ item.message = parsedEmail.html || parsedEmail.text || item.raw;
+ item.attachments = parsedEmail.attachments?.map((a_item) => {
+ const blob_url = URL.createObjectURL(
+ new Blob(
+ [a_item.content],
+ { type: a_item.mimeType || 'application/octet-stream' }
+ ))
+ if (a_item.contentId && a_item.contentId.length > 0) {
+ item.message = item.message.replace(`cid:${a_item.contentId}`, blob_url);
+ }
+ return {
+ id: a_item.contentId || Math.random().toString(36).substring(2, 15),
+ filename: a_item.filename || a_item.contentId || "",
+ size: a_item.content?.length || 0,
+ url: blob_url
+ }
+ }) || [];
+ } catch (error) {
+ console.log('Error parsing email with PostalMime');
+ console.error(error);
+ item.subject = 'No Subject';
+ item.message = item.raw;
+ }
+}
+
+export function getDownloadEmlUrl(raw) {
+ return URL.createObjectURL(
+ new Blob([raw], { type: 'text/plain' }
+ ))
+}
diff --git a/frontend/src/views/Admin.vue b/frontend/src/views/Admin.vue
index 16957c90..a8e78845 100644
--- a/frontend/src/views/Admin.vue
+++ b/frontend/src/views/Admin.vue
@@ -6,6 +6,7 @@ import { User, UserCheck, MailBulk } from '@vicons/fa'
import { useGlobalState } from '../store'
import { api } from '../api'
+import { processItem, getDownloadEmlUrl } from '../utils/email-parser'
const { localeCache, adminAuth, showAdminAuth } = useGlobalState()
const router = useRouter()
@@ -222,7 +223,9 @@ const fetchMailData = async () => {
+ `&limit=${mailPageSize.value}`
+ `&offset=${(mailPage.value - 1) * mailPageSize.value}`
);
- mailData.value = results;
+ mailData.value = await Promise.all(results.map(async (item) => {
+ return await processItem(item);
+ }));
if (count > 0) {
mailCount.value = count;
}
@@ -249,7 +252,9 @@ const fetchMailUnknowData = async () => {
+ `?limit=${mailPageSize.value}`
+ `&offset=${(mailPage.value - 1) * mailPageSize.value}`
);
- mailUnknowData.value = results;
+ mailUnknowData.value = await Promise.all(results.map(async (item) => {
+ return await processItem(item);
+ }));
if (count > 0) {
mailUnknowCount.value = count;
}
@@ -268,9 +273,7 @@ const fetchMailUnknowData = async () => {
{{ t('auth') }}
{{ t('authTip') }}
-
+
{{ t('auth') }}
diff --git a/frontend/src/views/Header.vue b/frontend/src/views/Header.vue
index 589e5bb5..6d328973 100644
--- a/frontend/src/views/Header.vue
+++ b/frontend/src/views/Header.vue
@@ -29,8 +29,8 @@ const emailDomain = ref("")
const login = async () => {
try {
- await api.getSettings()
jwt.value = password.value;
+ await api.getSettings()
location.reload()
} catch (error) {
message.error(error.message || "error");
@@ -85,6 +85,7 @@ const { t } = useI18n({
copied: 'Copied',
showPassword: 'Show Password',
fetchAddressError: 'Fetch address error, maybe your jwt is invalid or network error.',
+ mailV1Alert: 'You have some mails in v1, please click here to login and visit your history mails.',
},
zh: {
title: 'Cloudflare 临时邮件',
@@ -114,6 +115,7 @@ const { t } = useI18n({
copied: '已复制',
showPassword: '查看密码',
fetchAddressError: '获取地址失败, 请检查你的 jwt 是否有效 或 网络是否正常。',
+ mailV1Alert: '你有一些 v1 版本的邮件,请点击此处登录查看。',
}
}
});
@@ -351,14 +353,24 @@ onMounted(async () => {
-
-
- {{ t('yourAddress') }} {{ settings.address }}
-
- {{ t('copy') }}
-
-
-
+
+
+
+
+ {{ t('mailV1Alert') }}
+
+
+
+
+
+ {{ t('yourAddress') }} {{ settings.address }}
+
+ {{ t('copy') }}
+
+
+
+
diff --git a/frontend/src/views/Index.vue b/frontend/src/views/Index.vue
index 7d537acf..abcd3bfe 100644
--- a/frontend/src/views/Index.vue
+++ b/frontend/src/views/Index.vue
@@ -6,6 +6,7 @@ import { useGlobalState } from '../store'
import { api } from '../api'
import { CloudDownloadRound } from '@vicons/material'
import { useIsMobile } from '../utils/composables'
+import { processItem, getDownloadEmlUrl } from '../utils/email-parser'
const message = useMessage()
const isMobile = useIsMobile()
@@ -30,11 +31,13 @@ const { t } = useI18n({
autoRefresh: 'Auto Refresh',
refresh: 'Refresh',
attachments: 'Show Attachments',
+ downloadMail: 'Download Mail',
pleaseSelectMail: "Please select a mail to view."
},
zh: {
autoRefresh: '自动刷新',
refresh: '刷新',
+ downloadMail: '下载邮件',
attachments: '查看附件',
pleaseSelectMail: "请选择一封邮件查看。"
}
@@ -72,12 +75,14 @@ const refresh = async () => {
+ `?limit=${pageSize.value}`
+ `&offset=${(page.value - 1) * pageSize.value}`
);
- data.value = results;
+ data.value = await Promise.all(results.map(async (item) => {
+ return await processItem(item);
+ }));
if (totalCount > 0) {
count.value = totalCount;
}
if (!isMobile.value && !curMail.value && data.value.length > 0) {
- curMail.value = results[0];
+ curMail.value = data.value[0];
}
} catch (error) {
message.error(error.message || "error");
@@ -89,29 +94,9 @@ const clickRow = async (row) => {
curMail.value = row;
};
-const getAttachments = async (attachment_id) => {
- try {
- const res = await api.fetch(
- `/api/attachment/${attachment_id}`
- );
- curAttachments.value = res
- .filter((item) => item?.content?.data)
- .map((item) => {
- return {
- id: item.contentId || Math.random().toString(36).substring(2, 15),
- filename: item.filename || "",
- size: item.size,
- url: URL.createObjectURL(
- new Blob(
- [new Uint8Array(item.content.data)],
- { type: item.contentType || 'application/octet-stream' }
- ))
- }
- });
- showAttachments.value = true;
- } catch (error) {
- message.error(error.message || "error");
- }
+const getAttachments = (attachments) => {
+ curAttachments.value = attachments;
+ showAttachments.value = true;
};
const mailItemClass = (row) => {
@@ -177,12 +162,17 @@ onMounted(async () => {
FROM: {{ curMail.source }}
-
+
{{ t('attachments') }}
+
+
+ {{ t('downloadMail') }}
+
-
+
@@ -238,10 +228,15 @@ onMounted(async () => {
FROM: {{ curMail.source }}
-
+
{{ t('attachments') }}
+
+ {{ t('downloadMail') }}
+
+
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index f2199361..790bb17a 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -7,6 +7,8 @@ import { splitVendorChunkPlugin } from 'vite';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
+import wasm from "vite-plugin-wasm";
+import topLevelAwait from "vite-plugin-top-level-await";
// https://vitejs.dev/config/
export default defineConfig({
@@ -15,6 +17,8 @@ export default defineConfig({
},
plugins: [
vue(),
+ wasm(),
+ topLevelAwait(),
splitVendorChunkPlugin(),
AutoImport({
imports: [
diff --git a/mail-parser-wasm/.gitignore b/mail-parser-wasm/.gitignore
new file mode 100644
index 00000000..6985cf1b
--- /dev/null
+++ b/mail-parser-wasm/.gitignore
@@ -0,0 +1,14 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
diff --git a/mail-parser-wasm/Cargo.toml b/mail-parser-wasm/Cargo.toml
new file mode 100644
index 00000000..f7d20454
--- /dev/null
+++ b/mail-parser-wasm/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "mail-parser-wasm"
+version = "0.1.6"
+edition = "2021"
+description = "A simple mail parser for wasm"
+license = "MIT"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+mail-parser = "0.9.3"
+wasm-bindgen = "0.2.92"
diff --git a/mail-parser-wasm/README.md b/mail-parser-wasm/README.md
new file mode 100644
index 00000000..c6834356
--- /dev/null
+++ b/mail-parser-wasm/README.md
@@ -0,0 +1,16 @@
+# mail-parser-wasm
+
+## usage
+
+```js
+import { parse_message } from 'mail-parser-wasm'
+
+const parsedEmail = parse_message(item.raw);
+```
+
+## build
+
+```bash
+wasm-pack build --release
+wasm-pack publish
+```
diff --git a/mail-parser-wasm/src/lib.rs b/mail-parser-wasm/src/lib.rs
new file mode 100644
index 00000000..9312adf5
--- /dev/null
+++ b/mail-parser-wasm/src/lib.rs
@@ -0,0 +1,159 @@
+extern crate wasm_bindgen;
+
+use mail_parser::{MessageParser, MimeHeaders};
+use wasm_bindgen::prelude::*;
+
+#[derive(Clone)]
+#[wasm_bindgen]
+pub struct AttachmentResult {
+ content_id: String,
+ content_type: String,
+ filename: String,
+ content: Vec,
+}
+
+#[wasm_bindgen]
+impl AttachmentResult {
+ #[wasm_bindgen(getter)]
+ pub fn content_id(&self) -> String {
+ self.content_id.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn content_type(&self) -> String {
+ self.content_type.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn filename(&self) -> String {
+ self.filename.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn content(&self) -> Vec {
+ self.content.clone()
+ }
+}
+
+#[wasm_bindgen]
+pub struct MessageResult {
+ sender: String,
+ subject: String,
+ body_html: String,
+ text: String,
+ attachments: Vec,
+}
+
+#[wasm_bindgen]
+impl MessageResult {
+ #[wasm_bindgen(getter)]
+ pub fn sender(&self) -> String {
+ self.sender.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn subject(&self) -> String {
+ self.subject.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn body_html(&self) -> String {
+ self.body_html.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn text(&self) -> String {
+ self.text.clone()
+ }
+
+ #[wasm_bindgen(getter)]
+ pub fn attachments(&self) -> Vec {
+ self.attachments.clone()
+ }
+}
+
+pub fn parse_attachment(message: &mail_parser::Message) -> Vec {
+ let mut attachments: Vec = Vec::new();
+ for attachment in message.attachments() {
+ if !attachment.is_message() {
+ attachments.push(AttachmentResult {
+ content_id: attachment
+ .content_id()
+ .map(|id| id.to_owned())
+ .unwrap_or(String::new()),
+ content_type: attachment
+ .content_type()
+ .map(|ct| {
+ let c_type = ct.c_type.clone().into_owned();
+ let c_subtype = ct.c_subtype.clone();
+ if c_subtype.is_none() {
+ return c_type;
+ } else {
+ return format!("{}/{}", c_type, c_subtype.unwrap());
+ }
+ })
+ .unwrap_or(String::new()),
+ filename: attachment
+ .attachment_name()
+ .map(|name| name.to_owned())
+ .unwrap_or(String::new()),
+ content: attachment.contents().to_vec(),
+ });
+ } else {
+ attachments.append(
+ &mut attachment
+ .message()
+ .map(|msg| parse_attachment(msg))
+ .unwrap_or(Vec::new()),
+ );
+ }
+ }
+ attachments
+}
+
+#[wasm_bindgen]
+pub fn parse_message(raw_message: &str) -> MessageResult {
+ // check if the message is valid
+ let res = MessageParser::default().parse(raw_message);
+ if res.is_none() {
+ return MessageResult {
+ sender: String::new(),
+ subject: String::new(),
+ body_html: String::new(),
+ text: String::new(),
+ attachments: Vec::new(),
+ };
+ }
+ let message = res.unwrap();
+
+ MessageResult {
+ sender: message
+ .from()
+ .and_then(|from| from.first())
+ .map(|addr| {
+ if addr.name().is_some() {
+ return format!(
+ "{} <{}>",
+ addr.name().unwrap(),
+ addr.address().unwrap_or("")
+ );
+ } else {
+ return addr.address().unwrap_or("").to_owned();
+ }
+ })
+ .unwrap_or(String::new()),
+ subject: message
+ .subject()
+ .map(|subject| subject.to_owned())
+ .unwrap_or(String::new()),
+ body_html: message
+ .body_html(0)
+ .map(|html| html.into_owned())
+ .unwrap_or(String::new()),
+ text: message
+ .body_text(0)
+ .map(|text| text.into_owned())
+ .unwrap_or(String::new()),
+ attachments: parse_attachment(&message),
+ }
+}
diff --git a/worker/package.json b/worker/package.json
index 952e301b..0e744148 100644
--- a/worker/package.json
+++ b/worker/package.json
@@ -13,13 +13,6 @@
},
"dependencies": {
"hono": "^4.2.2",
- "mailparser": "^3.6.9",
- "mimetext": "^3.0.24",
- "postal-mime": "^2.2.1"
- },
- "pnpm": {
- "patchedDependencies": {
- "mailparser@3.6.9": "patches/mailparser@3.6.9.patch"
- }
+ "mimetext": "^3.0.24"
}
}
diff --git a/worker/patches/mailparser@3.6.9.patch b/worker/patches/mailparser@3.6.9.patch
deleted file mode 100644
index 95d24f0e..00000000
--- a/worker/patches/mailparser@3.6.9.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/lib/stream-hash.js b/lib/stream-hash.js
-index 3f9b44133766c04866ab2ab178c061f35dbf8f42..368ed6d94da4401909b7eddc87d18354947daf33 100644
---- a/lib/stream-hash.js
-+++ b/lib/stream-hash.js
-@@ -7,19 +7,15 @@ class StreamHash extends Transform {
- constructor(attachment, algo) {
- super();
- this.attachment = attachment;
-- this.algo = (algo || 'md5').toLowerCase();
-- this.hash = crypto.createHash(algo);
- this.byteCount = 0;
- }
-
- _transform(chunk, encoding, done) {
-- this.hash.update(chunk);
- this.byteCount += chunk.length;
- done(null, chunk);
- }
-
- _flush(done) {
-- this.attachment.checksum = this.hash.digest('hex');
- this.attachment.size = this.byteCount;
- done();
- }
diff --git a/worker/pnpm-lock.yaml b/worker/pnpm-lock.yaml
index b49aff72..d17ce057 100644
--- a/worker/pnpm-lock.yaml
+++ b/worker/pnpm-lock.yaml
@@ -4,24 +4,13 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
-patchedDependencies:
- mailparser@3.6.9:
- hash: vtv6mupuxeqjidadcgidi322su
- path: patches/mailparser@3.6.9.patch
-
dependencies:
hono:
specifier: ^4.2.2
version: 4.2.2
- mailparser:
- specifier: ^3.6.9
- version: 3.6.9(patch_hash=vtv6mupuxeqjidadcgidi322su)
mimetext:
specifier: ^3.0.24
version: 3.0.24
- postal-mime:
- specifier: ^2.2.1
- version: 2.2.1
devDependencies:
wrangler:
@@ -340,13 +329,6 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
- /@selderee/plugin-htmlparser2@0.11.0:
- resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
- dependencies:
- domhandler: 5.0.3
- selderee: 0.11.0
- dev: false
-
/@types/node-forge@1.3.11:
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
dependencies:
@@ -450,48 +432,6 @@ packages:
ms: 2.1.2
dev: true
- /deepmerge@4.3.1:
- resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
- engines: {node: '>=0.10.0'}
- dev: false
-
- /dom-serializer@2.0.0:
- resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- entities: 4.5.0
- dev: false
-
- /domelementtype@2.3.0:
- resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
- dev: false
-
- /domhandler@5.0.3:
- resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
- engines: {node: '>= 4'}
- dependencies:
- domelementtype: 2.3.0
- dev: false
-
- /domutils@3.1.0:
- resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
- dependencies:
- dom-serializer: 2.0.0
- domelementtype: 2.3.0
- domhandler: 5.0.3
- dev: false
-
- /encoding-japanese@2.0.0:
- resolution: {integrity: sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==}
- engines: {node: '>=8.10.0'}
- dev: false
-
- /entities@4.5.0:
- resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
- engines: {node: '>=0.12'}
- dev: false
-
/esbuild@0.17.19:
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
engines: {node: '>=12'}
@@ -580,43 +520,11 @@ packages:
function-bind: 1.1.2
dev: true
- /he@1.2.0:
- resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
- hasBin: true
- dev: false
-
/hono@4.2.2:
resolution: {integrity: sha512-mDmjBHF6uBNN3TASdAbDCFsN9FLbrlgXyFZkhLEkU7hUgk0+T9hcsUrL/nho4qV+Xk0RDHx7gop4Q1gelZZVRw==}
engines: {node: '>=16.0.0'}
dev: false
- /html-to-text@9.0.5:
- resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
- engines: {node: '>=14'}
- dependencies:
- '@selderee/plugin-htmlparser2': 0.11.0
- deepmerge: 4.3.1
- dom-serializer: 2.0.0
- htmlparser2: 8.0.2
- selderee: 0.11.0
- dev: false
-
- /htmlparser2@8.0.2:
- resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- domutils: 3.1.0
- entities: 4.5.0
- dev: false
-
- /iconv-lite@0.6.3:
- resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
- engines: {node: '>=0.10.0'}
- dependencies:
- safer-buffer: 2.1.2
- dev: false
-
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@@ -651,80 +559,12 @@ packages:
resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==}
dev: false
- /leac@0.6.0:
- resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
- dev: false
-
- /libbase64@1.2.1:
- resolution: {integrity: sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==}
- dev: false
-
- /libbase64@1.3.0:
- resolution: {integrity: sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==}
- dev: false
-
- /libmime@5.2.0:
- resolution: {integrity: sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==}
- dependencies:
- encoding-japanese: 2.0.0
- iconv-lite: 0.6.3
- libbase64: 1.2.1
- libqp: 2.0.1
- dev: false
-
- /libmime@5.3.4:
- resolution: {integrity: sha512-TsqPdercr6DHrnoQx1F0nS2Y4yPT+fWuOjEP2rqzvV77hMYWomTe/rpm0u9JORQ/FavEXybAGcBJsQbLr9+hjA==}
- dependencies:
- encoding-japanese: 2.0.0
- iconv-lite: 0.6.3
- libbase64: 1.3.0
- libqp: 2.1.0
- dev: false
-
- /libqp@2.0.1:
- resolution: {integrity: sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==}
- dev: false
-
- /libqp@2.1.0:
- resolution: {integrity: sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==}
- dev: false
-
- /linkify-it@5.0.0:
- resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
- dependencies:
- uc.micro: 2.1.0
- dev: false
-
/magic-string@0.25.9:
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
dependencies:
sourcemap-codec: 1.4.8
dev: true
- /mailparser@3.6.9(patch_hash=vtv6mupuxeqjidadcgidi322su):
- resolution: {integrity: sha512-1fIDZlgN1NnuzmTSEUxkaViquXYkw5NbQehVc+kz55QRy98QgLdTtRSKv289Jy4NrCiDchRx6zAijB4HrPsvkA==}
- dependencies:
- encoding-japanese: 2.0.0
- he: 1.2.0
- html-to-text: 9.0.5
- iconv-lite: 0.6.3
- libmime: 5.3.4
- linkify-it: 5.0.0
- mailsplit: 5.4.0
- nodemailer: 6.9.11
- punycode: 2.3.1
- tlds: 1.250.0
- dev: false
- patched: true
-
- /mailsplit@5.4.0:
- resolution: {integrity: sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==}
- dependencies:
- libbase64: 1.2.1
- libmime: 5.2.0
- libqp: 2.0.1
- dev: false
-
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -795,23 +635,11 @@ packages:
engines: {node: '>= 6.13.0'}
dev: true
- /nodemailer@6.9.11:
- resolution: {integrity: sha512-UiAkgiERuG94kl/3bKfE8o10epvDnl0vokNEtZDPTq9BWzIl6EFT9336SbIT4oaTBD8NmmUTLsQyXHV82eXSWg==}
- engines: {node: '>=6.0.0'}
- dev: false
-
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: true
- /parseley@0.12.1:
- resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
- dependencies:
- leac: 0.6.0
- peberminta: 0.9.0
- dev: false
-
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
@@ -820,28 +648,15 @@ packages:
resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==}
dev: true
- /peberminta@0.9.0:
- resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
- dev: false
-
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: true
- /postal-mime@2.2.1:
- resolution: {integrity: sha512-YqGeFmiKXUxv32hOy2t47VX67mYydC47CTCc7+HKd3xlNKPDhivnO/ZovN3iWXxvyyL2TRTxusuuq3etWeCKsw==}
- dev: false
-
/printable-characters@1.0.42:
resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==}
dev: true
- /punycode@2.3.1:
- resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
- engines: {node: '>=6'}
- dev: false
-
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@@ -888,16 +703,6 @@ packages:
estree-walker: 0.6.1
dev: true
- /safer-buffer@2.1.2:
- resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- dev: false
-
- /selderee@0.11.0:
- resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
- dependencies:
- parseley: 0.12.1
- dev: false
-
/selfsigned@2.4.1:
resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==}
engines: {node: '>=10'}
@@ -933,11 +738,6 @@ packages:
engines: {node: '>= 0.4'}
dev: true
- /tlds@1.250.0:
- resolution: {integrity: sha512-rWsBfFCWKrjM/o2Q1TTUeYQv6tHSd/umUutDjVs6taTuEgRDIreVYIBgWRWW4ot7jp6n0UVUuxhTLWBtUmPu/w==}
- hasBin: true
- dev: false
-
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -949,10 +749,6 @@ packages:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
dev: true
- /uc.micro@2.1.0:
- resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
- dev: false
-
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
diff --git a/worker/src/admin_api.js b/worker/src/admin_api.js
new file mode 100644
index 00000000..255edcc3
--- /dev/null
+++ b/worker/src/admin_api.js
@@ -0,0 +1,158 @@
+import { Hono } from 'hono'
+import { Jwt } from 'hono/utils/jwt'
+
+const api = new Hono()
+
+api.get('/admin/address', async (c) => {
+ const { limit, offset, query } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ if (query) {
+ const { results } = await c.env.DB.prepare(
+ `SELECT * FROM address where concat('${c.env.PREFIX}', name) like ? order by id desc limit ? offset ? `
+ ).bind(`%${query}%`, limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: addressCount } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM address where concat('${c.env.PREFIX}', name) like ?`
+ ).bind(`%${query}%`).first();
+ count = addressCount;
+ }
+ return c.json({
+ results: results.map((r) => {
+ r.name = c.env.PREFIX + r.name;
+ return r;
+ }),
+ count: count
+ })
+ }
+ const { results } = await c.env.DB.prepare(
+ `SELECT * FROM address order by id desc limit ? offset ? `
+ ).bind(limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: addressCount } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM address`
+ ).first();
+ count = addressCount;
+ }
+ return c.json({
+ results: results.map((r) => {
+ r.name = c.env.PREFIX + r.name;
+ return r;
+ }),
+ count: count
+ })
+})
+
+api.delete('/admin/delete_address/:id', async (c) => {
+ const { id } = c.req.param();
+ const { success } = await c.env.DB.prepare(
+ `DELETE FROM address WHERE id = ? `
+ ).bind(id).run();
+ if (!success) {
+ return c.text("Failed to delete address", 500)
+ }
+ const { success: mailSuccess } = await c.env.DB.prepare(
+ `DELETE FROM mails WHERE address IN
+ (select concat('${c.env.PREFIX}', name) from address where id = ?) `
+ ).bind(id).run();
+ if (!mailSuccess) {
+ return c.text("Failed to delete mails", 500)
+ }
+ return c.json({
+ success: success
+ })
+})
+
+api.get('/admin/show_password/:id', async (c) => {
+ const { id } = c.req.param();
+ const name = await c.env.DB.prepare(
+ `SELECT name FROM address WHERE id = ? `
+ ).bind(id).first("name");
+ // compute address
+ const emailAddress = c.env.PREFIX + name
+ const jwt = await Jwt.sign({
+ address: emailAddress,
+ address_id: id
+ }, c.env.JWT_SECRET)
+ return c.json({
+ password: jwt
+ })
+})
+
+
+api.get('/admin/mails', async (c) => {
+ const { address, limit, offset } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ const { results } = await c.env.DB.prepare(
+ `SELECT id, source, raw, created_at FROM raw_mails where address = ? order by id desc limit ? offset ?`
+ ).bind(address, limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: mailCount } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM raw_mails where address = ? `
+ ).bind(address).first();
+ count = mailCount;
+ }
+ return c.json({
+ results: results,
+ count: count
+ })
+});
+
+api.get('/admin/mails_unknow', async (c) => {
+ const { limit, offset } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ const { results } = await c.env.DB.prepare(`
+ SELECT id, source, raw, created_at FROM raw_mails
+ where address NOT IN(select concat('${c.env.PREFIX}', name) from address)
+ order by id desc limit ? offset ? `
+ ).bind(limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: mailCount } = await c.env.DB.prepare(`
+ SELECT count(*) as count FROM raw_mails
+ where address NOT IN
+ (select concat('${c.env.PREFIX}', name) from address)`
+ ).first();
+ count = mailCount;
+ }
+ return c.json({
+ results: results,
+ count: count
+ })
+});
+
+api.get('/admin/statistics', async (c) => {
+ const { count: mailCount } = await c.env.DB.prepare(`
+ SELECT count(*) as count FROM mails`
+ ).first();
+ const { count: addressCount } = await c.env.DB.prepare(`
+ SELECT count(*) as count FROM address`
+ ).first();
+ const { count: activeUserCount7days } = await c.env.DB.prepare(`
+ SELECT count(*) as count FROM address where updated_at > datetime('now', '-7 day')`
+ ).first();
+ return c.json({
+ mailCount: mailCount,
+ userCount: addressCount,
+ activeUserCount7days: activeUserCount7days
+ })
+});
+
+export { api }
diff --git a/worker/src/api_v1.js b/worker/src/api_v1.js
new file mode 100644
index 00000000..3e681bc9
--- /dev/null
+++ b/worker/src/api_v1.js
@@ -0,0 +1,114 @@
+import { Hono } from 'hono'
+
+// api v1 is deprecated
+const api = new Hono()
+
+api.get('/admin/v1/mails', async (c) => {
+ const { address, limit, offset } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ const { results } = await c.env.DB.prepare(
+ `SELECT id, source, subject, message FROM mails where address = ? order by id desc limit ? offset ? `
+ ).bind(address, limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: mailCount } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM mails where address = ? `
+ ).bind(address).first();
+ count = mailCount;
+ }
+ return c.json({
+ results: results,
+ count: count
+ })
+});
+
+api.get('/admin/v1/mails_unknow', async (c) => {
+ const { limit, offset } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ const { results } = await c.env.DB.prepare(`
+ SELECT id, source, subject, message FROM mails
+ where address NOT IN(select concat('${c.env.PREFIX}', name) from address)
+ order by id desc limit ? offset ? `
+ ).bind(limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: mailCount } = await c.env.DB.prepare(`
+ SELECT count(*) as count FROM mails
+ where address NOT IN
+ (select concat('${c.env.PREFIX}', name) from address)`
+ ).first();
+ count = mailCount;
+ }
+ return c.json({
+ results: results,
+ count: count
+ })
+});
+
+api.get('/api/v1/mails', async (c) => {
+ const { address } = c.get("jwtPayload")
+ if (!address) {
+ return c.json({ "error": "No address" }, 400)
+ }
+ const { limit, offset } = c.req.query();
+ if (!limit || limit < 0 || limit > 100) {
+ return c.text("Invalid limit", 400)
+ }
+ if (!offset || offset < 0) {
+ return c.text("Invalid offset", 400)
+ }
+ const { results } = await c.env.DB.prepare(
+ `SELECT id, source, subject, message, message_id, created_at FROM mails where address = ? order by id desc limit ? offset ?`
+ ).bind(address, limit, offset).all();
+ let count = 0;
+ if (offset == 0) {
+ const { count: mailCount } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM mails where address = ?`
+ ).bind(address).first();
+ count = mailCount;
+ }
+ // add attachments
+ let attachmentResults = [];
+ const message_ids = results.map((r) => r.message_id).filter((r) => r);
+ if (message_ids && message_ids.length > 0) {
+ const { results: innerAttachmentResults } = await c.env.DB.prepare(
+ `SELECT id, message_id FROM attachments where message_id in (${message_ids.map((id) => `'${id}'`).join(",")})`
+ ).all();
+ attachmentResults = innerAttachmentResults || [];
+ }
+ results.forEach((r) => {
+ const attachment_id = attachmentResults.filter((ar) => ar.message_id == r.message_id).map((ar) => ar.id);
+ if (attachment_id && attachment_id.length > 0) {
+ r.attachment_id = attachment_id[0];
+ }
+ delete r.message_id;
+ })
+ return c.json({
+ results: results,
+ count: count
+ })
+})
+
+// attachments
+api.get("/api/v1/attachment/:attachment_id", async (c) => {
+ const { attachment_id } = c.req.param();
+ const { data } = await c.env.DB.prepare(
+ `SELECT data FROM attachments where id = ? `
+ ).bind(attachment_id).first();
+ if (!data) {
+ return c.text("Not found", 404)
+ }
+ return c.json(JSON.parse(data))
+})
+
+export { api }
diff --git a/worker/src/email.js b/worker/src/email.js
index 43b3246e..3e31493f 100644
--- a/worker/src/email.js
+++ b/worker/src/email.js
@@ -1,8 +1,5 @@
import { createMimeMessage } from "mimetext";
import { EmailMessage } from "cloudflare:email";
-import { simpleParser } from 'mailparser';
-import PostalMime from 'postal-mime';
-global.setImmediate = (callback) => callback();
async function email(message, env, ctx) {
if (env.BLACK_LIST && env.BLACK_LIST.split(",").some(word => message.from.includes(word))) {
@@ -23,34 +20,17 @@ async function email(message, env, ctx) {
}
const message_id = message.headers.get("Message-ID");
- let parsedEmail = {};
- // todo fix this
- if (!message.from.endsWith("@mega.nz")) {
- try {
- parsedEmail = await simpleParser(rawEmail)
- } catch (error) {
- console.log(error)
- }
- }
-
- if (!parsedEmail.html && !parsedEmail.textAsHtml && !parsedEmail.text) {
- console.log("Failed parse email, try postal-mime");
- parsedEmail = await PostalMime.parse(rawEmail);
- }
-
- // process email
+ // save email
const { success } = await env.DB.prepare(
- `INSERT INTO mails (source, address, subject, message, message_id) VALUES (?, ?, ?, ?, ?)`
+ `INSERT INTO raw_mails (source, address, raw, message_id) VALUES (?, ?, ?, ?)`
).bind(
- message.from, message.to,
- parsedEmail.subject || "",
- parsedEmail.html || parsedEmail.textAsHtml || parsedEmail.text || "",
- message_id
+ message.from, message.to, rawEmail, message_id
).run();
if (!success) {
message.setReject(`Failed save message to ${message.to}`);
console.log(`Failed save message from ${message.from} to ${message.to}`);
}
+
// auto reply email
try {
const results = await env.DB.prepare(
@@ -80,28 +60,6 @@ async function email(message, env, ctx) {
} catch (error) {
console.log("reply email error", error);
}
- // process attachments
- try {
- if (
- env.ENABLE_ATTACHMENT
- && parsedEmail.attachments
- && parsedEmail.attachments.length > 0
- ) {
- const { success } = await env.DB.prepare(
- `INSERT INTO attachments (source, address, message_id, data) VALUES (?, ?, ?, ?)`
- ).bind(
- message.from, message.to, message_id,
- JSON.stringify(parsedEmail.attachments)
- ).run();
- if (!success) {
- message.setReject(`Failed save attachment to ${message.to}`);
- console.log(`Failed save attachment from ${message.from} to ${message.to}`);
- }
- }
- }
- catch (error) {
- console.log("save attachment error", error);
- }
} else {
message.setReject(`Unknown address ${message.to}`);
console.log(`Unknown address ${message.to}`);
diff --git a/worker/src/router.js b/worker/src/router.js
index a8e20a6f..7b17df22 100644
--- a/worker/src/router.js
+++ b/worker/src/router.js
@@ -16,31 +16,15 @@ api.get('/api/mails', async (c) => {
return c.text("Invalid offset", 400)
}
const { results } = await c.env.DB.prepare(
- `SELECT id, source, subject, message, message_id, created_at FROM mails where address = ? order by id desc limit ? offset ?`
+ `SELECT id, source, raw, created_at FROM raw_mails where address = ? order by id desc limit ? offset ?`
).bind(address, limit, offset).all();
let count = 0;
if (offset == 0) {
const { count: mailCount } = await c.env.DB.prepare(
- `SELECT count(*) as count FROM mails where address = ?`
+ `SELECT count(*) as count FROM raw_mails where address = ?`
).bind(address).first();
count = mailCount;
}
- // add attachments
- let attachmentResults = [];
- const message_ids = results.map((r) => r.message_id).filter((r) => r);
- if (message_ids && message_ids.length > 0) {
- const { results: innerAttachmentResults } = await c.env.DB.prepare(
- `SELECT id, message_id FROM attachments where message_id in (${message_ids.map((id) => `'${id}'`).join(",")})`
- ).all();
- attachmentResults = innerAttachmentResults || [];
- }
- results.forEach((r) => {
- const attachment_id = attachmentResults.filter((ar) => ar.message_id == r.message_id).map((ar) => ar.id);
- if (attachment_id && attachment_id.length > 0) {
- r.attachment_id = attachment_id[0];
- }
- delete r.message_id;
- })
return c.json({
results: results,
count: count
@@ -84,28 +68,29 @@ api.get('/api/settings', async (c) => {
console.warn("Failed to update address")
}
}
+ let auto_reply = {};
const results = await c.env.DB.prepare(
`SELECT * FROM auto_reply_mails where address = ? `
).bind(address).first();
- if (!results) {
- return c.json({
- auto_reply: {},
- address: address
- });
- }
- return c.json({
- auto_reply: {
+ if (results) {
+ auto_reply = {
subject: results.subject,
message: results.message,
enabled: results.enabled == 1,
source_prefix: results.source_prefix,
name: results.name,
- },
- address: address
+ }
+ }
+ const { count: mailCountV1 } = await c.env.DB.prepare(
+ `SELECT count(*) as count FROM mails where address = ?`
+ ).bind(address).first();
+ return c.json({
+ auto_reply: auto_reply,
+ address: address,
+ has_v1_mails: mailCountV1 > 0
});
})
-
api.post('/api/settings', async (c) => {
const { address } = c.get("jwtPayload")
const { auto_reply } = await c.req.json();
@@ -211,169 +196,4 @@ api.delete('/api/delete_address', async (c) => {
})
})
-api.get('/admin/address', async (c) => {
- const { limit, offset, query } = c.req.query();
- if (!limit || limit < 0 || limit > 100) {
- return c.text("Invalid limit", 400)
- }
- if (!offset || offset < 0) {
- return c.text("Invalid offset", 400)
- }
- if (query) {
- const { results } = await c.env.DB.prepare(
- `SELECT * FROM address where concat('${c.env.PREFIX}', name) like ? order by id desc limit ? offset ? `
- ).bind(`%${query}%`, limit, offset).all();
- let count = 0;
- if (offset == 0) {
- const { count: addressCount } = await c.env.DB.prepare(
- `SELECT count(*) as count FROM address where concat('${c.env.PREFIX}', name) like ?`
- ).bind(`%${query}%`).first();
- count = addressCount;
- }
- return c.json({
- results: results.map((r) => {
- r.name = c.env.PREFIX + r.name;
- return r;
- }),
- count: count
- })
- }
- const { results } = await c.env.DB.prepare(
- `SELECT * FROM address order by id desc limit ? offset ? `
- ).bind(limit, offset).all();
- let count = 0;
- if (offset == 0) {
- const { count: addressCount } = await c.env.DB.prepare(
- `SELECT count(*) as count FROM address`
- ).first();
- count = addressCount;
- }
- return c.json({
- results: results.map((r) => {
- r.name = c.env.PREFIX + r.name;
- return r;
- }),
- count: count
- })
-})
-
-api.delete('/admin/delete_address/:id', async (c) => {
- const { id } = c.req.param();
- const { success } = await c.env.DB.prepare(
- `DELETE FROM address WHERE id = ? `
- ).bind(id).run();
- if (!success) {
- return c.text("Failed to delete address", 500)
- }
- const { success: mailSuccess } = await c.env.DB.prepare(
- `DELETE FROM mails WHERE address IN
- (select concat('${c.env.PREFIX}', name) from address where id = ?) `
- ).bind(id).run();
- if (!mailSuccess) {
- return c.text("Failed to delete mails", 500)
- }
- return c.json({
- success: success
- })
-})
-
-api.get('/admin/show_password/:id', async (c) => {
- const { id } = c.req.param();
- const name = await c.env.DB.prepare(
- `SELECT name FROM address WHERE id = ? `
- ).bind(id).first("name");
- // compute address
- const emailAddress = c.env.PREFIX + name
- const jwt = await Jwt.sign({
- address: emailAddress,
- address_id: id
- }, c.env.JWT_SECRET)
- return c.json({
- password: jwt
- })
-})
-
-
-api.get('/admin/mails', async (c) => {
- const { address, limit, offset } = c.req.query();
- if (!limit || limit < 0 || limit > 100) {
- return c.text("Invalid limit", 400)
- }
- if (!offset || offset < 0) {
- return c.text("Invalid offset", 400)
- }
- const { results } = await c.env.DB.prepare(
- `SELECT id, source, subject, message FROM mails where address = ? order by id desc limit ? offset ? `
- ).bind(address, limit, offset).all();
- let count = 0;
- if (offset == 0) {
- const { count: mailCount } = await c.env.DB.prepare(
- `SELECT count(*) as count FROM mails where address = ? `
- ).bind(address).first();
- count = mailCount;
- }
- return c.json({
- results: results,
- count: count
- })
-});
-
-api.get('/admin/mails_unknow', async (c) => {
- const { limit, offset } = c.req.query();
- if (!limit || limit < 0 || limit > 100) {
- return c.text("Invalid limit", 400)
- }
- if (!offset || offset < 0) {
- return c.text("Invalid offset", 400)
- }
- const { results } = await c.env.DB.prepare(`
- SELECT id, source, subject, message FROM mails
- where address NOT IN(select concat('${c.env.PREFIX}', name) from address)
- order by id desc limit ? offset ? `
- ).bind(limit, offset).all();
- let count = 0;
- if (offset == 0) {
- const { count: mailCount } = await c.env.DB.prepare(`
- SELECT count(*) as count FROM mails
- where address NOT IN
- (select concat('${c.env.PREFIX}', name) from address)`
- ).first();
- count = mailCount;
- }
- return c.json({
- results: results,
- count: count
- })
-});
-
-
-api.get('/admin/statistics', async (c) => {
- const { count: mailCount } = await c.env.DB.prepare(`
- SELECT count(*) as count FROM mails`
- ).first();
- const { count: addressCount } = await c.env.DB.prepare(`
- SELECT count(*) as count FROM address`
- ).first();
- const { count: activeUserCount7days } = await c.env.DB.prepare(`
- SELECT count(*) as count FROM address where updated_at > datetime('now', '-7 day')`
- ).first();
- return c.json({
- mailCount: mailCount,
- userCount: addressCount,
- activeUserCount7days: activeUserCount7days
- })
-});
-
-// attachments
-api.get("/api/attachment/:attachment_id", async (c) => {
- const { attachment_id } = c.req.param();
- const { data } = await c.env.DB.prepare(
- `SELECT data FROM attachments where id = ? `
- ).bind(attachment_id).first();
- if (!data) {
- return c.text("Not found", 404)
- }
- return c.json(JSON.parse(data))
-})
-
export { api }
diff --git a/worker/src/worker.js b/worker/src/worker.js
index 689e0fa4..b858ae1c 100644
--- a/worker/src/worker.js
+++ b/worker/src/worker.js
@@ -3,6 +3,8 @@ import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt'
import { api } from './router';
+import { api as adminApi } from './admin_api';
+import { api as apiV1 } from './api_v1';
import { email } from './email';
const app = new Hono()
@@ -36,8 +38,10 @@ app.use('/admin/*', async (c, next) => {
app.route('/', api)
+app.route('/', adminApi)
+app.route('/', apiV1)
-app.all('/*', async c => c.html(`Hello World
`))
+app.all('/*', async c => c.text("Not Found", 404))
export default {
diff --git a/worker/wrangler.toml.template b/worker/wrangler.toml.template
index 6a1f8893..fb696159 100644
--- a/worker/wrangler.toml.template
+++ b/worker/wrangler.toml.template
@@ -1,6 +1,6 @@
name = "cloudflare_temp_email"
main = "src/worker.js"
-compatibility_date = "2023-08-14"
+compatibility_date = "2023-12-01"
node_compat = true
[vars]
@@ -12,8 +12,6 @@ PREFIX = "tmp"
DOMAINS = ["xxx.xxx1" , "xxx.xxx2"]
JWT_SECRET = "xxx"
BLACK_LIST = ""
-# IF YOU WANT DISABLE ATTACHMENT, SET IT TO false or COMMENT IT
-ENABLE_ATTACHMENT = true
[[d1_databases]]
binding = "DB"