mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-07 00:17:12 +08:00
feat: add resend for send mail (#274)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
- UI lazy load
|
- UI lazy load
|
||||||
- telegram bot 添加用户全局推送功能
|
- telegram bot 添加用户全局推送功能
|
||||||
- 增加对 cloudflare verified 用户发送邮件
|
- 增加对 cloudflare verified 用户发送邮件
|
||||||
|
- 增加使用 `resend` 发送邮件
|
||||||
|
|
||||||
## v0.4.4
|
## v0.4.4
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
> 本项目仅供学习和个人用途,请勿将其用于任何违法行为,否则后果自负。
|
> 本项目仅供学习和个人用途,请勿将其用于任何违法行为,否则后果自负。
|
||||||
>
|
|
||||||
> [Mail Channels 免费电子邮件发送 API 将于2024年6月30日结束](https://support.mailchannels.com/hc/en-us/articles/26814255454093-End-of-Life-Notice-Cloudflare-Workers)
|
|
||||||
>
|
|
||||||
> 正在集成 Resend 发送邮件
|
|
||||||
|
|
||||||
## [查看部署文档](https://temp-mail-docs.awsl.uk)
|
## [查看部署文档](https://temp-mail-docs.awsl.uk)
|
||||||
|
|
||||||
|
|||||||
@@ -62,13 +62,21 @@ const refresh = async () => {
|
|||||||
data.value = results.map((item) => {
|
data.value = results.map((item) => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(item.raw);
|
const data = JSON.parse(item.raw);
|
||||||
item.to_mail = data?.personalizations?.map(
|
if (data.version == "v2") {
|
||||||
(p) => p.to?.map((t) => t.email).join(',')
|
item.to_mail = data.to_name ? `${data.to_name} <${data.to_mail}>` : data.to_mail;
|
||||||
).join(';');
|
item.subject = data.subject;
|
||||||
item.subject = data.subject;
|
item.is_html = data.is_html;
|
||||||
item.contentType = data.content[0]?.type;
|
item.content = data.content;
|
||||||
item.content = data.content[0]?.value;
|
item.raw = JSON.stringify(data, null, 2);
|
||||||
item.raw = JSON.stringify(data, null, 2);
|
} else {
|
||||||
|
item.to_mail = data?.personalizations?.map(
|
||||||
|
(p) => p.to?.map((t) => t.email).join(',')
|
||||||
|
).join(';');
|
||||||
|
item.subject = data.subject;
|
||||||
|
item.is_html = (data.content[0]?.type != 'text/plain');
|
||||||
|
item.content = data.content[0]?.value;
|
||||||
|
item.raw = JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
@@ -160,7 +168,7 @@ onMounted(async () => {
|
|||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
||||||
<pre v-else-if="curMail.contentType == 'text/plain'" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
<pre v-else-if="!curMail.is_html" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
||||||
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card class="mail-item" v-else>
|
<n-card class="mail-item" v-else>
|
||||||
@@ -219,7 +227,7 @@ onMounted(async () => {
|
|||||||
</n-tag>
|
</n-tag>
|
||||||
</n-space>
|
</n-space>
|
||||||
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
<pre v-if="showCode" style="margin-top: 10px;">{{ curMail.raw }}</pre>
|
||||||
<pre v-else-if="curMail.contentType == 'text/plain'" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
<pre v-else-if="!curMail.is_html" style="margin-top: 10px;">{{ curMail.content }}</pre>
|
||||||
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
<div v-else v-html="curMail.content" style="margin-top: 10px;"></div>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-drawer-content>
|
</n-drawer-content>
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
|
|
||||||
# 配置发送邮件
|
# 配置发送邮件
|
||||||
|
|
||||||
|
## 使用 Cloudflare Workers 给已认证的邮箱发送邮件
|
||||||
|
|
||||||
|
admin 后台 账号配置 `已验证地址列表(可通过 cf 内部 api 发送邮件)`
|
||||||
|
|
||||||
|
## 使用 resend 发送邮件
|
||||||
|
|
||||||
|
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
||||||
|
|
||||||
|
`API KEYS` 页面创建 `api key`
|
||||||
|
|
||||||
|
使用 cli 或者直接添加到 `wrangler.toml` 的 `vars`,或者在 cloudflare worker 页面的变量中添加 `RESEND_TOKEN`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wrangler secret put RESEND_TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你有多个域名,对应不同的 `api key`,可以在 `wrangler.toml` 中添加多个 secret, 名称为 `RESEND_TOKEN_` + `<. 换成 _ 的 大写域名>`,例如
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wrangler secret put RESEND_TOKEN_XXX_COM
|
||||||
|
wrangler secret put RESEND_TOKEN_DREAMHUNTER2333_XYZ
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用 mailchannels 发送邮件
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
[Mail Channels 免费电子邮件发送 API 将于2024年6月30日结束](https://support.mailchannels.com/hc/en-us/articles/26814255454093-End-of-Life-Notice-Cloudflare-Workers)
|
||||||
|
:::
|
||||||
|
|
||||||
1. 找到域名 `DNS` 记录的 `TXT` 的 `SPF` 记录, 增加 `include:relay.mailchannels.net`
|
1. 找到域名 `DNS` 记录的 `TXT` 的 `SPF` 记录, 增加 `include:relay.mailchannels.net`
|
||||||
|
|
||||||
`v=spf1 include:_spf.mx.cloudflare.net include:relay.mailchannels.net ~all`
|
`v=spf1 include:_spf.mx.cloudflare.net include:relay.mailchannels.net ~all`
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
languageOptions: { globals: globals.browser },
|
||||||
|
},
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -5,18 +5,24 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
|
"lint": "eslint src",
|
||||||
"deploy": "wrangler deploy --minify",
|
"deploy": "wrangler deploy --minify",
|
||||||
"start": "wrangler dev",
|
"start": "wrangler dev",
|
||||||
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
"build": "wrangler deploy --dry-run --outdir dist --minify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20240512.0",
|
"@cloudflare/workers-types": "^4.20240512.0",
|
||||||
|
"@eslint/js": "8.56.0",
|
||||||
|
"eslint": "8.56.0",
|
||||||
|
"globals": "^15.3.0",
|
||||||
|
"typescript-eslint": "^7.10.0",
|
||||||
"wrangler": "^3.57.1"
|
"wrangler": "^3.57.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hono": "^4.3.9",
|
"hono": "^4.3.9",
|
||||||
"mimetext": "^3.0.24",
|
"mimetext": "^3.0.24",
|
||||||
"postal-mime": "^2.2.5",
|
"postal-mime": "^2.2.5",
|
||||||
|
"resend": "^3.2.0",
|
||||||
"telegraf": "4.16.3"
|
"telegraf": "4.16.3"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
|||||||
Generated
+1390
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ api.get('/admin/address', async (c) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
api.post('/admin/new_address', async (c) => {
|
api.post('/admin/new_address', async (c) => {
|
||||||
let { name, domain, enablePrefix } = await c.req.json();
|
const { name, domain, enablePrefix } = await c.req.json();
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return c.text("Please provide a name", 400)
|
return c.text("Please provide a name", 400)
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,9 @@ api.get('/admin/address_sender', async (c) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
api.post('/admin/address_sender', async (c) => {
|
api.post('/admin/address_sender', async (c) => {
|
||||||
|
/* eslint-disable prefer-const */
|
||||||
let { address, address_id, balance, enabled } = await c.req.json();
|
let { address, address_id, balance, enabled } = await c.req.json();
|
||||||
|
/* eslint-enable prefer-const */
|
||||||
if (!address_id) {
|
if (!address_id) {
|
||||||
return c.text("Invalid address_id", 400)
|
return c.text("Invalid address_id", 400)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Jwt } from 'hono/utils/jwt'
|
|||||||
|
|
||||||
import { getBooleanValue, getDomains, getStringValue } from './utils';
|
import { getBooleanValue, getDomains, getStringValue } from './utils';
|
||||||
import { HonoCustomType } from './types';
|
import { HonoCustomType } from './types';
|
||||||
import { CONSTANTS } from './constants';
|
|
||||||
import { unbindTelegramByAddress } from './telegram_api/common';
|
import { unbindTelegramByAddress } from './telegram_api/common';
|
||||||
|
|
||||||
export const newAddress = async (
|
export const newAddress = async (
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ api.post('/api/new_address', async (c) => {
|
|||||||
if (!getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL)) {
|
if (!getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL)) {
|
||||||
return c.text("New address is disabled", 403)
|
return c.text("New address is disabled", 403)
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let { name, domain, cf_token } = await c.req.json();
|
let { name, domain, cf_token } = await c.req.json();
|
||||||
// check cf turnstile
|
// check cf turnstile
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Context, Hono } from 'hono'
|
import { Context, Hono } from 'hono'
|
||||||
import { Jwt } from 'hono/utils/jwt'
|
import { Jwt } from 'hono/utils/jwt'
|
||||||
import { createMimeMessage } from 'mimetext';
|
import { createMimeMessage } from 'mimetext';
|
||||||
|
import { Resend } from 'resend';
|
||||||
|
|
||||||
import { CONSTANTS } from '../constants'
|
import { CONSTANTS } from '../constants'
|
||||||
import { getJsonSetting, getDomains, getIntValue } from '../utils';
|
import { getJsonSetting, getDomains, getIntValue } from '../utils';
|
||||||
@@ -29,7 +30,7 @@ api.post('/api/requset_send_mail_access', async (c) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = (e as Error).message;
|
const message = (e as Error).message;
|
||||||
if (message && message.includes("UNIQUE")) {
|
if (message && message.includes("UNIQUE")) {
|
||||||
throw new Error("Address already requested")
|
return c.text("Already requested", 400)
|
||||||
}
|
}
|
||||||
return c.text("Failed to request send mail access", 500)
|
return c.text("Failed to request send mail access", 500)
|
||||||
}
|
}
|
||||||
@@ -42,14 +43,14 @@ export const sendMailToVerifyAddress = async (
|
|||||||
from_name: string, to_mail: string, to_name: string,
|
from_name: string, to_mail: string, to_name: string,
|
||||||
subject: string, content: string, is_html: boolean
|
subject: string, content: string, is_html: boolean
|
||||||
}
|
}
|
||||||
) => {
|
): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
from_name, to_mail, to_name,
|
from_name, to_mail, to_name,
|
||||||
subject, content, is_html
|
subject, content, is_html
|
||||||
} = reqJson;
|
} = reqJson;
|
||||||
const msg = createMimeMessage();
|
const msg = createMimeMessage();
|
||||||
msg.setSender({ name: from_name, addr: address });
|
msg.setSender(from_name ? { name: from_name, addr: address } : address);
|
||||||
msg.setRecipient({ name: to_name, addr: to_mail });
|
msg.setRecipient(to_name ? { name: to_name, addr: to_mail } : to_mail);
|
||||||
msg.setSubject(subject);
|
msg.setSubject(subject);
|
||||||
msg.addMessage({
|
msg.addMessage({
|
||||||
contentType: is_html ? 'text/html' : 'text/plain',
|
contentType: is_html ? 'text/html' : 'text/plain',
|
||||||
@@ -60,59 +61,49 @@ export const sendMailToVerifyAddress = async (
|
|||||||
await c.env.SEND_MAIL.send(message);
|
await c.env.SEND_MAIL.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sendMail = async (
|
const sendMailByResend = async (
|
||||||
c: Context<HonoCustomType>, address: string,
|
c: Context<HonoCustomType>, address: string,
|
||||||
reqJson: {
|
reqJson: {
|
||||||
from_name: string, to_mail: string, to_name: string,
|
from_name: string, to_mail: string, to_name: string,
|
||||||
subject: string, content: string, is_html: boolean
|
subject: string, content: string, is_html: boolean
|
||||||
}
|
}
|
||||||
) => {
|
): Promise<void> => {
|
||||||
if (!address) {
|
|
||||||
throw new Error("No address")
|
|
||||||
}
|
|
||||||
// check domain
|
|
||||||
const mailDomain = address.split("@")[1];
|
const mailDomain = address.split("@")[1];
|
||||||
const domains = getDomains(c);
|
const token = c.env[
|
||||||
if (!domains.includes(mailDomain)) {
|
`RESEND_TOKEN_${mailDomain.replace(/\./g, "_").toUpperCase()}`
|
||||||
throw new Error("Invalid domain")
|
] || c.env.RESEND_TOKEN;
|
||||||
|
const resend = new Resend(token);
|
||||||
|
const { data, error } = await resend.emails.send({
|
||||||
|
from: reqJson.from_name ? `${reqJson.from_name} <${address}>` : address,
|
||||||
|
to: reqJson.to_name ? `${reqJson.to_name} <${reqJson.to_mail}>` : reqJson.to_mail,
|
||||||
|
subject: reqJson.subject,
|
||||||
|
...(reqJson.is_html ? {
|
||||||
|
html: reqJson.content,
|
||||||
|
} : {
|
||||||
|
text: reqJson.content,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Resend error: ${error.name} ${error.message}`);
|
||||||
}
|
}
|
||||||
// check permission
|
console.log(`Resend success: ${data}`);
|
||||||
const balance = await c.env.DB.prepare(
|
}
|
||||||
`SELECT balance FROM address_sender
|
|
||||||
where address = ? and enabled = 1`
|
const sendMailByMailChannels = async (
|
||||||
).bind(address).first<number>("balance");
|
c: Context<HonoCustomType>, address: string,
|
||||||
if (!balance || balance <= 0) {
|
reqJson: {
|
||||||
throw new Error("No balance")
|
from_name: string, to_mail: string, to_name: string,
|
||||||
|
subject: string, content: string, is_html: boolean
|
||||||
}
|
}
|
||||||
|
): Promise<void> => {
|
||||||
|
/* eslint-disable prefer-const */
|
||||||
let {
|
let {
|
||||||
from_name, to_mail, to_name,
|
from_name, to_mail, to_name,
|
||||||
subject, content, is_html
|
subject, content, is_html
|
||||||
} = reqJson;
|
} = reqJson;
|
||||||
if (!to_mail) {
|
/* eslint-enable prefer-const */
|
||||||
throw new Error("Invalid to mail")
|
|
||||||
}
|
|
||||||
// check SEND_BLOCK_LIST_KEY
|
|
||||||
const sendBlockList = await getJsonSetting(c, CONSTANTS.SEND_BLOCK_LIST_KEY) as string[];
|
|
||||||
if (sendBlockList && sendBlockList.some((item) => to_mail.includes(item))) {
|
|
||||||
throw new Error("to_mail address is blocked")
|
|
||||||
}
|
|
||||||
from_name = from_name || address;
|
from_name = from_name || address;
|
||||||
to_name = to_name || to_mail;
|
to_name = to_name || to_mail;
|
||||||
if (!subject) {
|
|
||||||
throw new Error("Invalid subject")
|
|
||||||
}
|
|
||||||
if (!content) {
|
|
||||||
throw new Error("Invalid content")
|
|
||||||
}
|
|
||||||
// send to verified address list, do not update balance
|
|
||||||
if (c.env.SEND_MAIL) {
|
|
||||||
const verifiedAddressList = await getJsonSetting(c, CONSTANTS.VERIFIED_ADDRESS_LIST_KEY) || [];
|
|
||||||
if (verifiedAddressList.includes(to_mail)) {
|
|
||||||
return sendMailToVerifyAddress(c, address, {
|
|
||||||
from_name, to_mail, to_name, subject, content, is_html
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let dmikBody = {}
|
let dmikBody = {}
|
||||||
if (c.env.DKIM_SELECTOR && c.env.DKIM_PRIVATE_KEY && address.includes("@")) {
|
if (c.env.DKIM_SELECTOR && c.env.DKIM_PRIVATE_KEY && address.includes("@")) {
|
||||||
dmikBody = {
|
dmikBody = {
|
||||||
@@ -141,7 +132,7 @@ export const sendMail = async (
|
|||||||
"value": content,
|
"value": content,
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
let send_request = new Request("https://api.mailchannels.net/tx/v1/send", {
|
const send_request = new Request("https://api.mailchannels.net/tx/v1/send", {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
@@ -154,6 +145,69 @@ export const sendMail = async (
|
|||||||
if (resp.status >= 300) {
|
if (resp.status >= 300) {
|
||||||
throw new Error(`Mailchannels error: ${resp.status} ${respText}`);
|
throw new Error(`Mailchannels error: ${resp.status} ${respText}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendMail = async (
|
||||||
|
c: Context<HonoCustomType>, address: string,
|
||||||
|
reqJson: {
|
||||||
|
from_name: string, to_mail: string, to_name: string,
|
||||||
|
subject: string, content: string, is_html: boolean
|
||||||
|
}
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!address) {
|
||||||
|
throw new Error("No address")
|
||||||
|
}
|
||||||
|
// check domain
|
||||||
|
const mailDomain = address.split("@")[1];
|
||||||
|
const domains = getDomains(c);
|
||||||
|
if (!domains.includes(mailDomain)) {
|
||||||
|
throw new Error("Invalid domain")
|
||||||
|
}
|
||||||
|
// check permission
|
||||||
|
const balance = await c.env.DB.prepare(
|
||||||
|
`SELECT balance FROM address_sender
|
||||||
|
where address = ? and enabled = 1`
|
||||||
|
).bind(address).first<number>("balance");
|
||||||
|
if (!balance || balance <= 0) {
|
||||||
|
throw new Error("No balance")
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
from_name, to_mail, to_name,
|
||||||
|
subject, content, is_html
|
||||||
|
} = reqJson;
|
||||||
|
if (!to_mail) {
|
||||||
|
throw new Error("Invalid to mail")
|
||||||
|
}
|
||||||
|
// check SEND_BLOCK_LIST_KEY
|
||||||
|
const sendBlockList = await getJsonSetting(c, CONSTANTS.SEND_BLOCK_LIST_KEY) as string[];
|
||||||
|
if (sendBlockList && sendBlockList.some((item) => to_mail.includes(item))) {
|
||||||
|
throw new Error("to_mail address is blocked")
|
||||||
|
}
|
||||||
|
if (!subject) {
|
||||||
|
throw new Error("Invalid subject")
|
||||||
|
}
|
||||||
|
if (!content) {
|
||||||
|
throw new Error("Invalid content")
|
||||||
|
}
|
||||||
|
// send to verified address list, do not update balance
|
||||||
|
const resendEnabled = c.env.RESEND_TOKEN || c.env[
|
||||||
|
`RESEND_TOKEN_${mailDomain.replace(/\./g, "_").toUpperCase()}`
|
||||||
|
];
|
||||||
|
if (c.env.SEND_MAIL) {
|
||||||
|
const verifiedAddressList = await getJsonSetting(c, CONSTANTS.VERIFIED_ADDRESS_LIST_KEY) || [];
|
||||||
|
if (verifiedAddressList.includes(to_mail)) {
|
||||||
|
await sendMailToVerifyAddress(c, address, reqJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// send by resend
|
||||||
|
else if (resendEnabled) {
|
||||||
|
await sendMailByResend(c, address, reqJson);
|
||||||
|
}
|
||||||
|
// send by mailchannels
|
||||||
|
else {
|
||||||
|
await sendMailByMailChannels(c, address, reqJson);
|
||||||
|
}
|
||||||
// update balance
|
// update balance
|
||||||
try {
|
try {
|
||||||
const { success } = await c.env.DB.prepare(
|
const { success } = await c.env.DB.prepare(
|
||||||
@@ -167,12 +221,13 @@ export const sendMail = async (
|
|||||||
}
|
}
|
||||||
// save to sendbox
|
// save to sendbox
|
||||||
try {
|
try {
|
||||||
if ((body as any)?.personalizations?.[0]?.dkim_private_key) {
|
|
||||||
delete (body as any).personalizations[0].dkim_private_key;
|
|
||||||
}
|
|
||||||
const reqIp = c.req.raw.headers.get("cf-connecting-ip")
|
const reqIp = c.req.raw.headers.get("cf-connecting-ip")
|
||||||
const geoData = new GeoData(reqIp, c.req.raw.cf as any);
|
const geoData = new GeoData(reqIp, c.req.raw.cf as any);
|
||||||
(body as any).geoData = geoData;
|
const body = {
|
||||||
|
version: "v2",
|
||||||
|
...reqJson,
|
||||||
|
geoData: geoData,
|
||||||
|
};
|
||||||
const { success: success2 } = await c.env.DB.prepare(
|
const { success: success2 } = await c.env.DB.prepare(
|
||||||
`INSERT INTO sendbox (address, raw) VALUES (?, ?)`
|
`INSERT INTO sendbox (address, raw) VALUES (?, ?)`
|
||||||
).bind(address, JSON.stringify(body)).run();
|
).bind(address, JSON.stringify(body)).run();
|
||||||
|
|||||||
@@ -59,12 +59,14 @@ async function sendWebhook(settings: WebhookSettings, formatMap: WebhookMail): P
|
|||||||
// send webhook
|
// send webhook
|
||||||
let body = settings.body;
|
let body = settings.body;
|
||||||
for (const key of Object.keys(formatMap)) {
|
for (const key of Object.keys(formatMap)) {
|
||||||
|
/* eslint-disable no-useless-escape */
|
||||||
body = body.replace(
|
body = body.replace(
|
||||||
new RegExp(`\\$\\{${key}\\}`, "g"),
|
new RegExp(`\\$\\{${key}\\}`, "g"),
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
formatMap[key as keyof WebhookMail]
|
formatMap[key as keyof WebhookMail]
|
||||||
).replace(/^"(.*)"$/, '\$1')
|
).replace(/^"(.*)"$/, '\$1')
|
||||||
);
|
);
|
||||||
|
/* eslint-enable no-useless-escape */
|
||||||
}
|
}
|
||||||
const response = await fetch(settings.url, {
|
const response = await fetch(settings.url, {
|
||||||
method: settings.method,
|
method: settings.method,
|
||||||
|
|||||||
Vendored
+4
@@ -29,6 +29,10 @@ export type Bindings = {
|
|||||||
CF_TURNSTILE_SITE_KEY: string | undefined
|
CF_TURNSTILE_SITE_KEY: string | undefined
|
||||||
CF_TURNSTILE_SECRET_KEY: string | undefined
|
CF_TURNSTILE_SECRET_KEY: string | undefined
|
||||||
|
|
||||||
|
// resend
|
||||||
|
RESEND_TOKEN: string | undefined
|
||||||
|
[key: `RESEND_TOKEN_${string}`]: string | undefined;
|
||||||
|
|
||||||
// telegram config
|
// telegram config
|
||||||
TELEGRAM_BOT_TOKEN: string
|
TELEGRAM_BOT_TOKEN: string
|
||||||
TG_MAX_ADDRESS: number | undefined
|
TG_MAX_ADDRESS: number | undefined
|
||||||
|
|||||||
+1
-1
@@ -171,7 +171,7 @@ export const checkCfTurnstile = async (
|
|||||||
throw new Error("Captcha token is required");
|
throw new Error("Captcha token is required");
|
||||||
}
|
}
|
||||||
const reqIp = c.req.raw.headers.get("cf-connecting-ip");
|
const reqIp = c.req.raw.headers.get("cf-connecting-ip");
|
||||||
let formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('secret', c.env.CF_TURNSTILE_SECRET_KEY);
|
formData.append('secret', c.env.CF_TURNSTILE_SECRET_KEY);
|
||||||
formData.append('response', token);
|
formData.append('response', token);
|
||||||
if (reqIp) formData.append('remoteip', reqIp);
|
if (reqIp) formData.append('remoteip', reqIp);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { api as apiV1 } from './deprecated';
|
|||||||
import { api as commonApi } from './commom_api';
|
import { api as commonApi } from './commom_api';
|
||||||
import { api as mailsApi } from './mails_api'
|
import { api as mailsApi } from './mails_api'
|
||||||
import { api as userApi } from './user_api';
|
import { api as userApi } from './user_api';
|
||||||
// @ts-ignore
|
|
||||||
import { api as adminApi } from './admin_api';
|
import { api as adminApi } from './admin_api';
|
||||||
import { api as apiSendMail } from './mails_api/send_mail_api'
|
import { api as apiSendMail } from './mails_api/send_mail_api'
|
||||||
import { api as telegramApi } from './telegram_api'
|
import { api as telegramApi } from './telegram_api'
|
||||||
|
|||||||
Reference in New Issue
Block a user