Skip to content
64 changes: 56 additions & 8 deletions src/lib/email/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,60 @@ import { DEFAULT_EXTRACT_RESULT } from "@/types";
import type { Email, NewEmail } from "@/types";


function replaceTemplateAdvanced(template: string, email: Email): string {
return template.replace(/{(\w+)}/g, (match, key) => {
function replaceVars(str: string, email: Email): string {
const fn = (_match: string, key: string): string => {
const value = email[key as keyof Email];
if (value === null || value === undefined) {
return '';
}
return JSON.stringify(String(value)).slice(1, -1);
});
return String(value);
};
return str
.replace(/\{\{(\w+)\}\}/g, fn)
.replace(/{(\w+)}/g, fn);
}

function replaceInValue(value: unknown, email: Email): unknown {
if (typeof value === 'string') {
return replaceVars(value, email);
}
if (Array.isArray(value)) {
return value.map(item => replaceInValue(item, email));
}
if (value !== null && typeof value === 'object') {
const result: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value)) {
result[k] = replaceInValue(v, email);
}
return result;
}
return value;
}

function processTemplate(template: string, email: Email): string {
// Try to parse as JSON, walk tree, replace vars in string values, then stringify.
// This avoids all escaping issues from CI/config pipeline.
for (const input of [template, template.replace(/\\+"/g, '"')]) {
try {
const parsed = JSON.parse(input);
return JSON.stringify(replaceInValue(parsed, email));
} catch {
continue;
}
}

// Fallback: raw string replacement with JSON-safe value escaping
return template
.replace(/\{\{(\w+)\}\}/g, (_match: string, key: string) => {
const value = email[key as keyof Email];
if (value === null || value === undefined) return '';
return JSON.stringify(String(value)).slice(1, -1);
})
.replace(/{(\w+)}/g, (_match: string, key: string) => {
const value = email[key as keyof Email];
if (value === null || value === undefined) return '';
return JSON.stringify(String(value)).slice(1, -1);
});
}

export default async function storeEmail(
Expand Down Expand Up @@ -79,14 +125,16 @@ export default async function storeEmail(

const res = await emailDB.create(env, emailData);

if (env.WEBHOOK_URL && env.WEBHOOK_TEMPLATE && env.WEBHOOK_TYPE.split(',').includes(emailData.emailType)) {
await sendWebhook(replaceTemplateAdvanced(env.WEBHOOK_TEMPLATE, res), env.WEBHOOK_URL);
if (env.WEBHOOK_URL && env.WEBHOOK_TEMPLATE && env.WEBHOOK_TYPE.split(',').map(t => t.trim()).includes(emailData.emailType)) {
const webhookPayload = processTemplate(env.WEBHOOK_TEMPLATE, res);
console.log('Sending webhook to:', env.WEBHOOK_URL, 'type:', emailData.emailType, 'payload:', webhookPayload);
await sendWebhook(webhookPayload, env.WEBHOOK_URL);
}

// 发送到Telegram Bot
if (env.TELEGRAM_BOT_TOKEN && env.TELEGRAM_CHAT_ID && env.TELEGRAM_TEMPLATE && env.TELEGRAM_TYPE && env.TELEGRAM_TYPE.split(',').includes(emailData.emailType)) {
if (env.TELEGRAM_BOT_TOKEN && env.TELEGRAM_CHAT_ID && env.TELEGRAM_TEMPLATE && env.TELEGRAM_TYPE && env.TELEGRAM_TYPE.split(',').map(t => t.trim()).includes(emailData.emailType)) {
await sendTelegramMessage(
replaceTemplateAdvanced(env.TELEGRAM_TEMPLATE, res),
processTemplate(env.TELEGRAM_TEMPLATE, res),
env.TELEGRAM_BOT_TOKEN,
env.TELEGRAM_CHAT_ID
);
Expand Down
30 changes: 21 additions & 9 deletions src/lib/webhook/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,39 @@ export default async function sendWebhook(payload: string, url: string): Promise
return
}

const requestHeaders = {
'Content-Type': 'application/json',
}

try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)
const timeoutId = setTimeout(() => controller.abort(), 10000)
const response = await fetch(url, {
method: 'POST',
headers: requestHeaders,
headers: {
'Content-Type': 'application/json',
},
body: payload,
signal: controller.signal
})

clearTimeout(timeoutId)

const resBody = await response.text()

if (!response.ok) {
const errorMsg = `HTTP ${response.status}: ${response.statusText}`
console.error('Webhook error:', errorMsg)
console.error('Webhook error:', `HTTP ${response.status}: ${response.statusText}`, resBody)
return
}

try {
const result = JSON.parse(resBody)
if (result.errcode !== undefined && result.errcode !== 0) {
console.error('Webhook API error:', result)
return
}
} catch {
// response is not JSON, ignore
}

console.log('Webhook sent successfully:', url, resBody)
} catch (error) {
console.error('Webhook error:', error)
}
}
}