82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
"""Plesk REST API Client.
|
|
|
|
Nutzt den CLI-Wrapper /api/v2/cli/{utility}/call. Vorteile:
|
|
- mappt 1:1 auf `plesk bin mail`
|
|
- stabil über Plesk-Versionen hinweg
|
|
- Fehlertexte sind die gleichen wie auf der Shell.
|
|
"""
|
|
from typing import Optional
|
|
import requests
|
|
import urllib3
|
|
|
|
|
|
class PleskError(Exception):
|
|
pass
|
|
|
|
|
|
class PleskClient:
|
|
def __init__(self, host: str, *, api_key: Optional[str] = None,
|
|
user: Optional[str] = None, password: Optional[str] = None,
|
|
port: int = 8443, verify: bool = True):
|
|
if not host:
|
|
raise PleskError("Plesk-Host ist leer")
|
|
self.base = f"https://{host}:{port}"
|
|
self.session = requests.Session()
|
|
self.session.verify = verify
|
|
if not verify:
|
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
if api_key:
|
|
self.session.headers["X-API-Key"] = api_key
|
|
elif user and password:
|
|
self.session.auth = (user, password)
|
|
else:
|
|
raise PleskError(
|
|
"Plesk: Weder API-Key noch User/Passwort gesetzt. "
|
|
"Siehe README → Plesk-API-Key per SSH erzeugen."
|
|
)
|
|
self.session.headers["Content-Type"] = "application/json"
|
|
self.session.headers["Accept"] = "application/json"
|
|
|
|
def close(self) -> None:
|
|
try:
|
|
self.session.close()
|
|
except Exception:
|
|
pass
|
|
|
|
def _cli(self, utility: str, params: list) -> dict:
|
|
url = f"{self.base}/api/v2/cli/{utility}/call"
|
|
try:
|
|
r = self.session.post(url, json={"params": params}, timeout=30)
|
|
except requests.RequestException as e:
|
|
raise PleskError(f"Plesk Verbindung fehlgeschlagen: {e}")
|
|
if r.status_code == 401:
|
|
raise PleskError("Plesk: Authentifizierung fehlgeschlagen (API-Key/Login prüfen)")
|
|
if not r.ok:
|
|
raise PleskError(f"Plesk HTTP {r.status_code}: {r.text[:300]}")
|
|
try:
|
|
data = r.json()
|
|
except ValueError:
|
|
raise PleskError(f"Plesk: ungültige Antwort: {r.text[:300]}")
|
|
if data.get("code", 0) != 0:
|
|
err = (data.get("stderr") or data.get("stdout") or "").strip()
|
|
raise PleskError(f"Plesk CLI exit {data.get('code')}: {err}")
|
|
return data
|
|
|
|
def mail_exists(self, email: str) -> bool:
|
|
try:
|
|
self._cli("mail", ["--info", email])
|
|
return True
|
|
except PleskError as e:
|
|
msg = str(e).lower()
|
|
if "does not exist" in msg or "not found" in msg or "unknown mailname" in msg:
|
|
return False
|
|
raise
|
|
|
|
def create_mail(self, email: str, password: str) -> None:
|
|
# plesk bin mail --create user@dom -passwd 'pw' -mailbox true
|
|
self._cli("mail", [
|
|
"--create", email,
|
|
"-passwd", password,
|
|
"-mailbox", "true",
|
|
])
|