first commit
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
// ==================== EMAIL PROVIDER SERVICE ====================
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { decrypt } from '../../utils/encryption.js';
|
||||
import {
|
||||
IEmailProvider,
|
||||
EmailProviderConfig,
|
||||
EmailExistsResult,
|
||||
EmailOperationResult,
|
||||
CreateEmailParams,
|
||||
} from './types.js';
|
||||
import { PleskEmailProvider } from './pleskProvider.js';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// Factory-Funktion um den richtigen Provider zu erstellen
|
||||
function createProvider(config: EmailProviderConfig): IEmailProvider {
|
||||
switch (config.type) {
|
||||
case 'PLESK':
|
||||
return new PleskEmailProvider(config);
|
||||
case 'CPANEL':
|
||||
// TODO: cPanel Provider implementieren
|
||||
throw new Error('cPanel Provider noch nicht implementiert');
|
||||
case 'DIRECTADMIN':
|
||||
// TODO: DirectAdmin Provider implementieren
|
||||
throw new Error('DirectAdmin Provider noch nicht implementiert');
|
||||
default:
|
||||
throw new Error(`Unbekannter Provider-Typ: ${config.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== CONFIG CRUD ====================
|
||||
|
||||
export async function getAllProviderConfigs() {
|
||||
return prisma.emailProviderConfig.findMany({
|
||||
orderBy: [{ isDefault: 'desc' }, { name: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProviderConfigById(id: number) {
|
||||
return prisma.emailProviderConfig.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDefaultProviderConfig() {
|
||||
return prisma.emailProviderConfig.findFirst({
|
||||
where: { isActive: true, isDefault: true },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getActiveProviderConfig() {
|
||||
// Erst Default-Provider versuchen, dann irgendeinen aktiven
|
||||
const defaultProvider = await getDefaultProviderConfig();
|
||||
if (defaultProvider) return defaultProvider;
|
||||
|
||||
return prisma.emailProviderConfig.findFirst({
|
||||
where: { isActive: true },
|
||||
});
|
||||
}
|
||||
|
||||
export interface CreateProviderConfigData {
|
||||
name: string;
|
||||
type: 'PLESK' | 'CPANEL' | 'DIRECTADMIN';
|
||||
apiUrl: string;
|
||||
apiKey?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
domain: string;
|
||||
defaultForwardEmail?: string;
|
||||
isActive?: boolean;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
export async function createProviderConfig(data: CreateProviderConfigData) {
|
||||
// Falls isDefault=true, alle anderen auf false setzen
|
||||
if (data.isDefault) {
|
||||
await prisma.emailProviderConfig.updateMany({
|
||||
where: { isDefault: true },
|
||||
data: { isDefault: false },
|
||||
});
|
||||
}
|
||||
|
||||
// Passwort verschlüsseln falls vorhanden
|
||||
const { encrypt } = await import('../../utils/encryption.js');
|
||||
const passwordEncrypted = data.password ? encrypt(data.password) : null;
|
||||
|
||||
return prisma.emailProviderConfig.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey || null,
|
||||
username: data.username || null,
|
||||
passwordEncrypted,
|
||||
domain: data.domain,
|
||||
defaultForwardEmail: data.defaultForwardEmail || null,
|
||||
isActive: data.isActive ?? true,
|
||||
isDefault: data.isDefault ?? false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateProviderConfig(
|
||||
id: number,
|
||||
data: Partial<CreateProviderConfigData>
|
||||
) {
|
||||
// Falls isDefault=true, alle anderen auf false setzen
|
||||
if (data.isDefault) {
|
||||
await prisma.emailProviderConfig.updateMany({
|
||||
where: { isDefault: true, id: { not: id } },
|
||||
data: { isDefault: false },
|
||||
});
|
||||
}
|
||||
|
||||
const updateData: Record<string, unknown> = {};
|
||||
|
||||
if (data.name !== undefined) updateData.name = data.name;
|
||||
if (data.type !== undefined) updateData.type = data.type;
|
||||
if (data.apiUrl !== undefined) updateData.apiUrl = data.apiUrl;
|
||||
if (data.apiKey !== undefined) updateData.apiKey = data.apiKey || null;
|
||||
if (data.username !== undefined) updateData.username = data.username || null;
|
||||
if (data.domain !== undefined) updateData.domain = data.domain;
|
||||
if (data.defaultForwardEmail !== undefined)
|
||||
updateData.defaultForwardEmail = data.defaultForwardEmail || null;
|
||||
if (data.isActive !== undefined) updateData.isActive = data.isActive;
|
||||
if (data.isDefault !== undefined) updateData.isDefault = data.isDefault;
|
||||
|
||||
// Passwort-Logik:
|
||||
// - Wenn neues Passwort übergeben → verschlüsseln und speichern
|
||||
// - Wenn Benutzername gelöscht wird → Passwort auch löschen (gehören zusammen)
|
||||
if (data.password) {
|
||||
const { encrypt } = await import('../../utils/encryption.js');
|
||||
updateData.passwordEncrypted = encrypt(data.password);
|
||||
} else if (data.username !== undefined && !data.username) {
|
||||
// Benutzername wird gelöscht → Passwort auch löschen
|
||||
updateData.passwordEncrypted = null;
|
||||
}
|
||||
|
||||
return prisma.emailProviderConfig.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteProviderConfig(id: number) {
|
||||
return prisma.emailProviderConfig.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== EMAIL OPERATIONS ====================
|
||||
|
||||
// Provider-Instanz aus DB-Config erstellen
|
||||
async function getProviderInstance(): Promise<IEmailProvider> {
|
||||
const dbConfig = await getActiveProviderConfig();
|
||||
|
||||
if (!dbConfig) {
|
||||
throw new Error('Kein aktiver Email-Provider konfiguriert');
|
||||
}
|
||||
|
||||
// Passwort entschlüsseln
|
||||
let password: string | undefined;
|
||||
if (dbConfig.passwordEncrypted) {
|
||||
try {
|
||||
password = decrypt(dbConfig.passwordEncrypted);
|
||||
} catch {
|
||||
console.error('Konnte Passwort nicht entschlüsseln');
|
||||
}
|
||||
}
|
||||
|
||||
const config: EmailProviderConfig = {
|
||||
id: dbConfig.id,
|
||||
name: dbConfig.name,
|
||||
type: dbConfig.type as 'PLESK' | 'CPANEL' | 'DIRECTADMIN',
|
||||
apiUrl: dbConfig.apiUrl,
|
||||
apiKey: dbConfig.apiKey || undefined,
|
||||
username: dbConfig.username || undefined,
|
||||
password,
|
||||
domain: dbConfig.domain,
|
||||
defaultForwardEmail: dbConfig.defaultForwardEmail || undefined,
|
||||
isActive: dbConfig.isActive,
|
||||
isDefault: dbConfig.isDefault,
|
||||
};
|
||||
|
||||
return createProvider(config);
|
||||
}
|
||||
|
||||
// Prüfen ob eine E-Mail existiert
|
||||
export async function checkEmailExists(localPart: string): Promise<EmailExistsResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
return provider.emailExists(localPart);
|
||||
} catch (error) {
|
||||
console.error('checkEmailExists error:', error);
|
||||
return { exists: false };
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail erstellen mit Weiterleitungen
|
||||
export async function provisionEmail(
|
||||
localPart: string,
|
||||
customerEmail: string
|
||||
): Promise<EmailOperationResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
const config = await getActiveProviderConfig();
|
||||
|
||||
// Weiterleitungsziele zusammenstellen
|
||||
const forwardTargets: string[] = [customerEmail];
|
||||
|
||||
// Unsere eigene Weiterleitungsadresse hinzufügen falls konfiguriert
|
||||
if (config?.defaultForwardEmail) {
|
||||
forwardTargets.push(config.defaultForwardEmail);
|
||||
}
|
||||
|
||||
// Prüfen ob existiert
|
||||
const exists = await provider.emailExists(localPart);
|
||||
if (exists.exists) {
|
||||
return {
|
||||
success: true,
|
||||
message: `E-Mail ${exists.email} existiert bereits`,
|
||||
};
|
||||
}
|
||||
|
||||
// Erstellen
|
||||
const result = await provider.createEmail({
|
||||
localPart,
|
||||
forwardTargets,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail löschen
|
||||
export async function deprovisionEmail(localPart: string): Promise<EmailOperationResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
return provider.deleteEmail(localPart);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail umbenennen
|
||||
export async function renameProvisionedEmail(
|
||||
oldLocalPart: string,
|
||||
newLocalPart: string
|
||||
): Promise<EmailOperationResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
return provider.renameEmail({ oldLocalPart, newLocalPart });
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Domain aus aktivem Provider holen
|
||||
export async function getProviderDomain(): Promise<string | null> {
|
||||
const config = await getActiveProviderConfig();
|
||||
return config?.domain || null;
|
||||
}
|
||||
|
||||
// Provider-Instanz aus übergebener Config erstellen (für Tests mit ungespeicherten Daten)
|
||||
function createProviderFromFormData(data: {
|
||||
type: 'PLESK' | 'CPANEL' | 'DIRECTADMIN';
|
||||
apiUrl: string;
|
||||
apiKey?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
domain: string;
|
||||
}): IEmailProvider {
|
||||
const config: EmailProviderConfig = {
|
||||
id: 0,
|
||||
name: 'Test',
|
||||
type: data.type,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
domain: data.domain,
|
||||
isActive: true,
|
||||
isDefault: false,
|
||||
};
|
||||
return createProvider(config);
|
||||
}
|
||||
|
||||
// Provider-Instanz aus DB-Config per ID erstellen
|
||||
async function getProviderInstanceById(id: number): Promise<IEmailProvider> {
|
||||
const dbConfig = await getProviderConfigById(id);
|
||||
|
||||
if (!dbConfig) {
|
||||
throw new Error('Email-Provider nicht gefunden');
|
||||
}
|
||||
|
||||
// Passwort entschlüsseln
|
||||
let password: string | undefined;
|
||||
if (dbConfig.passwordEncrypted) {
|
||||
try {
|
||||
password = decrypt(dbConfig.passwordEncrypted);
|
||||
} catch {
|
||||
console.error('Konnte Passwort nicht entschlüsseln');
|
||||
}
|
||||
}
|
||||
|
||||
const config: EmailProviderConfig = {
|
||||
id: dbConfig.id,
|
||||
name: dbConfig.name,
|
||||
type: dbConfig.type as 'PLESK' | 'CPANEL' | 'DIRECTADMIN',
|
||||
apiUrl: dbConfig.apiUrl,
|
||||
apiKey: dbConfig.apiKey || undefined,
|
||||
username: dbConfig.username || undefined,
|
||||
password,
|
||||
domain: dbConfig.domain,
|
||||
defaultForwardEmail: dbConfig.defaultForwardEmail || undefined,
|
||||
isActive: dbConfig.isActive,
|
||||
isDefault: dbConfig.isDefault,
|
||||
};
|
||||
|
||||
return createProvider(config);
|
||||
}
|
||||
|
||||
// Provider-Verbindung testen (mit ID, Formulardaten oder Default-Provider)
|
||||
export async function testProviderConnection(options?: {
|
||||
id?: number;
|
||||
testData?: {
|
||||
type: 'PLESK' | 'CPANEL' | 'DIRECTADMIN';
|
||||
apiUrl: string;
|
||||
apiKey?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
domain: string;
|
||||
};
|
||||
}): Promise<EmailOperationResult> {
|
||||
try {
|
||||
let provider: IEmailProvider;
|
||||
|
||||
if (options?.testData) {
|
||||
// Mit übergebenen Daten testen (z.B. aus Modal beim Neuanlegen)
|
||||
provider = createProviderFromFormData(options.testData);
|
||||
} else if (options?.id) {
|
||||
// Gespeicherten Provider per ID testen
|
||||
provider = await getProviderInstanceById(options.id);
|
||||
} else {
|
||||
// Default-Provider testen
|
||||
provider = await getProviderInstance();
|
||||
}
|
||||
|
||||
// Expliziter Verbindungstest (wirft Fehler bei Auth-Problemen)
|
||||
await provider.testConnection();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Verbindung zum Email-Provider erfolgreich',
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user