Implement mail signal webhook and IMAP mail fetching logic
This commit is contained in:
82
app/api/webhooks/mail-signal/route.ts
Normal file
82
app/api/webhooks/mail-signal/route.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { prisma } from '@/lib/prisma';
|
||||||
|
import { getLatestEmail } from '@/lib/mail';
|
||||||
|
|
||||||
|
// Bu kısım normalde .env içinde olmalı
|
||||||
|
const WEBHOOK_SECRET = 'besiktasK1903*';
|
||||||
|
const IMAP_PASSWORD = process.env.MAILCOW_MASTER_PASSWORD || ''; // Dovecot Master Password tavsiye edilir
|
||||||
|
|
||||||
|
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, timestamp } = body;
|
||||||
|
|
||||||
|
console.log(`📩 Webhook Sinyali Alındı! Alıcı: ${to}`);
|
||||||
|
|
||||||
|
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. Mailcow'dan son maili çek
|
||||||
|
// Not: Master password kullanılıyorsa format 'user@domain.tld*master@domain.tld' şeklindedir
|
||||||
|
const loginUser = IMAP_PASSWORD ? `${to}*${process.env.MAILCOW_MASTER_USER || 'admin'}` : to;
|
||||||
|
const 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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Signal] Mail Çekildi: "${mailData.subject}"`);
|
||||||
|
|
||||||
|
// 3. İçerik Analizi (BMW, Penti vb.)
|
||||||
|
let processed = false;
|
||||||
|
const content = (mailData.subject + " " + mailData.text).toLowerCase();
|
||||||
|
|
||||||
|
if (content.includes("bmw") || content.includes("tamir")) {
|
||||||
|
console.log("🚗 BMW/Tamir içerikli mail tespit edildi!");
|
||||||
|
// Burada bildirim fırlatılacak
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.includes("penti") || content.includes("sipariş")) {
|
||||||
|
console.log("🛍️ Penti/Sipariş içerikli mail tespit edildi!");
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Bildirim Logu
|
||||||
|
await prisma.notificationLog.create({
|
||||||
|
data: {
|
||||||
|
mailbox: to,
|
||||||
|
sender: mailData.from,
|
||||||
|
subject: mailData.subject,
|
||||||
|
status: processed ? "SENT" : "SKIPPED",
|
||||||
|
userId: mapping.userId,
|
||||||
|
error: processed ? null : "Kritik anahtar kelime bulunamadı"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
processed,
|
||||||
|
subject: mailData.subject
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("[Signal Webhook] Hata:", error.message);
|
||||||
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
66
lib/mail.ts
Normal file
66
lib/mail.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { ImapFlow } from 'imapflow';
|
||||||
|
import { simpleParser } from 'mailparser';
|
||||||
|
|
||||||
|
export interface EmailData {
|
||||||
|
from: string;
|
||||||
|
subject: string;
|
||||||
|
text: string;
|
||||||
|
html?: string;
|
||||||
|
date: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getLatestEmail(email: string, password: string): Promise<EmailData | null> {
|
||||||
|
const host = process.env.MAILCOW_HOSTNAME || email.split('@')[1];
|
||||||
|
|
||||||
|
const client = new ImapFlow({
|
||||||
|
host: host,
|
||||||
|
port: 993,
|
||||||
|
secure: true,
|
||||||
|
auth: {
|
||||||
|
user: email,
|
||||||
|
pass: password
|
||||||
|
},
|
||||||
|
logger: false
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.connect();
|
||||||
|
|
||||||
|
// Select INBOX
|
||||||
|
let lock = await client.getMailboxLock('INBOX');
|
||||||
|
try {
|
||||||
|
// Find the last message
|
||||||
|
const messages = await client.fetch('1:*', {
|
||||||
|
envelope: true,
|
||||||
|
source: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// imapflow fetch returns an async generator or array?
|
||||||
|
// Actually fetch() returns an async iterator.
|
||||||
|
let lastMsg = null;
|
||||||
|
for await (const msg of messages) {
|
||||||
|
lastMsg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastMsg && lastMsg.source) {
|
||||||
|
const parsed = await simpleParser(lastMsg.source);
|
||||||
|
return {
|
||||||
|
from: parsed.from?.text || '',
|
||||||
|
subject: parsed.subject || '',
|
||||||
|
text: parsed.text || '',
|
||||||
|
html: parsed.html || undefined,
|
||||||
|
date: parsed.date || new Date()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.logout();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[IMAP] Error fetching mail for ${email}:`, err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user