save email as pdf likae attachment version 2

This commit is contained in:
2026-02-04 19:49:09 +01:00
parent d98c97a81f
commit 2d052c76d9
21 changed files with 1143 additions and 151 deletions
@@ -9,6 +9,7 @@ import { getImapSmtpSettings } from '../services/emailProvider/emailProviderServ
import { decrypt } from '../utils/encryption.js';
import { ApiResponse } from '../types/index.js';
import { getCustomerTargets, getContractTargets, getIdentityDocumentTargets, getBankCardTargets, documentTargets } from '../config/documentTargets.config.js';
import { generateEmailPdf } from '../services/pdfService.js';
import { PrismaClient, DocumentType } from '@prisma/client';
import path from 'path';
import fs from 'fs';
@@ -1270,3 +1271,250 @@ export async function saveAttachmentTo(req: Request, res: Response): Promise<voi
} as ApiResponse);
}
}
// ==================== SAVE EMAIL AS PDF ====================
// E-Mail als PDF exportieren und in Dokumentenfeld speichern
export async function saveEmailAsPdf(req: Request, res: Response): Promise<void> {
try {
const emailId = parseInt(req.params.id);
const { entityType, entityId, targetKey } = req.body;
console.log('[saveEmailAsPdf] Request:', { emailId, entityType, entityId, targetKey });
// Validierung
if (!entityType || !targetKey) {
res.status(400).json({
success: false,
error: 'entityType und targetKey sind erforderlich',
} as ApiResponse);
return;
}
// E-Mail aus Cache laden (mit Body)
const email = await cachedEmailService.getCachedEmailById(emailId);
if (!email) {
res.status(404).json({
success: false,
error: 'E-Mail nicht gefunden',
} as ApiResponse);
return;
}
// StressfreiEmail laden um an den Kunden zu kommen
const stressfreiEmail = await prisma.stressfreiEmail.findUnique({
where: { id: email.stressfreiEmailId },
include: { customer: true },
});
if (!stressfreiEmail) {
res.status(404).json({
success: false,
error: 'E-Mail-Konto nicht gefunden',
} as ApiResponse);
return;
}
// Empfänger-Adressen parsen (JSON Array)
let toAddresses: string[] = [];
let ccAddresses: string[] = [];
try {
toAddresses = JSON.parse(email.toAddresses);
} catch { toAddresses = [email.toAddresses]; }
try {
if (email.ccAddresses) ccAddresses = JSON.parse(email.ccAddresses);
} catch { /* ignore */ }
// PDF generieren
const pdfBuffer = await generateEmailPdf({
from: email.fromAddress,
to: toAddresses.join(', '),
cc: ccAddresses.length > 0 ? ccAddresses.join(', ') : undefined,
subject: email.subject || '(Kein Betreff)',
date: email.receivedAt,
bodyText: email.textBody || undefined,
bodyHtml: email.htmlBody || undefined,
});
// Ziel-Konfiguration finden
let targetConfig;
let targetDir: string;
let targetField: string;
console.log('[saveEmailAsPdf] Looking for target config:', { entityType, targetKey });
if (entityType === 'customer') {
targetConfig = documentTargets.customer.find(t => t.key === targetKey);
} else if (entityType === 'identityDocument') {
targetConfig = documentTargets.identityDocument.find(t => t.key === targetKey);
} else if (entityType === 'bankCard') {
targetConfig = documentTargets.bankCard.find(t => t.key === targetKey);
} else if (entityType === 'contract') {
targetConfig = documentTargets.contract.find(t => t.key === targetKey);
}
console.log('[saveEmailAsPdf] Found targetConfig:', targetConfig);
if (!targetConfig) {
res.status(400).json({
success: false,
error: `Unbekanntes Dokumentziel: ${entityType}/${targetKey}`,
} as ApiResponse);
return;
}
targetDir = targetConfig.directory;
targetField = targetConfig.field;
// Uploads-Verzeichnis erstellen
const uploadsDir = path.join(process.cwd(), 'uploads', targetDir);
if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir, { recursive: true });
}
// Eindeutigen Dateinamen generieren
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
const newFilename = `email-${uniqueSuffix}.pdf`;
const filePath = path.join(uploadsDir, newFilename);
const relativePath = `/uploads/${targetDir}/${newFilename}`;
// PDF speichern
fs.writeFileSync(filePath, pdfBuffer);
// Alte Datei löschen und DB aktualisieren
if (entityType === 'customer') {
const customer = stressfreiEmail.customer;
// Alte Datei löschen
const oldPath = (customer as any)[targetField];
if (oldPath) {
const oldFullPath = path.join(process.cwd(), oldPath);
if (fs.existsSync(oldFullPath)) {
fs.unlinkSync(oldFullPath);
}
}
await prisma.customer.update({
where: { id: customer.id },
data: { [targetField]: relativePath },
});
} else if (entityType === 'identityDocument') {
if (!entityId) {
fs.unlinkSync(filePath);
res.status(400).json({
success: false,
error: 'entityId ist für identityDocument erforderlich',
} as ApiResponse);
return;
}
const doc = await prisma.identityDocument.findUnique({ where: { id: entityId } });
if (!doc) {
fs.unlinkSync(filePath);
res.status(404).json({
success: false,
error: 'Ausweis nicht gefunden',
} as ApiResponse);
return;
}
// Alte Datei löschen
const oldPath = (doc as any)[targetField];
if (oldPath) {
const oldFullPath = path.join(process.cwd(), oldPath);
if (fs.existsSync(oldFullPath)) {
fs.unlinkSync(oldFullPath);
}
}
await prisma.identityDocument.update({
where: { id: entityId },
data: { [targetField]: relativePath },
});
} else if (entityType === 'bankCard') {
if (!entityId) {
fs.unlinkSync(filePath);
res.status(400).json({
success: false,
error: 'entityId ist für bankCard erforderlich',
} as ApiResponse);
return;
}
const card = await prisma.bankCard.findUnique({ where: { id: entityId } });
if (!card) {
fs.unlinkSync(filePath);
res.status(404).json({
success: false,
error: 'Bankkarte nicht gefunden',
} as ApiResponse);
return;
}
// Alte Datei löschen
const oldPath = (card as any)[targetField];
if (oldPath) {
const oldFullPath = path.join(process.cwd(), oldPath);
if (fs.existsSync(oldFullPath)) {
fs.unlinkSync(oldFullPath);
}
}
await prisma.bankCard.update({
where: { id: entityId },
data: { [targetField]: relativePath },
});
} else if (entityType === 'contract') {
// Contract-ID kommt aus der E-Mail-Zuordnung oder direkt
const contractId = email.contractId;
if (!contractId) {
fs.unlinkSync(filePath);
res.status(400).json({
success: false,
error: 'E-Mail ist keinem Vertrag zugeordnet',
} as ApiResponse);
return;
}
const contract = await prisma.contract.findUnique({ where: { id: contractId } });
if (!contract) {
fs.unlinkSync(filePath);
res.status(404).json({
success: false,
error: 'Vertrag nicht gefunden',
} as ApiResponse);
return;
}
// Alte Datei löschen
const oldPath = (contract as any)[targetField];
if (oldPath) {
const oldFullPath = path.join(process.cwd(), oldPath);
if (fs.existsSync(oldFullPath)) {
fs.unlinkSync(oldFullPath);
}
}
await prisma.contract.update({
where: { id: contractId },
data: { [targetField]: relativePath },
});
}
res.json({
success: true,
data: {
path: relativePath,
filename: newFilename,
size: pdfBuffer.length,
},
} as ApiResponse);
} catch (error) {
console.error('saveEmailAsPdf error:', error);
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
res.status(500).json({
success: false,
error: `Fehler beim Erstellen der PDF: ${errorMessage}`,
} as ApiResponse);
}
}