added backup and email client
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
EmailExistsResult,
|
||||
EmailOperationResult,
|
||||
CreateEmailParams,
|
||||
MailEncryption,
|
||||
} from './types.js';
|
||||
import { PleskEmailProvider } from './pleskProvider.js';
|
||||
|
||||
@@ -68,6 +69,10 @@ export interface CreateProviderConfigData {
|
||||
password?: string;
|
||||
domain: string;
|
||||
defaultForwardEmail?: string;
|
||||
// Verschlüsselungs-Einstellungen
|
||||
imapEncryption?: MailEncryption;
|
||||
smtpEncryption?: MailEncryption;
|
||||
allowSelfSignedCerts?: boolean;
|
||||
isActive?: boolean;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
@@ -95,6 +100,9 @@ export async function createProviderConfig(data: CreateProviderConfigData) {
|
||||
passwordEncrypted,
|
||||
domain: data.domain,
|
||||
defaultForwardEmail: data.defaultForwardEmail || null,
|
||||
imapEncryption: data.imapEncryption ?? 'SSL',
|
||||
smtpEncryption: data.smtpEncryption ?? 'SSL',
|
||||
allowSelfSignedCerts: data.allowSelfSignedCerts ?? false,
|
||||
isActive: data.isActive ?? true,
|
||||
isDefault: data.isDefault ?? false,
|
||||
},
|
||||
@@ -123,6 +131,9 @@ export async function updateProviderConfig(
|
||||
if (data.domain !== undefined) updateData.domain = data.domain;
|
||||
if (data.defaultForwardEmail !== undefined)
|
||||
updateData.defaultForwardEmail = data.defaultForwardEmail || null;
|
||||
if (data.imapEncryption !== undefined) updateData.imapEncryption = data.imapEncryption;
|
||||
if (data.smtpEncryption !== undefined) updateData.smtpEncryption = data.smtpEncryption;
|
||||
if (data.allowSelfSignedCerts !== undefined) updateData.allowSelfSignedCerts = data.allowSelfSignedCerts;
|
||||
if (data.isActive !== undefined) updateData.isActive = data.isActive;
|
||||
if (data.isDefault !== undefined) updateData.isDefault = data.isDefault;
|
||||
|
||||
@@ -179,6 +190,13 @@ async function getProviderInstance(): Promise<IEmailProvider> {
|
||||
password,
|
||||
domain: dbConfig.domain,
|
||||
defaultForwardEmail: dbConfig.defaultForwardEmail || undefined,
|
||||
imapServer: dbConfig.imapServer || undefined,
|
||||
imapPort: dbConfig.imapPort || undefined,
|
||||
smtpServer: dbConfig.smtpServer || undefined,
|
||||
smtpPort: dbConfig.smtpPort || undefined,
|
||||
imapEncryption: dbConfig.imapEncryption as MailEncryption,
|
||||
smtpEncryption: dbConfig.smtpEncryption as MailEncryption,
|
||||
allowSelfSignedCerts: dbConfig.allowSelfSignedCerts,
|
||||
isActive: dbConfig.isActive,
|
||||
isDefault: dbConfig.isDefault,
|
||||
};
|
||||
@@ -239,6 +257,169 @@ export async function provisionEmail(
|
||||
}
|
||||
}
|
||||
|
||||
// E-Mail mit echter Mailbox erstellen (IMAP/SMTP-Zugang)
|
||||
export async function provisionEmailWithMailbox(
|
||||
localPart: string,
|
||||
customerEmail: string,
|
||||
password: string
|
||||
): Promise<EmailOperationResult & { email?: string }> {
|
||||
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`,
|
||||
email: exists.email,
|
||||
};
|
||||
}
|
||||
|
||||
// Mit Mailbox erstellen
|
||||
const result = await provider.createEmailWithMailbox({
|
||||
localPart,
|
||||
forwardTargets,
|
||||
password,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox für existierende E-Mail-Weiterleitung aktivieren
|
||||
export async function enableMailboxForExistingEmail(
|
||||
localPart: string,
|
||||
password: string
|
||||
): Promise<EmailOperationResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
|
||||
const result = await provider.enableMailboxForExisting({
|
||||
localPart,
|
||||
password,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox-Passwort beim Provider aktualisieren
|
||||
export async function updateMailboxPassword(
|
||||
localPart: string,
|
||||
password: string
|
||||
): Promise<EmailOperationResult> {
|
||||
try {
|
||||
const provider = await getProviderInstance();
|
||||
|
||||
const result = await provider.updateMailboxPassword({
|
||||
localPart,
|
||||
password,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// IMAP/SMTP-Einstellungen vom aktiven Provider holen
|
||||
export interface ImapSmtpSettings {
|
||||
imapServer: string;
|
||||
imapPort: number;
|
||||
imapEncryption: MailEncryption; // SSL, STARTTLS oder NONE
|
||||
smtpServer: string;
|
||||
smtpPort: number;
|
||||
smtpEncryption: MailEncryption; // SSL, STARTTLS oder NONE
|
||||
allowSelfSignedCerts: boolean; // Selbstsignierte Zertifikate erlauben
|
||||
domain: string;
|
||||
}
|
||||
|
||||
export async function getImapSmtpSettings(): Promise<ImapSmtpSettings | null> {
|
||||
const config = await getActiveProviderConfig();
|
||||
if (!config) return null;
|
||||
|
||||
// Default-Server: Hostname aus der apiUrl extrahieren (z.B. rs001871.fastrootserver.de aus https://rs001871.fastrootserver.de:8443)
|
||||
// Der Plesk-Server ist gleichzeitig der Mail-Server
|
||||
let defaultServer: string;
|
||||
try {
|
||||
const url = new URL(config.apiUrl);
|
||||
defaultServer = url.hostname;
|
||||
} catch {
|
||||
// Fallback falls apiUrl ungültig
|
||||
defaultServer = `mail.${config.domain}`;
|
||||
}
|
||||
|
||||
// Verschlüsselungs-Einstellungen
|
||||
const imapEncryption = (config.imapEncryption ?? 'SSL') as MailEncryption;
|
||||
const smtpEncryption = (config.smtpEncryption ?? 'SSL') as MailEncryption;
|
||||
|
||||
// Ports basierend auf Verschlüsselung berechnen:
|
||||
// SSL: IMAP 993, SMTP 465
|
||||
// STARTTLS: IMAP 143, SMTP 587
|
||||
// NONE: IMAP 143, SMTP 25
|
||||
//
|
||||
// Standard-Ports werden IMMER basierend auf Verschlüsselung berechnet.
|
||||
// Nur benutzerdefinierte Ports (nicht 993/143/465/587/25) werden aus der DB übernommen.
|
||||
const getImapPort = (enc: MailEncryption, storedPort: number | null) => {
|
||||
const standardPorts = [993, 143];
|
||||
// Wenn ein nicht-standard Port gespeichert ist, diesen verwenden
|
||||
if (storedPort && !standardPorts.includes(storedPort)) {
|
||||
return storedPort;
|
||||
}
|
||||
// Sonst basierend auf Verschlüsselung
|
||||
return enc === 'SSL' ? 993 : 143;
|
||||
};
|
||||
|
||||
const getSmtpPort = (enc: MailEncryption, storedPort: number | null) => {
|
||||
const standardPorts = [465, 587, 25];
|
||||
// Wenn ein nicht-standard Port gespeichert ist, diesen verwenden
|
||||
if (storedPort && !standardPorts.includes(storedPort)) {
|
||||
return storedPort;
|
||||
}
|
||||
// Sonst basierend auf Verschlüsselung
|
||||
if (enc === 'SSL') return 465;
|
||||
if (enc === 'STARTTLS') return 587;
|
||||
return 25; // NONE
|
||||
};
|
||||
|
||||
return {
|
||||
imapServer: config.imapServer || defaultServer,
|
||||
imapPort: getImapPort(imapEncryption, config.imapPort),
|
||||
imapEncryption,
|
||||
smtpServer: config.smtpServer || defaultServer,
|
||||
smtpPort: getSmtpPort(smtpEncryption, config.smtpPort),
|
||||
smtpEncryption,
|
||||
allowSelfSignedCerts: config.allowSelfSignedCerts ?? false,
|
||||
domain: config.domain,
|
||||
};
|
||||
}
|
||||
|
||||
// E-Mail löschen
|
||||
export async function deprovisionEmail(localPart: string): Promise<EmailOperationResult> {
|
||||
try {
|
||||
@@ -328,6 +509,13 @@ async function getProviderInstanceById(id: number): Promise<IEmailProvider> {
|
||||
password,
|
||||
domain: dbConfig.domain,
|
||||
defaultForwardEmail: dbConfig.defaultForwardEmail || undefined,
|
||||
imapServer: dbConfig.imapServer || undefined,
|
||||
imapPort: dbConfig.imapPort || undefined,
|
||||
smtpServer: dbConfig.smtpServer || undefined,
|
||||
smtpPort: dbConfig.smtpPort || undefined,
|
||||
imapEncryption: dbConfig.imapEncryption as MailEncryption,
|
||||
smtpEncryption: dbConfig.smtpEncryption as MailEncryption,
|
||||
allowSelfSignedCerts: dbConfig.allowSelfSignedCerts,
|
||||
isActive: dbConfig.isActive,
|
||||
isDefault: dbConfig.isDefault,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,10 @@ import {
|
||||
EmailExistsResult,
|
||||
EmailOperationResult,
|
||||
CreateEmailParams,
|
||||
CreateEmailWithMailboxParams,
|
||||
CreateEmailWithMailboxResult,
|
||||
EnableMailboxParams,
|
||||
UpdateMailboxPasswordParams,
|
||||
RenameEmailParams,
|
||||
} from './types.js';
|
||||
|
||||
@@ -173,9 +177,20 @@ export class PleskEmailProvider implements IEmailProvider {
|
||||
|
||||
// stdout sollte die Mail-Infos enthalten
|
||||
const exists = result.stdout?.toLowerCase().includes(localPart.toLowerCase());
|
||||
|
||||
// Mailbox-Status aus stdout parsen (Format: "Mailbox: true" oder "Mailbox: false")
|
||||
let hasMailbox: boolean | undefined;
|
||||
if (exists && result.stdout) {
|
||||
const mailboxMatch = result.stdout.match(/Mailbox:\s*(true|false)/i);
|
||||
if (mailboxMatch) {
|
||||
hasMailbox = mailboxMatch[1].toLowerCase() === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
exists,
|
||||
email: exists ? email : undefined,
|
||||
hasMailbox,
|
||||
};
|
||||
} catch (error) {
|
||||
// HTTP-Fehler oder Netzwerkfehler
|
||||
@@ -231,6 +246,127 @@ export class PleskEmailProvider implements IEmailProvider {
|
||||
}
|
||||
}
|
||||
|
||||
async createEmailWithMailbox(params: CreateEmailWithMailboxParams): Promise<CreateEmailWithMailboxResult> {
|
||||
const { localPart, forwardTargets, password } = params;
|
||||
const email = `${localPart}@${this.config.domain}`;
|
||||
|
||||
try {
|
||||
// Prüfen ob schon existiert
|
||||
const exists = await this.emailExists(localPart);
|
||||
if (exists.exists) {
|
||||
return {
|
||||
success: false,
|
||||
error: `E-Mail ${email} existiert bereits`,
|
||||
};
|
||||
}
|
||||
|
||||
// Plesk CLI API: Mail-Account mit echter Mailbox erstellen
|
||||
// -mailbox true: Echte Mailbox (IMAP/SMTP-Zugang)
|
||||
// -passwd: Passwort für die Mailbox
|
||||
// -forwarding true: Zusätzlich Weiterleitung aktivieren
|
||||
await this.request('POST', '/api/v2/cli/mail/call', {
|
||||
params: [
|
||||
'--create', email,
|
||||
'-mailbox', 'true',
|
||||
'-passwd', password,
|
||||
'-forwarding', 'true',
|
||||
'-forwarding-addresses', `add:${forwardTargets.join(',')}`,
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `E-Mail ${email} mit Mailbox erfolgreich erstellt`,
|
||||
email,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
console.error('Plesk createEmailWithMailbox error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Fehler beim Erstellen der E-Mail mit Mailbox: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async enableMailboxForExisting(params: EnableMailboxParams): Promise<EmailOperationResult> {
|
||||
const { localPart, password } = params;
|
||||
const email = `${localPart}@${this.config.domain}`;
|
||||
|
||||
try {
|
||||
// Prüfen ob E-Mail existiert
|
||||
const exists = await this.emailExists(localPart);
|
||||
if (!exists.exists) {
|
||||
return {
|
||||
success: false,
|
||||
error: `E-Mail ${email} nicht gefunden`,
|
||||
};
|
||||
}
|
||||
|
||||
// Plesk CLI API: Mailbox für existierende E-Mail aktivieren
|
||||
// --update: Existierende E-Mail aktualisieren
|
||||
// -mailbox true: Mailbox aktivieren
|
||||
// -passwd: Passwort für die Mailbox setzen
|
||||
await this.request('POST', '/api/v2/cli/mail/call', {
|
||||
params: [
|
||||
'--update', email,
|
||||
'-mailbox', 'true',
|
||||
'-passwd', password,
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Mailbox für ${email} erfolgreich aktiviert`,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
console.error('Plesk enableMailboxForExisting error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Fehler beim Aktivieren der Mailbox: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async updateMailboxPassword(params: UpdateMailboxPasswordParams): Promise<EmailOperationResult> {
|
||||
const { localPart, password } = params;
|
||||
const email = `${localPart}@${this.config.domain}`;
|
||||
|
||||
try {
|
||||
// Prüfen ob E-Mail existiert
|
||||
const exists = await this.emailExists(localPart);
|
||||
if (!exists.exists) {
|
||||
return {
|
||||
success: false,
|
||||
error: `E-Mail ${email} nicht gefunden`,
|
||||
};
|
||||
}
|
||||
|
||||
// Plesk CLI API: Passwort für existierende E-Mail aktualisieren
|
||||
// --update: Existierende E-Mail aktualisieren
|
||||
// -passwd: Neues Passwort setzen
|
||||
await this.request('POST', '/api/v2/cli/mail/call', {
|
||||
params: [
|
||||
'--update', email,
|
||||
'-passwd', password,
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Passwort für ${email} erfolgreich aktualisiert`,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
console.error('Plesk updateMailboxPassword error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Fehler beim Aktualisieren des Passworts: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async deleteEmail(localPart: string): Promise<EmailOperationResult> {
|
||||
const email = `${localPart}@${this.config.domain}`;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// ==================== EMAIL PROVIDER TYPES ====================
|
||||
|
||||
// Verschlüsselungstyp für E-Mail-Verbindungen
|
||||
export type MailEncryption = 'SSL' | 'STARTTLS' | 'NONE';
|
||||
|
||||
export interface EmailForwardTarget {
|
||||
email: string;
|
||||
}
|
||||
@@ -9,6 +12,27 @@ export interface CreateEmailParams {
|
||||
forwardTargets: string[]; // Weiterleitungsziele
|
||||
}
|
||||
|
||||
export interface CreateEmailWithMailboxParams {
|
||||
localPart: string; // z.B. "max.mustermann"
|
||||
forwardTargets: string[]; // Weiterleitungsziele
|
||||
password: string; // Passwort für Mailbox (IMAP/SMTP)
|
||||
}
|
||||
|
||||
export interface CreateEmailWithMailboxResult extends EmailOperationResult {
|
||||
// Erfolg: Mailbox-Informationen zurückgeben
|
||||
email?: string; // Vollständige E-Mail-Adresse
|
||||
}
|
||||
|
||||
export interface EnableMailboxParams {
|
||||
localPart: string; // z.B. "max.mustermann"
|
||||
password: string; // Passwort für Mailbox (IMAP/SMTP)
|
||||
}
|
||||
|
||||
export interface UpdateMailboxPasswordParams {
|
||||
localPart: string; // z.B. "max.mustermann"
|
||||
password: string; // Neues Passwort für Mailbox
|
||||
}
|
||||
|
||||
export interface RenameEmailParams {
|
||||
oldLocalPart: string;
|
||||
newLocalPart: string;
|
||||
@@ -17,6 +41,7 @@ export interface RenameEmailParams {
|
||||
export interface EmailExistsResult {
|
||||
exists: boolean;
|
||||
email?: string;
|
||||
hasMailbox?: boolean; // true wenn echte Mailbox vorhanden
|
||||
}
|
||||
|
||||
export interface EmailOperationResult {
|
||||
@@ -36,9 +61,18 @@ export interface IEmailProvider {
|
||||
// Prüft ob eine E-Mail-Adresse existiert
|
||||
emailExists(localPart: string): Promise<EmailExistsResult>;
|
||||
|
||||
// Erstellt eine neue E-Mail-Weiterleitung
|
||||
// Erstellt eine neue E-Mail-Weiterleitung (ohne Mailbox)
|
||||
createEmail(params: CreateEmailParams): Promise<EmailOperationResult>;
|
||||
|
||||
// Erstellt eine neue E-Mail mit echter Mailbox (IMAP/SMTP-Zugang)
|
||||
createEmailWithMailbox(params: CreateEmailWithMailboxParams): Promise<CreateEmailWithMailboxResult>;
|
||||
|
||||
// Aktiviert Mailbox für eine existierende E-Mail-Weiterleitung
|
||||
enableMailboxForExisting(params: EnableMailboxParams): Promise<EmailOperationResult>;
|
||||
|
||||
// Aktualisiert das Passwort einer Mailbox
|
||||
updateMailboxPassword(params: UpdateMailboxPasswordParams): Promise<EmailOperationResult>;
|
||||
|
||||
// Löscht eine E-Mail-Adresse
|
||||
deleteEmail(localPart: string): Promise<EmailOperationResult>;
|
||||
|
||||
@@ -60,6 +94,15 @@ export interface EmailProviderConfig {
|
||||
password?: string; // Entschlüsselt
|
||||
domain: string;
|
||||
defaultForwardEmail?: string;
|
||||
// IMAP/SMTP-Server für E-Mail-Client (optional, default: mail.{domain})
|
||||
imapServer?: string;
|
||||
imapPort?: number;
|
||||
smtpServer?: string;
|
||||
smtpPort?: number;
|
||||
// Verschlüsselungs-Einstellungen
|
||||
imapEncryption?: MailEncryption; // SSL, STARTTLS oder NONE
|
||||
smtpEncryption?: MailEncryption; // SSL, STARTTLS oder NONE
|
||||
allowSelfSignedCerts?: boolean; // Selbstsignierte Zertifikate erlauben
|
||||
isActive: boolean;
|
||||
isDefault: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user