Initial commit - ERP System
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// /opt/erp-system/app/api/cron/imap/route.ts
|
||||
import { NextResponse } from 'next/server';
|
||||
import prisma from '../../../../lib/prisma';
|
||||
import { ImapFlow } from 'imapflow';
|
||||
import { simpleParser } from 'mailparser';
|
||||
import { writeFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
const UPLOAD_DIR = join(process.cwd(), 'uploads');
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const settings = await prisma.systemSettings.findFirst({ where: { id: 1 } });
|
||||
if (!settings || !settings.imapHost || !settings.imapUser || !settings.imapPass) {
|
||||
return NextResponse.json({ error: 'IMAP Zugangsdaten fehlen.' }, { status: 400 });
|
||||
}
|
||||
|
||||
const client = new ImapFlow({
|
||||
host: settings.imapHost,
|
||||
port: settings.imapPort,
|
||||
secure: settings.imapPort === 993,
|
||||
auth: { user: settings.imapUser, pass: settings.imapPass },
|
||||
logger: false
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
let lock = await client.getMailboxLock('INBOX');
|
||||
let processedCounter = 0;
|
||||
|
||||
try {
|
||||
for await (let message of client.fetch({ seen: false }, { source: true, uid: true })) {
|
||||
const parsed = await simpleParser(message.source);
|
||||
|
||||
const fromEmail = parsed.from?.value[0]?.address;
|
||||
const fromName = parsed.from?.value[0]?.name || 'Unbekannt';
|
||||
const subject = parsed.subject || 'Kein Betreff';
|
||||
const textContent = parsed.text || parsed.textAsHtml || '(Kein Inhalt)';
|
||||
|
||||
if (!fromEmail) continue;
|
||||
|
||||
// 1. Kunden-Matching (Haupt-Email, alternative E-Mails oder Mitarbeiter)
|
||||
let targetCustomerId = null;
|
||||
|
||||
const directCustomer = await prisma.customer.findFirst({
|
||||
where: {
|
||||
OR: [
|
||||
{ email: fromEmail },
|
||||
{ additionalEmails: { has: fromEmail } }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
if (directCustomer) {
|
||||
targetCustomerId = directCustomer.id;
|
||||
} else {
|
||||
const contact = await prisma.customerContact.findFirst({
|
||||
where: { email: fromEmail }
|
||||
});
|
||||
if (contact) {
|
||||
targetCustomerId = contact.customerId;
|
||||
}
|
||||
}
|
||||
|
||||
// Falls komplett unbekannt, neuen Kunden anlegen
|
||||
if (!targetCustomerId) {
|
||||
const newCustomer = await prisma.customer.create({
|
||||
data: {
|
||||
email: fromEmail,
|
||||
firstName: fromName,
|
||||
lastName: '(Auto-Erfasst)',
|
||||
companyName: 'Aus E-Mail Anfrage'
|
||||
}
|
||||
});
|
||||
targetCustomerId = newCustomer.id;
|
||||
}
|
||||
|
||||
// 2. Ticket Zuordnung
|
||||
const ticketMatch = subject.match(/Ticket #(\d+)/i);
|
||||
let ticketId = ticketMatch ? parseInt(ticketMatch[1]) : null;
|
||||
let activeTicketId = null;
|
||||
|
||||
if (ticketId) {
|
||||
const existingTicket = await prisma.ticket.findUnique({ where: { id: ticketId } });
|
||||
if (existingTicket) {
|
||||
activeTicketId = existingTicket.id;
|
||||
await prisma.ticketMessage.create({
|
||||
data: { content: textContent, isFromCustomer: true, ticketId: existingTicket.id }
|
||||
});
|
||||
|
||||
if (existingTicket.status !== 'OPEN' && existingTicket.status !== 'IN_PROGRESS') {
|
||||
await prisma.ticket.update({ where: { id: existingTicket.id }, data: { status: 'OPEN' } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!activeTicketId) {
|
||||
const newTicket = await prisma.ticket.create({
|
||||
data: { title: subject, description: textContent, customerId: targetCustomerId, status: 'OPEN' }
|
||||
});
|
||||
activeTicketId = newTicket.id;
|
||||
}
|
||||
|
||||
// 3. Dateianhänge verarbeiten
|
||||
if (parsed.attachments && parsed.attachments.length > 0) {
|
||||
for (const att of parsed.attachments) {
|
||||
// Buffer in Datei schreiben
|
||||
const safeOriginalName = (att.filename || 'unbekannt.dat').replace(/[^a-zA-Z0-9.-]/g, '_');
|
||||
const savedName = `${Date.now()}-${safeOriginalName}`;
|
||||
const filepath = join(UPLOAD_DIR, savedName);
|
||||
|
||||
await writeFile(filepath, att.content);
|
||||
|
||||
// DB Eintrag
|
||||
await prisma.attachment.create({
|
||||
data: {
|
||||
fileName: att.filename || 'unbekannt.dat',
|
||||
savedName: savedName,
|
||||
fileSize: att.size || 0,
|
||||
fileType: att.contentType || 'application/octet-stream',
|
||||
ticketId: activeTicketId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await client.messageFlagsAdd(message.uid, ['\\Seen'], { uid: true });
|
||||
processedCounter++;
|
||||
}
|
||||
} finally {
|
||||
lock.release();
|
||||
}
|
||||
await client.logout();
|
||||
return NextResponse.json({ success: true, processedTickets: processedCounter });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('IMAP Error:', error);
|
||||
return NextResponse.json({ error: 'Verarbeitungsfehler', details: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user