mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-23 17:19:47 +08:00
feat: update webhook to support global webhook (#407)
This commit is contained in:
@@ -8,6 +8,7 @@ import { CONSTANTS } from '../constants'
|
||||
import cleanup_api from './cleanup_api'
|
||||
import admin_user_api from './admin_user_api'
|
||||
import webhook_settings from './webhook_settings'
|
||||
import mail_webhook_settings from './mail_webhook_settings'
|
||||
|
||||
export const api = new Hono<HonoCustomType>()
|
||||
|
||||
@@ -291,9 +292,12 @@ api.post('/admin/account_settings', async (c) => {
|
||||
})
|
||||
})
|
||||
|
||||
// cleanup
|
||||
api.post('/admin/cleanup', cleanup_api.cleanup)
|
||||
api.get('/admin/auto_cleanup', cleanup_api.getCleanup)
|
||||
api.post('/admin/auto_cleanup', cleanup_api.saveCleanup)
|
||||
|
||||
// user settings
|
||||
api.get('/admin/user_settings', admin_user_api.getSetting)
|
||||
api.post('/admin/user_settings', admin_user_api.saveSetting)
|
||||
api.get('/admin/users', admin_user_api.getUsers)
|
||||
@@ -302,5 +306,12 @@ api.post('/admin/users', admin_user_api.createUser)
|
||||
api.post('/admin/users/:user_id/reset_password', admin_user_api.resetPassword)
|
||||
api.get('/admin/user_roles', async (c) => c.json(getUserRoles(c)))
|
||||
api.post('/admin/user_roles', admin_user_api.updateUserRoles)
|
||||
|
||||
// webhook settings
|
||||
api.get("/admin/webhook/settings", webhook_settings.getWebhookSettings);
|
||||
api.post("/admin/webhook/settings", webhook_settings.saveWebhookSettings);
|
||||
|
||||
// mail webhook settings
|
||||
api.get("/admin/mail_webhook/settings", mail_webhook_settings.getWebhookSettings);
|
||||
api.post("/admin/mail_webhook/settings", mail_webhook_settings.saveWebhookSettings);
|
||||
api.post("/admin/mail_webhook/test", mail_webhook_settings.testWebhookSettings);
|
||||
|
||||
55
worker/src/admin_api/mail_webhook_settings.ts
Normal file
55
worker/src/admin_api/mail_webhook_settings.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Context } from "hono";
|
||||
import { HonoCustomType } from "../types";
|
||||
import { CONSTANTS } from "../constants";
|
||||
import { WebhookSettings } from "../models";
|
||||
import { getBooleanValue } from "../utils";
|
||||
import { commonParseMail, sendWebhook } from "../common";
|
||||
|
||||
async function getWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
|
||||
if (!c.env.KV) {
|
||||
return c.text("KV is not available", 400);
|
||||
}
|
||||
if (!getBooleanValue(c.env.ENABLE_WEBHOOK)) {
|
||||
return c.text("Webhook is disabled", 403);
|
||||
}
|
||||
const settings = await c.env.KV.get<WebhookSettings>(
|
||||
CONSTANTS.WEBHOOK_KV_ADMIN_MAIL_SETTINGS_KEY, "json"
|
||||
) || new WebhookSettings();
|
||||
return c.json(settings);
|
||||
}
|
||||
|
||||
async function saveWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
|
||||
const settings = await c.req.json<WebhookSettings>();
|
||||
await c.env.KV.put(
|
||||
CONSTANTS.WEBHOOK_KV_ADMIN_MAIL_SETTINGS_KEY,
|
||||
JSON.stringify(settings));
|
||||
return c.json({ success: true })
|
||||
}
|
||||
|
||||
async function testWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
|
||||
const settings = await c.req.json<WebhookSettings>();
|
||||
// random raw email
|
||||
const raw = await c.env.DB.prepare(
|
||||
`SELECT raw FROM raw_mails ORDER BY RANDOM() LIMIT 1`
|
||||
).first<string>("raw");
|
||||
|
||||
const parsedEmail = await commonParseMail(raw);
|
||||
const res = await sendWebhook(settings, {
|
||||
from: parsedEmail?.sender || "test@test.com",
|
||||
to: "admin@test.com",
|
||||
subject: parsedEmail?.subject || "test subject",
|
||||
raw: raw || "test raw email",
|
||||
parsedText: parsedEmail?.text || "test parsed text",
|
||||
parsedHtml: parsedEmail?.html || "test parsed html"
|
||||
});
|
||||
if (!res.success) {
|
||||
return c.text(res.message || "send webhook error", 400);
|
||||
}
|
||||
return c.json({ success: true });
|
||||
}
|
||||
|
||||
export default {
|
||||
getWebhookSettings,
|
||||
saveWebhookSettings,
|
||||
testWebhookSettings,
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { Jwt } from 'hono/utils/jwt'
|
||||
import { getBooleanValue, getDomains, getStringValue, getIntValue, getUserRoles, getDefaultDomains } from './utils';
|
||||
import { HonoCustomType, UserRole } from './types';
|
||||
import { unbindTelegramByAddress } from './telegram_api/common';
|
||||
import { CONSTANTS } from './constants';
|
||||
import { AdminWebhookSettings, WebhookMail, WebhookSettings } from './models';
|
||||
|
||||
const DEFAULT_NAME_REGEX = /[^a-z0-9]/g;
|
||||
|
||||
@@ -273,3 +275,76 @@ export const getAllowDomains = async (c: Context<HonoCustomType>): Promise<strin
|
||||
const user_role = await commonGetUserRole(c, user.user_id);
|
||||
return user_role?.domains || getDefaultDomains(c);;
|
||||
}
|
||||
|
||||
export async function sendWebhook(settings: WebhookSettings, formatMap: WebhookMail): Promise<{ success: boolean, message?: string }> {
|
||||
// send webhook
|
||||
let body = settings.body;
|
||||
for (const key of Object.keys(formatMap)) {
|
||||
/* eslint-disable no-useless-escape */
|
||||
body = body.replace(
|
||||
new RegExp(`\\$\\{${key}\\}`, "g"),
|
||||
JSON.stringify(
|
||||
formatMap[key as keyof WebhookMail]
|
||||
).replace(/^"(.*)"$/, '\$1')
|
||||
);
|
||||
/* eslint-enable no-useless-escape */
|
||||
}
|
||||
const response = await fetch(settings.url, {
|
||||
method: settings.method,
|
||||
headers: JSON.parse(settings.headers),
|
||||
body: body
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.log("send webhook error", response.status, response.statusText);
|
||||
return { success: false, message: `send webhook error: ${response.status} ${response.statusText}` };
|
||||
}
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export async function triggerWebhook(
|
||||
c: Context<HonoCustomType>,
|
||||
address: string,
|
||||
raw_mail: string
|
||||
): Promise<void> {
|
||||
if (!c.env.KV || !getBooleanValue(c.env.ENABLE_WEBHOOK)) {
|
||||
return
|
||||
}
|
||||
const webhookList: WebhookSettings[] = []
|
||||
|
||||
// admin mail webhook
|
||||
const adminMailWebhookSettings = await c.env.KV.get<WebhookSettings>(CONSTANTS.WEBHOOK_KV_ADMIN_MAIL_SETTINGS_KEY, "json");
|
||||
if (adminMailWebhookSettings?.enabled) {
|
||||
webhookList.push(adminMailWebhookSettings)
|
||||
}
|
||||
|
||||
// user mail webhook
|
||||
const adminSettings = await c.env.KV.get<AdminWebhookSettings>(CONSTANTS.WEBHOOK_KV_SETTINGS_KEY, "json");
|
||||
if (adminSettings?.allowList.includes(address)) {
|
||||
const settings = await c.env.KV.get<WebhookSettings>(
|
||||
`${CONSTANTS.WEBHOOK_KV_USER_SETTINGS_KEY}:${address}`, "json"
|
||||
);
|
||||
if (settings?.enabled) {
|
||||
webhookList.push(settings)
|
||||
}
|
||||
}
|
||||
|
||||
// no webhook
|
||||
if (webhookList.length === 0) {
|
||||
return
|
||||
}
|
||||
const parsedEmail = await commonParseMail(raw_mail);
|
||||
const webhookMail = {
|
||||
from: parsedEmail?.sender || "",
|
||||
to: address,
|
||||
subject: parsedEmail?.subject || "",
|
||||
raw: raw_mail,
|
||||
parsedText: parsedEmail?.text || "",
|
||||
parsedHtml: parsedEmail?.html || ""
|
||||
}
|
||||
for (const settings of webhookList) {
|
||||
const res = await sendWebhook(settings, webhookMail);
|
||||
if (!res.success) {
|
||||
console.error(res.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ export const CONSTANTS = {
|
||||
WEBHOOK_KV_SETTINGS_KEY: "temp-mail-webhook-settings",
|
||||
WEBHOOK_KV_USER_SETTINGS_KEY: "temp-mail-webhook-user-settings",
|
||||
EMAIL_KV_BLACK_LIST: "temp-mail-email-black-list",
|
||||
WEBHOOK_KV_ADMIN_MAIL_SETTINGS_KEY: "temp-mail-webhook-admin-mail-settings",
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { getEnvStringList } from "../utils";
|
||||
import { sendMailToTelegram } from "../telegram_api";
|
||||
import { Bindings, HonoCustomType } from "../types";
|
||||
import { auto_reply } from "./auto_reply";
|
||||
import { trigerWebhook } from "../mails_api/webhook_settings";
|
||||
import { isBlocked } from "./black_list";
|
||||
import { triggerWebhook } from "../common";
|
||||
|
||||
|
||||
async function email(message: ForwardableEmailMessage, env: Bindings, ctx: ExecutionContext) {
|
||||
@@ -48,7 +48,7 @@ async function email(message: ForwardableEmailMessage, env: Bindings, ctx: Execu
|
||||
|
||||
// send webhook
|
||||
try {
|
||||
await trigerWebhook(
|
||||
await triggerWebhook(
|
||||
{ env: env } as Context<HonoCustomType>,
|
||||
message.to, rawEmail
|
||||
);
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
import { Context } from "hono";
|
||||
import { HonoCustomType } from "../types";
|
||||
import { CONSTANTS } from "../constants";
|
||||
import { AdminWebhookSettings, WebhookMail } from "../models";
|
||||
import { AdminWebhookSettings, WebhookSettings } from "../models";
|
||||
import { getBooleanValue } from "../utils";
|
||||
import { commonParseMail } from "../common";
|
||||
|
||||
|
||||
class WebhookSettings {
|
||||
url: string = ''
|
||||
method: string = 'POST'
|
||||
headers: string = JSON.stringify({
|
||||
"Content-Type": "application/json"
|
||||
}, null, 2)
|
||||
body: string = JSON.stringify({
|
||||
"from": "${from}",
|
||||
"to": "${to}",
|
||||
"subject": "${subject}",
|
||||
"raw": "${raw}",
|
||||
"parsedText": "${parsedText}",
|
||||
"parsedHtml": "${parsedHtml}",
|
||||
}, null, 2)
|
||||
}
|
||||
import { commonParseMail, sendWebhook } from "../common";
|
||||
|
||||
|
||||
async function getWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
|
||||
@@ -55,63 +38,6 @@ async function saveWebhookSettings(c: Context<HonoCustomType>): Promise<Response
|
||||
return c.json({ success: true })
|
||||
}
|
||||
|
||||
async function sendWebhook(settings: WebhookSettings, formatMap: WebhookMail): Promise<{ success: boolean, message?: string }> {
|
||||
// send webhook
|
||||
let body = settings.body;
|
||||
for (const key of Object.keys(formatMap)) {
|
||||
/* eslint-disable no-useless-escape */
|
||||
body = body.replace(
|
||||
new RegExp(`\\$\\{${key}\\}`, "g"),
|
||||
JSON.stringify(
|
||||
formatMap[key as keyof WebhookMail]
|
||||
).replace(/^"(.*)"$/, '\$1')
|
||||
);
|
||||
/* eslint-enable no-useless-escape */
|
||||
}
|
||||
const response = await fetch(settings.url, {
|
||||
method: settings.method,
|
||||
headers: JSON.parse(settings.headers),
|
||||
body: body
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.log("send webhook error", response.status, response.statusText);
|
||||
return { success: false, message: `send webhook error: ${response.status} ${response.statusText}` };
|
||||
}
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export async function trigerWebhook(
|
||||
c: Context<HonoCustomType>,
|
||||
address: string,
|
||||
raw_mail: string
|
||||
): Promise<void> {
|
||||
if (!c.env.KV || !getBooleanValue(c.env.ENABLE_WEBHOOK)) {
|
||||
return
|
||||
}
|
||||
const adminSettings = await c.env.KV.get<AdminWebhookSettings>(CONSTANTS.WEBHOOK_KV_SETTINGS_KEY, "json");
|
||||
if (!adminSettings?.allowList.includes(address)) {
|
||||
return;
|
||||
}
|
||||
const settings = await c.env.KV.get<WebhookSettings>(
|
||||
`${CONSTANTS.WEBHOOK_KV_USER_SETTINGS_KEY}:${address}`, "json"
|
||||
);
|
||||
if (!settings) {
|
||||
return;
|
||||
}
|
||||
const parsedEmail = await commonParseMail(raw_mail);
|
||||
const res = await sendWebhook(settings, {
|
||||
from: parsedEmail?.sender || "",
|
||||
to: address,
|
||||
subject: parsedEmail?.subject || "",
|
||||
raw: raw_mail,
|
||||
parsedText: parsedEmail?.text || "",
|
||||
parsedHtml: parsedEmail?.html || ""
|
||||
});
|
||||
if (!res.success) {
|
||||
console.log(res.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function testWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
|
||||
const settings = await c.req.json<WebhookSettings>();
|
||||
const { address } = c.get("jwtPayload");
|
||||
|
||||
@@ -119,3 +119,20 @@ export class UserInfo {
|
||||
this.userEmail = userEmail;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebhookSettings {
|
||||
enabled: boolean = false
|
||||
url: string = ''
|
||||
method: string = 'POST'
|
||||
headers: string = JSON.stringify({
|
||||
"Content-Type": "application/json"
|
||||
}, null, 2)
|
||||
body: string = JSON.stringify({
|
||||
"from": "${from}",
|
||||
"to": "${to}",
|
||||
"subject": "${subject}",
|
||||
"raw": "${raw}",
|
||||
"parsedText": "${parsedText}",
|
||||
"parsedHtml": "${parsedHtml}",
|
||||
}, null, 2)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user