diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7343255 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Python Bytecode-Dateien +__pycache__/ + diff --git a/inwx_cli.py b/inwx_cli.py new file mode 100644 index 0000000..99b7cf2 --- /dev/null +++ b/inwx_cli.py @@ -0,0 +1,59 @@ +import os +from inwx_config import login, logout # Importiere Login/Logout aus der Konfigurationsdatei +from inwx_dns_functions import get_dns_info, add_record, update_record, delete_record # Importiere DNS-Funktionen + +def main_menu(): + """Hauptmenü der CLI-Anwendung.""" + + # 1. Login-Daten abfragen (kann auch über Umgebungsvariablen INWX_USER/INWX_PASS gesetzt werden) + INWX_USER = os.getenv('INWX_USER') + INWX_PASS = os.getenv('INWX_PASS') + + if not INWX_USER: + INWX_USER = input("Gib deinen INWX-Benutzernamen ein: ") + if not INWX_PASS: + INWX_PASS = input("Gib dein INWX-Passwort ein: ") + + if not login(INWX_USER, INWX_PASS): + return + + # 2. Hauptschleife + try: + while True: + # Das Menü im MC-Stil + print("\n" + "=" * 40) + print(" INWX DNS-CLI (MC-Style) - DOMAIN-VERWALTUNG") + print("=" * 40) + print("1: 🌐 **Anzeigen** (nameserver.info)") + print("---") + print("2: ➕ **Hinzufügen** (nameserver.addRecord)") + print("3: ✏️ **Ändern** (nameserver.updateRecord)") + print("4: 🗑️ **Löschen** (nameserver.deleteRecord)") + print("---") + print("9: 🚪 Logout & Beenden") + print("-" * 40) + + choice = input("Wähle eine Aktion (1-4 oder 9): ").strip() + + if choice == '1': + domain = input("Gib die Domain ein, deren Einträge du sehen möchtest: ").strip() + if domain: + get_dns_info(domain) + elif choice == '2': + add_record() + elif choice == '3': + update_record() + elif choice == '4': + delete_record() + elif choice == '9': + break + else: + print("Ungültige Auswahl. Bitte versuche es erneut.") + + finally: + # Stelle sicher, dass immer ausgeloggt wird + logout() + +if __name__ == "__main__": + main_menu() + diff --git a/inwx_config.py b/inwx_config.py new file mode 100644 index 0000000..ca2f06a --- /dev/null +++ b/inwx_config.py @@ -0,0 +1,81 @@ +import requests +import json + +# --- Konfiguration --- +API_ENDPOINT = "https://api.domrobot.com/jsonrpc/" +SESSION_ID = None + +def api_call(method, params={}): + """ + Führt einen API-Aufruf aus (Post-Login) und gibt die JSON-Antwort zurück. + Verwendet die globale SESSION_ID im Cookie-Header. + """ + global SESSION_ID + + # Stelle sicher, dass die Session-ID für alle Anfragen nach dem Login im Cookie-Header ist + headers = { + "Content-Type": "application/json", + "Cookie": f"domrobot={SESSION_ID}" + } + + payload = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": 1 + } + + try: + response = requests.post(API_ENDPOINT, headers=headers, data=json.dumps(payload), timeout=10) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + print(f"\n❌ API-Fehler bei {method}: {e}") + return None + +def login(user, password): + """Loggt sich in die INWX API ein und speichert die Session-ID.""" + global SESSION_ID + + print("⏳ Versuche, mich einzuloggen...") + + headers = {"Content-Type": "application/json"} + payload = { + "jsonrpc": "2.0", + "method": "account.login", + "params": {"user": user, "pass": password}, + "id": 1 + } + + try: + response = requests.post(API_ENDPOINT, headers=headers, data=json.dumps(payload), timeout=10) + response.raise_for_status() + + result_json = response.json() + if result_json.get('code') == 1000: + if 'domrobot' in response.cookies: + SESSION_ID = response.cookies['domrobot'] + print("✅ Login erfolgreich! Session-ID gespeichert.") + return True + else: + print("❌ Login fehlgeschlagen: 'code 1000' erhalten, aber kein Session-Cookie.") + else: + print(f"❌ Login fehlgeschlagen: {result_json.get('msg', 'Unbekannter Fehler.')}") + + except requests.exceptions.RequestException as e: + print(f"\n❌ API-Fehler beim Login-Request: {e}") + + return False + +def logout(): + """Loggt sich aus der INWX API aus.""" + global SESSION_ID + if not SESSION_ID: + return + + print("\n⏳ Logge mich aus...") + # Hier verwenden wir api_call, die jetzt die SESSION_ID verwendet + api_call("account.logout", {}) + SESSION_ID = None + print("✅ Logout erfolgreich.") + diff --git a/inwx_dns_functions.py b/inwx_dns_functions.py new file mode 100644 index 0000000..e666f04 --- /dev/null +++ b/inwx_dns_functions.py @@ -0,0 +1,122 @@ +from inwx_config import api_call # Importiere die Basis-API-Funktion + +def get_dns_info(domain): + """Ruft die DNS-Einträge für eine Domain ab und gibt sie im MC-Stil aus.""" + print(f"\n⏳ Rufe DNS-Einträge für {domain} ab...") + result = api_call("nameserver.info", {"domain": domain}) + + if result and result.get('code') == 1000 and 'record' in result.get('resData', {}): + print(f"✅ DNS-Einträge für **{domain}**:") + records = result['resData']['record'] + + # Tabelle im MC-Stil ausgeben + print("-" * 100) + print(f"| {'ID':<10} | {'Name/Host':<15} | {'Typ':<5} | {'TTL':<5} | {'Inhalt/Content':<50} |") + print("-" * 100) + + for record in records: + # Begrenze Name und Content für eine saubere Ausgabe + name_short = record.get('name', '')[:14].ljust(15) + content_short = record.get('content', '')[:49].ljust(50) + + print(f"| {str(record['id']):<10} | {name_short} | {record['type']:<5} | {str(record['ttl']):<5} | {content_short} |") + + print("-" * 100) + return True + elif result and 'msg' in result: + print(f"❌ Fehler: {result.get('msg', 'Unbekannter Fehler.')}") + else: + print("❌ Fehler beim Abrufen der DNS-Einträge (API-Antwort unerwartet).") + return False + +def add_record(): + """Fügt einen neuen DNS-Eintrag hinzu (nameserver.createRecord).""" + print("\n--- NEUEN DNS-EINTRAG HINZUFÜGEN ---") + domain = input("Domain (z.B. bouquet24.de): ").strip() + name = input("Host/Subdomain (leer lassen für die Hauptdomain '@'): ").strip() + record_type = input("Typ (A, CNAME, TXT, MX, etc.): ").strip().upper() + content = input("Inhalt/Content (IP-Adresse, Ziel, Text): ").strip() + + # Optional: TTL mit Standardwert 3600 + try: + ttl = int(input("TTL (Sekunden, Standard: 3600): ") or 3600) + except ValueError: + print("Ungültige TTL. Verwende Standardwert 3600.") + ttl = 3600 + + params = { + "domain": domain, + "name": name, + "type": record_type, + "content": content, + "ttl": ttl + } + + print(f"\n⏳ Sende Anfrage zum Hinzufügen von {name}.{domain}...") + result = api_call("nameserver.createRecord", params) + + if result and result.get('code') == 1000: + new_id = result.get('resData', {}).get('id', 'N/A') + print(f"✅ Eintrag erfolgreich hinzugefügt! ID: **{new_id}**") + else: + print(f"❌ Fehler beim Hinzufügen: {result.get('msg', 'Unbekannter Fehler.')}") + +def update_record(): + """Ändert einen bestehenden DNS-Eintrag (nameserver.updateRecord).""" + print("\n--- BESTEHENDEN DNS-EINTRAG ÄNDERN ---") + + record_id = input("ID des zu ändernden Eintrags (siehe Option 1): ").strip() + if not record_id.isdigit(): + print("❌ Ungültige ID.") + return + + # Frage nach den Werten, die geändert werden sollen + new_content = input("Neuer Inhalt/Content (leer lassen, um nicht zu ändern): ").strip() + new_ttl = input("Neue TTL (Sekunden, leer lassen, um nicht zu ändern): ").strip() + + params = {"id": int(record_id)} + if new_content: + params["content"] = new_content + if new_ttl: + try: + params["ttl"] = int(new_ttl) + except ValueError: + print("❌ Ungültige TTL. Abbruch der Änderung.") + return + + if len(params) <= 1: + print("Keine Änderungen angegeben. Vorgang abgebrochen.") + return + + print(f"\n⏳ Sende Anfrage zur Änderung der ID **{record_id}**...") + result = api_call("nameserver.updateRecord", params) + + if result and result.get('code') == 1000: + print(f"✅ Eintrag ID **{record_id}** erfolgreich aktualisiert.") + else: + print(f"❌ Fehler beim Ändern: {result.get('msg', 'Unbekannter Fehler.')}") + +def delete_record(): + """Löscht einen DNS-Eintrag (nameserver.deleteRecord).""" + print("\n--- DNS-EINTRAG LÖSCHEN ---") + + record_id = input("ID des zu löschenden Eintrags (siehe Option 1): ").strip() + if not record_id.isdigit(): + print("❌ Ungültige ID.") + return + + confirm = input(f"Soll Eintrag ID **{record_id}** wirklich gelöscht werden? (J/N): ").strip().upper() + if confirm != 'J': + print("Löschvorgang abgebrochen.") + return + + params = {"id": int(record_id)} + + print(f"\n⏳ Sende Anfrage zum Löschen der ID **{record_id}**...") + result = api_call("nameserver.deleteRecord", params) + + if result and result.get('code') == 1000: + print(f"✅ Eintrag ID **{record_id}** erfolgreich gelöscht.") + else: + print(f"❌ Fehler beim Löschen: {result.get('msg', 'Unbekannter Fehler.')}") +