added backup and email client
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import { Request, Response } from 'express';
|
||||
import * as backupService from '../services/backup.service.js';
|
||||
|
||||
/**
|
||||
* Liste aller Backups abrufen
|
||||
* GET /api/settings/backups
|
||||
*/
|
||||
export async function listBackups(req: Request, res: Response) {
|
||||
try {
|
||||
const backups = await backupService.listBackups();
|
||||
res.json({ data: backups });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler beim Laden der Backups', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Neues Backup erstellen
|
||||
* POST /api/settings/backup
|
||||
*/
|
||||
export async function createBackup(req: Request, res: Response) {
|
||||
try {
|
||||
const result = await backupService.createBackup();
|
||||
|
||||
if (result.success) {
|
||||
res.json({ data: { backupName: result.backupName }, message: 'Backup erfolgreich erstellt' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Backup fehlgeschlagen', details: result.error });
|
||||
}
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler beim Erstellen des Backups', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup wiederherstellen
|
||||
* POST /api/settings/backup/:name/restore
|
||||
*/
|
||||
export async function restoreBackup(req: Request, res: Response) {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Backup-Name erforderlich' });
|
||||
}
|
||||
|
||||
const result = await backupService.restoreBackup(name);
|
||||
|
||||
if (result.success) {
|
||||
res.json({
|
||||
data: {
|
||||
restoredRecords: result.restoredRecords,
|
||||
restoredFiles: result.restoredFiles,
|
||||
},
|
||||
message: `${result.restoredRecords} Datensätze und ${result.restoredFiles || 0} Dateien wiederhergestellt`,
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({ error: 'Wiederherstellung fehlgeschlagen', details: result.error });
|
||||
}
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler bei der Wiederherstellung', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup löschen
|
||||
* DELETE /api/settings/backup/:name
|
||||
*/
|
||||
export async function deleteBackup(req: Request, res: Response) {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Backup-Name erforderlich' });
|
||||
}
|
||||
|
||||
const result = await backupService.deleteBackup(name);
|
||||
|
||||
if (result.success) {
|
||||
res.json({ message: 'Backup gelöscht' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Löschen fehlgeschlagen', details: result.error });
|
||||
}
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler beim Löschen des Backups', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup als ZIP herunterladen
|
||||
* GET /api/settings/backup/:name/download
|
||||
*/
|
||||
export async function downloadBackup(req: Request, res: Response) {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Backup-Name erforderlich' });
|
||||
}
|
||||
|
||||
const result = await backupService.createBackupZip(name);
|
||||
|
||||
if ('error' in result) {
|
||||
return res.status(404).json({ error: result.error });
|
||||
}
|
||||
|
||||
// Response-Header setzen
|
||||
res.setHeader('Content-Type', 'application/zip');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${result.filename}"`);
|
||||
|
||||
// Archiver zum Response pipen
|
||||
result.stream.pipe(res);
|
||||
|
||||
// Archiver finalisieren (startet das Schreiben)
|
||||
result.stream.finalize();
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler beim Download', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup-ZIP hochladen
|
||||
* POST /api/settings/backup/upload
|
||||
*/
|
||||
export async function uploadBackup(req: Request, res: Response) {
|
||||
try {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'Keine Datei hochgeladen' });
|
||||
}
|
||||
|
||||
// Prüfen ob es eine ZIP-Datei ist
|
||||
if (!req.file.originalname.endsWith('.zip')) {
|
||||
return res.status(400).json({ error: 'Nur ZIP-Dateien sind erlaubt' });
|
||||
}
|
||||
|
||||
const result = await backupService.uploadBackupZip(req.file.buffer);
|
||||
|
||||
if (result.success) {
|
||||
res.json({
|
||||
data: { backupName: result.backupName },
|
||||
message: 'Backup erfolgreich hochgeladen',
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({ error: 'Upload fehlgeschlagen', details: result.error });
|
||||
}
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler beim Upload', details: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Werkseinstellungen - Alle Daten löschen
|
||||
* POST /api/settings/factory-reset
|
||||
*/
|
||||
export async function factoryReset(req: Request, res: Response) {
|
||||
try {
|
||||
const result = await backupService.factoryReset();
|
||||
|
||||
if (result.success) {
|
||||
res.json({
|
||||
message: 'Werkseinstellungen wiederhergestellt. Bitte melden Sie sich mit admin@admin.com / admin an.',
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({ error: 'Werkseinstellungen fehlgeschlagen', details: result.error });
|
||||
}
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: 'Fehler bei Werkseinstellungen', details: error.message });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,805 @@
|
||||
// ==================== CACHED EMAIL CONTROLLER ====================
|
||||
|
||||
import { Request, Response } from 'express';
|
||||
import * as cachedEmailService from '../services/cachedEmail.service.js';
|
||||
import * as stressfreiEmailService from '../services/stressfreiEmail.service.js';
|
||||
import { sendEmail, SmtpCredentials, SendEmailParams, EmailAttachment } from '../services/smtpService.js';
|
||||
import { fetchAttachment, appendToSent, ImapCredentials } from '../services/imapService.js';
|
||||
import { getImapSmtpSettings } from '../services/emailProvider/emailProviderService.js';
|
||||
import { decrypt } from '../utils/encryption.js';
|
||||
import { ApiResponse } from '../types/index.js';
|
||||
|
||||
// ==================== E-MAIL LIST ====================
|
||||
|
||||
// E-Mails für einen Kunden abrufen
|
||||
export async function getEmailsForCustomer(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
const stressfreiEmailId = req.query.accountId ? parseInt(req.query.accountId as string) : undefined;
|
||||
const folder = req.query.folder as string | undefined; // INBOX oder SENT
|
||||
const limit = req.query.limit ? parseInt(req.query.limit as string) : 50;
|
||||
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0;
|
||||
|
||||
const emails = await cachedEmailService.getCachedEmails({
|
||||
customerId,
|
||||
stressfreiEmailId,
|
||||
folder,
|
||||
limit,
|
||||
offset,
|
||||
includeBody: false,
|
||||
});
|
||||
|
||||
res.json({ success: true, data: emails } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getEmailsForCustomer error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mails für einen Vertrag abrufen
|
||||
export async function getEmailsForContract(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const contractId = parseInt(req.params.contractId);
|
||||
const folder = req.query.folder as string | undefined; // INBOX oder SENT
|
||||
const limit = req.query.limit ? parseInt(req.query.limit as string) : 50;
|
||||
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0;
|
||||
|
||||
const emails = await cachedEmailService.getCachedEmails({
|
||||
contractId,
|
||||
folder,
|
||||
limit,
|
||||
offset,
|
||||
includeBody: false,
|
||||
});
|
||||
|
||||
res.json({ success: true, data: emails } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getEmailsForContract error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Vertrags-E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== SINGLE EMAIL ====================
|
||||
|
||||
// Einzelne E-Mail abrufen (mit Body)
|
||||
export async function getEmail(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
const email = await cachedEmailService.getCachedEmailById(id);
|
||||
|
||||
if (!email) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Als gelesen markieren
|
||||
await cachedEmailService.markEmailAsRead(id);
|
||||
|
||||
res.json({ success: true, data: { ...email, isRead: true } } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getEmail error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail als gelesen/ungelesen markieren
|
||||
export async function markAsRead(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
const { isRead } = req.body;
|
||||
|
||||
if (isRead) {
|
||||
await cachedEmailService.markEmailAsRead(id);
|
||||
} else {
|
||||
await cachedEmailService.markEmailAsUnread(id);
|
||||
}
|
||||
|
||||
res.json({ success: true } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('markAsRead error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Markieren der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail Stern umschalten
|
||||
export async function toggleStar(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
const isStarred = await cachedEmailService.toggleEmailStar(id);
|
||||
|
||||
res.json({ success: true, data: { isStarred } } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('toggleStar error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Ändern des Sterns',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== CONTRACT ASSIGNMENT ====================
|
||||
|
||||
// E-Mail einem Vertrag zuordnen
|
||||
export async function assignToContract(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const emailId = parseInt(req.params.id);
|
||||
const { contractId } = req.body;
|
||||
const userId = (req as any).userId; // Falls Auth-Middleware userId setzt
|
||||
|
||||
const email = await cachedEmailService.assignEmailToContract(emailId, contractId, userId);
|
||||
|
||||
res.json({ success: true, data: email } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('assignToContract error:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Zuordnen der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertragszuordnung aufheben
|
||||
export async function unassignFromContract(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const emailId = parseInt(req.params.id);
|
||||
|
||||
const email = await cachedEmailService.unassignEmailFromContract(emailId);
|
||||
|
||||
res.json({ success: true, data: email } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('unassignFromContract error:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Aufheben der Zuordnung',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail-Anzahl pro Ordner für ein Konto
|
||||
export async function getFolderCounts(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const stressfreiEmailId = parseInt(req.params.id);
|
||||
|
||||
const counts = await cachedEmailService.getFolderCountsForAccount(stressfreiEmailId);
|
||||
|
||||
res.json({ success: true, data: counts } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getFolderCounts error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Ordner-Anzahlen',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail-Anzahl pro Ordner für einen Vertrag
|
||||
export async function getContractFolderCounts(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const contractId = parseInt(req.params.contractId);
|
||||
|
||||
const counts = await cachedEmailService.getFolderCountsForContract(contractId);
|
||||
|
||||
res.json({ success: true, data: counts } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getContractFolderCounts error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Ordner-Anzahlen',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== SYNC & SEND ====================
|
||||
|
||||
// E-Mails für ein Konto synchronisieren (INBOX + SENT)
|
||||
export async function syncAccount(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const stressfreiEmailId = parseInt(req.params.id);
|
||||
const fullSync = req.query.full === 'true';
|
||||
|
||||
// Synchronisiert sowohl INBOX als auch SENT
|
||||
const result = await cachedEmailService.syncAllFoldersForAccount(stressfreiEmailId, { fullSync });
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
newEmails: result.newEmails,
|
||||
totalEmails: result.totalEmails,
|
||||
},
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('syncAccount error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Synchronisieren der E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail senden
|
||||
export async function sendEmailFromAccount(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const stressfreiEmailId = parseInt(req.params.id);
|
||||
const { to, cc, subject, text, html, inReplyTo, references, attachments, contractId } = req.body;
|
||||
|
||||
// StressfreiEmail laden
|
||||
const stressfreiEmail = await stressfreiEmailService.getEmailWithMailboxById(stressfreiEmailId);
|
||||
|
||||
if (!stressfreiEmail) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail-Konto nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stressfreiEmail.hasMailbox) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Dieses Konto hat keine Mailbox für den Versand',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Passwort entschlüsseln
|
||||
const password = await stressfreiEmailService.getDecryptedPassword(stressfreiEmailId);
|
||||
|
||||
if (!password) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Passwort für E-Mail-Versand nicht verfügbar',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// SMTP-Einstellungen vom Provider
|
||||
const settings = await getImapSmtpSettings();
|
||||
|
||||
if (!settings) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Keine SMTP-Einstellungen konfiguriert',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// SMTP-Credentials
|
||||
const credentials: SmtpCredentials = {
|
||||
host: settings.smtpServer,
|
||||
port: settings.smtpPort,
|
||||
user: stressfreiEmail.email,
|
||||
password,
|
||||
encryption: settings.smtpEncryption,
|
||||
allowSelfSignedCerts: settings.allowSelfSignedCerts,
|
||||
};
|
||||
|
||||
// E-Mail-Parameter
|
||||
const emailParams: SendEmailParams = {
|
||||
to,
|
||||
cc,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
inReplyTo,
|
||||
references,
|
||||
attachments: attachments as EmailAttachment[] | undefined,
|
||||
};
|
||||
|
||||
// E-Mail senden
|
||||
const result = await sendEmail(credentials, stressfreiEmail.email, emailParams);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Gesendete E-Mail im IMAP Sent-Ordner speichern (für Attachment-Download)
|
||||
let sentUid: number | undefined;
|
||||
if (result.rawEmail) {
|
||||
try {
|
||||
// IMAP-Credentials für Sent-Ordner
|
||||
const imapCredentials: ImapCredentials = {
|
||||
host: settings.imapServer,
|
||||
port: settings.imapPort,
|
||||
user: stressfreiEmail.email,
|
||||
password,
|
||||
encryption: settings.imapEncryption,
|
||||
allowSelfSignedCerts: settings.allowSelfSignedCerts,
|
||||
};
|
||||
|
||||
const appendResult = await appendToSent(imapCredentials, {
|
||||
rawEmail: result.rawEmail,
|
||||
});
|
||||
|
||||
if (appendResult.success && appendResult.uid) {
|
||||
sentUid = appendResult.uid;
|
||||
console.log(`[SMTP] Email stored in Sent folder with UID ${sentUid}`);
|
||||
}
|
||||
} catch (appendError) {
|
||||
// Nicht kritisch - E-Mail wurde trotzdem gesendet
|
||||
console.error('Error appending to IMAP Sent folder:', appendError);
|
||||
}
|
||||
}
|
||||
|
||||
// Gesendete E-Mail im Cache speichern
|
||||
try {
|
||||
// Anhangsnamen extrahieren falls vorhanden
|
||||
const attachmentNames = attachments?.map((a: EmailAttachment) => a.filename) || [];
|
||||
|
||||
await cachedEmailService.createSentEmail(stressfreiEmailId, {
|
||||
to,
|
||||
cc,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
messageId: result.messageId || `sent-${Date.now()}@opencrm.local`,
|
||||
contractId: contractId ? parseInt(contractId) : undefined,
|
||||
attachmentNames: attachmentNames.length > 0 ? attachmentNames : undefined,
|
||||
uid: sentUid, // UID vom IMAP Sent-Ordner für Attachment-Download
|
||||
});
|
||||
} catch (saveError) {
|
||||
// Fehler beim Speichern nicht kritisch - E-Mail wurde trotzdem gesendet
|
||||
console.error('Error saving sent email to cache:', saveError);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: { messageId: result.messageId },
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('sendEmailFromAccount error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Senden der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== ATTACHMENTS ====================
|
||||
|
||||
// Anhang-Liste einer E-Mail abrufen
|
||||
export async function getAttachments(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const emailId = parseInt(req.params.emailId);
|
||||
|
||||
// E-Mail aus Cache laden
|
||||
const email = await cachedEmailService.getCachedEmailById(emailId);
|
||||
if (!email) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Anhänge aus attachmentNames parsen (JSON Array)
|
||||
const attachmentNames: string[] = email.attachmentNames
|
||||
? JSON.parse(email.attachmentNames)
|
||||
: [];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: attachmentNames.map((name) => ({ filename: name })),
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getAttachments error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Anhänge',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Einzelnen Anhang herunterladen
|
||||
export async function downloadAttachment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const emailId = parseInt(req.params.emailId);
|
||||
const filename = decodeURIComponent(req.params.filename);
|
||||
|
||||
// E-Mail aus Cache laden
|
||||
const email = await cachedEmailService.getCachedEmailById(emailId);
|
||||
if (!email) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Für gesendete E-Mails: Prüfen ob UID vorhanden (im IMAP Sent gespeichert)
|
||||
if (email.folder === 'SENT' && email.uid === 0) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Anhang nicht verfügbar - E-Mail wurde vor der IMAP-Speicherung gesendet',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// StressfreiEmail laden um Zugangsdaten zu bekommen
|
||||
const stressfreiEmail = await stressfreiEmailService.getEmailWithMailboxById(email.stressfreiEmailId);
|
||||
if (!stressfreiEmail || !stressfreiEmail.emailPasswordEncrypted) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Keine Mailbox-Zugangsdaten verfügbar',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// IMAP-Einstellungen laden
|
||||
const settings = await getImapSmtpSettings();
|
||||
if (!settings) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Keine E-Mail-Provider-Einstellungen gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Passwort entschlüsseln
|
||||
const password = decrypt(stressfreiEmail.emailPasswordEncrypted);
|
||||
|
||||
// IMAP-Credentials
|
||||
const credentials: ImapCredentials = {
|
||||
host: settings.imapServer,
|
||||
port: settings.imapPort,
|
||||
user: stressfreiEmail.email,
|
||||
password,
|
||||
encryption: settings.imapEncryption,
|
||||
allowSelfSignedCerts: settings.allowSelfSignedCerts,
|
||||
};
|
||||
|
||||
// Ordner basierend auf E-Mail-Typ bestimmen (INBOX oder Sent)
|
||||
const imapFolder = email.folder === 'SENT' ? 'Sent' : 'INBOX';
|
||||
|
||||
// Anhang per IMAP abrufen
|
||||
const attachment = await fetchAttachment(credentials, email.uid, filename, imapFolder);
|
||||
|
||||
if (!attachment) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'Anhang nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Datei senden - inline (öffnen) oder attachment (download)
|
||||
const disposition = req.query.view === 'true' ? 'inline' : 'attachment';
|
||||
res.setHeader('Content-Type', attachment.contentType);
|
||||
res.setHeader('Content-Disposition', `${disposition}; filename="${encodeURIComponent(attachment.filename)}"`);
|
||||
res.setHeader('Content-Length', attachment.size);
|
||||
res.send(attachment.content);
|
||||
} catch (error) {
|
||||
console.error('downloadAttachment error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Herunterladen des Anhangs',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== MAILBOX ACCOUNTS ====================
|
||||
|
||||
// Mailbox-Konten eines Kunden abrufen
|
||||
export async function getMailboxAccounts(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
|
||||
const accounts = await cachedEmailService.getMailboxAccountsForCustomer(customerId);
|
||||
|
||||
res.json({ success: true, data: accounts } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getMailboxAccounts error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der E-Mail-Konten',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox nachträglich aktivieren
|
||||
export async function enableMailbox(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
const result = await stressfreiEmailService.enableMailbox(id);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ success: true } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('enableMailbox error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Aktivieren der Mailbox',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox-Status mit Provider synchronisieren
|
||||
export async function syncMailboxStatus(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
const result = await stressfreiEmailService.syncMailboxStatus(id);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
hasMailbox: result.hasMailbox,
|
||||
wasUpdated: result.wasUpdated,
|
||||
},
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('syncMailboxStatus error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Synchronisieren des Mailbox-Status',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail-Thread abrufen
|
||||
export async function getThread(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
const thread = await cachedEmailService.getEmailThread(id);
|
||||
|
||||
res.json({ success: true, data: thread } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getThread error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden des E-Mail-Threads',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox-Zugangsdaten abrufen (IMAP/SMTP)
|
||||
export async function getMailboxCredentials(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
// StressfreiEmail laden
|
||||
const stressfreiEmail = await stressfreiEmailService.getEmailWithMailboxById(id);
|
||||
|
||||
if (!stressfreiEmail) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail-Konto nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stressfreiEmail.hasMailbox) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Keine Mailbox für diese E-Mail-Adresse',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// Passwort entschlüsseln
|
||||
const password = await stressfreiEmailService.getDecryptedPassword(id);
|
||||
|
||||
if (!password) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Passwort konnte nicht entschlüsselt werden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// IMAP/SMTP-Einstellungen laden
|
||||
const settings = await getImapSmtpSettings();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
email: stressfreiEmail.email,
|
||||
password,
|
||||
imap: settings ? {
|
||||
server: settings.imapServer,
|
||||
port: settings.imapPort,
|
||||
encryption: settings.imapEncryption,
|
||||
} : null,
|
||||
smtp: settings ? {
|
||||
server: settings.smtpServer,
|
||||
port: settings.smtpPort,
|
||||
encryption: settings.smtpEncryption,
|
||||
} : null,
|
||||
},
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getMailboxCredentials error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Mailbox-Zugangsdaten',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Ungelesene E-Mails zählen
|
||||
export async function getUnreadCount(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const customerId = req.query.customerId ? parseInt(req.query.customerId as string) : undefined;
|
||||
const contractId = req.query.contractId ? parseInt(req.query.contractId as string) : undefined;
|
||||
|
||||
let count = 0;
|
||||
|
||||
if (customerId) {
|
||||
count = await cachedEmailService.getUnreadCountForCustomer(customerId);
|
||||
} else if (contractId) {
|
||||
count = await cachedEmailService.getUnreadCountForContract(contractId);
|
||||
}
|
||||
|
||||
res.json({ success: true, data: { count } } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getUnreadCount error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Zählen der ungelesenen E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail in Papierkorb verschieben (nur Admin)
|
||||
export async function deleteEmail(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
// Prüfen ob E-Mail existiert
|
||||
const email = await cachedEmailService.getCachedEmailById(id);
|
||||
if (!email) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
error: 'E-Mail nicht gefunden',
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await cachedEmailService.moveEmailToTrash(id);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ success: true, message: 'E-Mail in Papierkorb verschoben' } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('deleteEmail error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Löschen der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== TRASH OPERATIONS ====================
|
||||
|
||||
// Papierkorb-E-Mails für einen Kunden abrufen
|
||||
export async function getTrashEmails(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
|
||||
const emails = await cachedEmailService.getTrashEmails(customerId);
|
||||
|
||||
res.json({ success: true, data: emails } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getTrashEmails error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Laden der Papierkorb-E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Papierkorb-Anzahl für einen Kunden
|
||||
export async function getTrashCount(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
|
||||
const count = await cachedEmailService.getTrashCount(customerId);
|
||||
|
||||
res.json({ success: true, data: { count } } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('getTrashCount error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Zählen der Papierkorb-E-Mails',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail aus Papierkorb wiederherstellen
|
||||
export async function restoreEmail(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
const result = await cachedEmailService.restoreEmailFromTrash(id);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ success: true, message: 'E-Mail wiederhergestellt' } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('restoreEmail error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim Wiederherstellen der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail endgültig löschen (aus Papierkorb)
|
||||
export async function permanentDeleteEmail(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
const result = await cachedEmailService.permanentDeleteEmail(id);
|
||||
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ success: true, message: 'E-Mail endgültig gelöscht' } as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('permanentDeleteEmail error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Fehler beim endgültigen Löschen der E-Mail',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
@@ -74,3 +74,26 @@ export async function deleteEmail(req: Request, res: Response): Promise<void> {
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetPassword(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await stressfreiEmailService.resetMailboxPassword(parseInt(req.params.id));
|
||||
if (!result.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: result.error,
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
data: { password: result.password },
|
||||
message: 'Passwort wurde zurückgesetzt',
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Zurücksetzen des Passworts',
|
||||
} as ApiResponse);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user