Files
webmailserver/app/api/webhooks/mail-signal/route.ts
2026-05-14 19:09:09 +03:00

129 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getLatestEmail } from '@/lib/mail';
import { sendTelegramNotification } from '@/lib/notifications';
// Bu kısım normalde .env içinde olmalı
const WEBHOOK_SECRET = process.env.WEBHOOK_SIGNAL_SECRET || 'besiktasK1903*';
const IMAP_PASSWORD = process.env.MAILCOW_MASTER_PASSWORD || ''; // Dovecot Master Password tavsiye edilir
// RFC 2047 Decode Fonksiyonu
function decodeMimeText(text: string) {
if (!text) return text;
// Eğer metin =? ile başlıyorsa decode etmeye çalış
return text.replace(/=\?([^?]+)\?([QB])\?([^?]+)\?=/gi, (match, charset, encoding, data) => {
try {
if (encoding.toUpperCase() === 'Q') {
// Quoted-Printable decode
return data
.replace(/_/g, ' ')
.replace(/=([0-9A-F]{2})/gi, (_: any, hex: string) => String.fromCharCode(parseInt(hex, 16)));
} else if (encoding.toUpperCase() === 'B') {
// Base64 decode
return Buffer.from(data, 'base64').toString(charset.toLowerCase() === 'utf-8' ? 'utf8' : 'binary');
}
} catch (e) {
return match;
}
return match;
});
}
export async function POST(request: Request) {
const secret = request.headers.get('x-ayristech-secret');
if (secret !== WEBHOOK_SECRET) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await request.json();
const { to, event, subject: incomingSubject, snippet: incomingSnippet, from: incomingFrom } = body;
const subject = decodeMimeText(incomingSubject || "");
console.log(`📩 Webhook Sinyali Alındı! Alıcı: ${to} | Konu: ${subject}`);
try {
// 1. Mailbox Mapping kontrolü
const mapping = await prisma.mailboxMapping.findUnique({
where: { email: to },
include: { user: true }
});
if (!mapping) {
console.log(`[Signal] Mapping bulunamadı: ${to}`);
return NextResponse.json({ success: true, message: 'No mapping found' });
}
// 2. Mail İçeriğini Belirle
let mailData = null;
if (incomingSubject || incomingSnippet) {
console.log(`[Signal] İçerik worker'dan hazır geldi: ${subject}`);
mailData = {
subject: subject || "(Konu Yok)",
text: incomingSnippet || "",
from: incomingFrom || "Bilinmiyor"
};
} else {
console.log("[Signal] İçerik eksik, IMAP'e gidiliyor...");
mailData = await getLatestEmail(to, IMAP_PASSWORD);
}
if (!mailData) {
console.error(`[Signal] Mail içeriği çekilemedi: ${to}`);
return NextResponse.json({ success: false, error: 'Could not fetch mail' }, { status: 500 });
}
// 3. İçerik Analizi (BMW, Penti vb.)
let processed = false;
let extraInfo = "";
const analysisContent = (mailData.subject + " " + mailData.text).toLowerCase();
if (analysisContent.includes("bmw") || analysisContent.includes("tamir")) {
console.log("🚗 [Signal] BMW/Tamir içerikli mail tespit edildi!");
extraInfo = "🚗 BMW/Tamir İlgili İçerik";
processed = true;
}
if (analysisContent.includes("penti") || analysisContent.includes("sipariş")) {
console.log("🛍️ [Signal] Penti/Sipariş içerikli mail tespit edildi!");
extraInfo = "🛍️ Penti/Sipariş İlgili İçerik";
processed = true;
}
// 4. Bildirim Gönder (Telegram)
const notificationResult = await sendTelegramNotification(
mapping.userId,
to,
mailData.from,
mailData.subject,
extraInfo
);
// 5. Bildirim Logu
await prisma.notificationLog.create({
data: {
mailbox: to,
sender: mailData.from,
subject: mailData.subject,
status: notificationResult.status,
userId: mapping.userId,
error: notificationResult.error || (processed ? null : "Anahtar kelime eşleşmedi")
}
});
return NextResponse.json({
success: true,
processed,
notification: notificationResult.status,
subject: mailData.subject,
mode: incomingSubject ? 'worker-data' : 'imap-fallback'
});
} catch (error: any) {
console.error("[Signal Webhook] Hata:", error.message);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}