diff --git a/Website/main.py b/Website/main.py index 4986ea3..06e5e31 100644 --- a/Website/main.py +++ b/Website/main.py @@ -35,6 +35,7 @@ from datetime import timezone, datetime import string import traceback import io +import time app = Flask(__name__) app.secret_key = "ASDfhbsdfseiufhgildsrfrjg874368546987s6e8468f4!?FAUS/&s" @@ -2334,6 +2335,72 @@ def background_booking_provisioning( finally: req_client.close() +def background_tenant_removal( + app_instance, + instance_id, + subdomain +): + """Hintergrund-Worker, der den Tenant vom Host entfernt und den Status auf 'storniert' setzt.""" + with app_instance.app_context(): + inst_client, inst_col = _get_collection("instances") + req_client, req_col = _get_collection("instance_requests") + + try: + # 1. Zwischenstatus in der Instanz-Collection setzen + inst_col.update_one( + {"_id": ObjectId(instance_id)}, + {"$set": {"status": "storniert_wird_entfernt", "nginx_status": "removing"}} + ) + + # 2. Trigger-Datei für den Host-Watcher erstellen (führt "manage-tenant.sh remove [slug]" aus) + os.makedirs(TRIGGER_DIR, exist_ok=True) + timestamp = int(time.time()) + trigger_file = os.path.join(TRIGGER_DIR, f"remove_{subdomain}_{timestamp}.json") + + trigger_payload = { + "action": "remove", + "slug": subdomain + } + + with open(trigger_file, "w") as f: + json.dump(trigger_payload, f) + + print(f"[INFO] Trigger-Datei für Host-Watcher erstellt: {trigger_file}") + + # 3. Status der Instanz in 'instances' auf 'storniert' setzen + inst_col.update_one( + {"_id": ObjectId(instance_id)}, + {"$set": { + "status": "storniert", + "nginx_status": "inaktiv", + "last_message": "Instanz wurde vom Host entfernt und storniert.", + "updated_at": _utc_now_iso() + }} + ) + + # 4. Zugehörige Anfrage/Rechnung in 'instance_requests' als gekündigt markieren + # Sucht nach der Verknüpfung über instance_id oder subdomain + req_col.update_many( + {"$or": [{"instance_id": str(instance_id)}, {"subdomain": subdomain}]}, + {"$set": { + "provision_status": "gekündigt", + "cancelled_at": _utc_now_iso(), + "last_error": None + }} + ) + + print(f"[SUCCESS] Instanz {subdomain} erfolgreich storniert/gekündigt.") + + except Exception as e: + print(f"[ERROR] Entfernen/Stornieren fehlgeschlagen für {subdomain}:") + traceback.print_exc() + inst_col.update_one( + {"_id": ObjectId(instance_id)}, + {"$set": {"status": "error_removing", "last_message": str(e)}} + ) + finally: + inst_client.close() + req_client.close() @app.route('/booking/payment', methods=['GET', 'POST']) @login_required @@ -3474,6 +3541,40 @@ def tenant_status(): return jsonify({"status": "pending"}), 200 +@app.route('/admin/instances/cancel/', methods=['POST']) +@login_required +def admin_cancel_instance(instance_id): + if not session.get("is_admin") and session.get("role") != "admin": + flash("Zugriff verweigert.", "error") + return redirect(url_for('my_instance_management')) + + inst_client, inst_col = _get_collection("instances") + + try: + instance = inst_col.find_one({"_id": ObjectId(instance_id)}) + + if instance: + subdomain = instance.get("subdomain") + + # Async-Prozess im Hintergrund starten + app_instance = current_app._get_current_object() + t = threading.Thread( + target=background_tenant_removal, + args=(app_instance, instance_id, subdomain) + ) + t.start() + + flash(f"Stornierung für '{subdomain}' wurde eingeleitet. Die Instanz wird gelöscht.", "success") + else: + flash("Fehler: Instanz konnte nicht gefunden werden.", "error") + + except Exception as e: + print(f"[ERROR] Fehler beim Einleiten der Stornierung: {e}") + flash("Ein Datenbankfehler ist aufgetreten.", "error") + finally: + inst_client.close() + + return redirect(url_for('admin_instances')) @app.route('/admin/test/drop_all_instances', methods=['POST']) @login_required diff --git a/Website/templates/admin_instances.html b/Website/templates/admin_instances.html index cf91c92..534334a 100644 --- a/Website/templates/admin_instances.html +++ b/Website/templates/admin_instances.html @@ -26,6 +26,7 @@ Nginx Status Letzte Meldung Aktualisiert + Aktionen @@ -59,6 +60,13 @@ {{ item.nginx_status or 'unbekannt' }} {{ item.last_message or '-' }} {{ item.updated_at or '-' }} + +
+ +
+ {% endfor %} @@ -67,9 +75,13 @@

Aktuell sind keine Tenant-Instanzen in der Datenbank vorhanden.

{% endif %} -
- -
+
+
+ +
+
{% endblock %} \ No newline at end of file