420 lines
17 KiB
JavaScript
420 lines
17 KiB
JavaScript
"use strict";
|
|
// ==================== PLESK EMAIL PROVIDER ====================
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.PleskEmailProvider = void 0;
|
|
const undici_1 = require("undici");
|
|
// Undici-Agent der selbstsignierte Zertifikate akzeptiert
|
|
// Mit Timeouts und Connection-Limits um Probleme zu vermeiden
|
|
const httpsAgent = new undici_1.Agent({
|
|
connect: {
|
|
rejectUnauthorized: false,
|
|
timeout: 10000, // 10 Sekunden Connect-Timeout
|
|
},
|
|
bodyTimeout: 30000, // 30 Sekunden für Response-Body
|
|
headersTimeout: 30000, // 30 Sekunden für Headers
|
|
keepAliveTimeout: 1000, // Connections nach 1 Sekunde schließen
|
|
keepAliveMaxTimeout: 5000, // Maximal 5 Sekunden Keep-Alive
|
|
connections: 1, // Nur eine Connection gleichzeitig pro Host
|
|
pipelining: 1, // Kein Pipelining
|
|
});
|
|
class PleskEmailProvider {
|
|
type = 'PLESK';
|
|
config;
|
|
constructor(config) {
|
|
this.config = config;
|
|
}
|
|
// Basis-URL für API-Requests
|
|
get baseUrl() {
|
|
// Entferne trailing slash falls vorhanden
|
|
return this.config.apiUrl.replace(/\/$/, '');
|
|
}
|
|
// HTTP-Request an Plesk API senden
|
|
async request(method, endpoint, data) {
|
|
const url = `${this.baseUrl}${endpoint}`;
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
};
|
|
// Authentifizierung: API-Key hat Priorität, sonst Basic Auth
|
|
if (this.config.apiKey) {
|
|
// Nur API-Key verwenden (ohne Basic Auth)
|
|
headers['X-API-Key'] = this.config.apiKey;
|
|
}
|
|
else if (this.config.username && this.config.password) {
|
|
// Basic Auth nur wenn kein API-Key
|
|
const authHeader = Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64');
|
|
headers['Authorization'] = `Basic ${authHeader}`;
|
|
}
|
|
else {
|
|
// Keine Authentifizierung vorhanden
|
|
throw new Error('Keine Zugangsdaten angegeben - bitte API-Key oder Benutzername/Passwort eingeben');
|
|
}
|
|
const options = {
|
|
method,
|
|
headers,
|
|
dispatcher: httpsAgent,
|
|
};
|
|
if (data && (method === 'POST' || method === 'PUT')) {
|
|
options.body = JSON.stringify(data);
|
|
}
|
|
try {
|
|
const response = await (0, undici_1.fetch)(url, options);
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`Plesk API Fehler: ${response.status} - ${errorText}`);
|
|
}
|
|
// Leere Response bei DELETE
|
|
if (response.status === 204) {
|
|
return {};
|
|
}
|
|
return await response.json();
|
|
}
|
|
catch (error) {
|
|
// Verbesserte Fehlermeldungen für häufige Probleme
|
|
if (error instanceof Error) {
|
|
const msg = error.message.toLowerCase();
|
|
// Netzwerkfehler
|
|
if (msg.includes('econnrefused')) {
|
|
throw new Error(`Server nicht erreichbar unter ${this.baseUrl} - Ist der Server gestartet?`);
|
|
}
|
|
if (msg.includes('enotfound') || msg.includes('getaddrinfo')) {
|
|
throw new Error(`Server-Adresse nicht gefunden: ${this.baseUrl} - Bitte URL prüfen`);
|
|
}
|
|
if (msg.includes('etimedout') || msg.includes('timeout')) {
|
|
throw new Error(`Zeitüberschreitung bei Verbindung zu ${this.baseUrl}`);
|
|
}
|
|
if (msg.includes('econnreset')) {
|
|
throw new Error(`Verbindung wurde vom Server abgebrochen`);
|
|
}
|
|
// SSL/TLS Fehler
|
|
if (msg.includes('cert') || msg.includes('ssl') || msg.includes('tls') || msg.includes('unable_to_verify')) {
|
|
throw new Error(`SSL-Zertifikatsfehler - Selbstsigniertes Zertifikat wird nicht akzeptiert`);
|
|
}
|
|
// fetch failed ist meist ein Netzwerk/SSL Problem
|
|
if (msg.includes('fetch failed')) {
|
|
throw new Error(`Verbindung fehlgeschlagen zu ${this.baseUrl} - Bitte prüfen: Server erreichbar? HTTPS-Port korrekt?`);
|
|
}
|
|
}
|
|
console.error('Plesk API Request failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
async testConnection() {
|
|
// Versuche Server-Info abzurufen - wirft Fehler bei Auth-Problemen
|
|
try {
|
|
await this.request('GET', '/api/v2/server');
|
|
}
|
|
catch (error) {
|
|
if (error instanceof Error) {
|
|
// Verbesserte Fehlermeldung
|
|
if (error.message.includes('401')) {
|
|
throw new Error('Authentifizierung fehlgeschlagen - Benutzername/Passwort oder API-Key prüfen');
|
|
}
|
|
if (error.message.includes('403')) {
|
|
throw new Error('Zugriff verweigert - Berechtigungen prüfen');
|
|
}
|
|
// Andere Fehler wurden schon in request() übersetzt
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
async emailExists(localPart) {
|
|
const email = `${localPart}@${this.config.domain}`;
|
|
try {
|
|
// Plesk CLI API: Mail-Info abfragen
|
|
const result = await this.request('POST', '/api/v2/cli/mail/call', { params: ['--info', email] });
|
|
// Debug: Response-Struktur loggen
|
|
console.log('Plesk emailExists response:', JSON.stringify(result, null, 2));
|
|
// Plesk gibt code=0 bei Erfolg, code!=0 bei Fehler
|
|
// stderr enthält Fehlermeldung wenn Mail nicht existiert
|
|
const hasError = result.code !== 0 ||
|
|
result.stderr?.toLowerCase().includes('not found') ||
|
|
result.stderr?.toLowerCase().includes('does not exist') ||
|
|
result.stderr?.toLowerCase().includes('unable to find') ||
|
|
result.stderr?.toLowerCase().includes('no such');
|
|
if (hasError) {
|
|
return { exists: false };
|
|
}
|
|
// 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;
|
|
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
|
|
if (error instanceof Error) {
|
|
const msg = error.message.toLowerCase();
|
|
// "not found" = Mail gibt es nicht
|
|
if (msg.includes('not found') || msg.includes('does not exist') || msg.includes('unable to find')) {
|
|
return { exists: false };
|
|
}
|
|
}
|
|
console.error('Plesk emailExists error:', error);
|
|
return { exists: false };
|
|
}
|
|
}
|
|
async createEmail(params) {
|
|
const { localPart, forwardTargets } = 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 Weiterleitung erstellen
|
|
// Verwendet den CLI-Wrapper unter /api/v2/cli/mail/call
|
|
// Format für -forwarding-addresses: "add:email1,email2" oder "set:email1,email2"
|
|
await this.request('POST', '/api/v2/cli/mail/call', {
|
|
params: [
|
|
'--create', email,
|
|
'-forwarding', 'true',
|
|
'-forwarding-addresses', `add:${forwardTargets.join(',')}`,
|
|
'-mailbox', 'false',
|
|
],
|
|
});
|
|
return {
|
|
success: true,
|
|
message: `E-Mail ${email} erfolgreich erstellt mit Weiterleitung an: ${forwardTargets.join(', ')}`,
|
|
};
|
|
}
|
|
catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
|
console.error('Plesk createEmail error:', error);
|
|
return {
|
|
success: false,
|
|
error: `Fehler beim Erstellen der E-Mail: ${errorMessage}`,
|
|
};
|
|
}
|
|
}
|
|
async createEmailWithMailbox(params) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
const email = `${localPart}@${this.config.domain}`;
|
|
try {
|
|
// Prüfen ob Mail existiert
|
|
const exists = await this.emailExists(localPart);
|
|
if (!exists.exists) {
|
|
return {
|
|
success: false,
|
|
error: `E-Mail ${email} nicht gefunden`,
|
|
};
|
|
}
|
|
// Plesk CLI API: Mail-Account löschen
|
|
await this.request('POST', '/api/v2/cli/mail/call', {
|
|
params: ['--remove', email],
|
|
});
|
|
return {
|
|
success: true,
|
|
message: `E-Mail ${email} erfolgreich gelöscht`,
|
|
};
|
|
}
|
|
catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
|
console.error('Plesk deleteEmail error:', error);
|
|
return {
|
|
success: false,
|
|
error: `Fehler beim Löschen der E-Mail: ${errorMessage}`,
|
|
};
|
|
}
|
|
}
|
|
async renameEmail(params) {
|
|
const { oldLocalPart, newLocalPart } = params;
|
|
const oldEmail = `${oldLocalPart}@${this.config.domain}`;
|
|
const newEmail = `${newLocalPart}@${this.config.domain}`;
|
|
try {
|
|
// Prüfen ob alte Mail existiert
|
|
const oldExists = await this.emailExists(oldLocalPart);
|
|
if (!oldExists.exists) {
|
|
return {
|
|
success: false,
|
|
error: `E-Mail ${oldEmail} nicht gefunden`,
|
|
};
|
|
}
|
|
// Prüfen ob neue Adresse schon existiert
|
|
const newExists = await this.emailExists(newLocalPart);
|
|
if (newExists.exists) {
|
|
return {
|
|
success: false,
|
|
error: `E-Mail ${newEmail} existiert bereits`,
|
|
};
|
|
}
|
|
// Plesk CLI API: Mail-Account umbenennen
|
|
await this.request('POST', '/api/v2/cli/mail/call', {
|
|
params: ['--rename', oldEmail, '-new-name', newLocalPart],
|
|
});
|
|
return {
|
|
success: true,
|
|
message: `E-Mail erfolgreich umbenannt von ${oldEmail} zu ${newEmail}`,
|
|
};
|
|
}
|
|
catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
|
console.error('Plesk renameEmail error:', error);
|
|
return {
|
|
success: false,
|
|
error: `Fehler beim Umbenennen der E-Mail: ${errorMessage}`,
|
|
};
|
|
}
|
|
}
|
|
async updateForwardTargets(localPart, targets) {
|
|
const email = `${localPart}@${this.config.domain}`;
|
|
try {
|
|
// Prüfen ob Mail existiert
|
|
const exists = await this.emailExists(localPart);
|
|
if (!exists.exists) {
|
|
return {
|
|
success: false,
|
|
error: `E-Mail ${email} nicht gefunden`,
|
|
};
|
|
}
|
|
// Plesk CLI API: Weiterleitungsziele aktualisieren
|
|
// Format für -forwarding-addresses: "set:email1,email2" ersetzt alle Adressen
|
|
await this.request('POST', '/api/v2/cli/mail/call', {
|
|
params: [
|
|
'--update', email,
|
|
'-forwarding', 'true',
|
|
'-forwarding-addresses', `set:${targets.join(',')}`,
|
|
],
|
|
});
|
|
return {
|
|
success: true,
|
|
message: `Weiterleitungen für ${email} aktualisiert: ${targets.join(', ')}`,
|
|
};
|
|
}
|
|
catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
|
console.error('Plesk updateForwardTargets error:', error);
|
|
return {
|
|
success: false,
|
|
error: `Fehler beim Aktualisieren der Weiterleitungen: ${errorMessage}`,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
exports.PleskEmailProvider = PleskEmailProvider;
|
|
//# sourceMappingURL=pleskProvider.js.map
|