From f71da406d52b493347d591adaf53358f9051e327 Mon Sep 17 00:00:00 2001 From: AyrisAI Date: Thu, 14 May 2026 18:39:41 +0300 Subject: [PATCH] Implement mail signal webhook and IMAP mail fetching logic --- app/api/webhooks/mail-signal/route.ts | 82 +++++++++++++++++++++++++++ lib/mail.ts | 66 +++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 app/api/webhooks/mail-signal/route.ts create mode 100644 lib/mail.ts diff --git a/app/api/webhooks/mail-signal/route.ts b/app/api/webhooks/mail-signal/route.ts new file mode 100644 index 0000000..233e07f --- /dev/null +++ b/app/api/webhooks/mail-signal/route.ts @@ -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 }); + } +} diff --git a/lib/mail.ts b/lib/mail.ts new file mode 100644 index 0000000..8ca883b --- /dev/null +++ b/lib/mail.ts @@ -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 { + 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; +}