changes to the deletion process

This commit is contained in:
2026-08-28 16:11:53 +02:00
parent 01d1fbce92
commit e224741092
2 changed files with 166 additions and 3 deletions
+101
View File
@@ -35,6 +35,7 @@ from datetime import timezone, datetime
import string import string
import traceback import traceback
import io import io
import time
app = Flask(__name__) app = Flask(__name__)
app.secret_key = "ASDfhbsdfseiufhgildsrfrjg874368546987s6e8468f4!?FAUS/&s" app.secret_key = "ASDfhbsdfseiufhgildsrfrjg874368546987s6e8468f4!?FAUS/&s"
@@ -2334,6 +2335,72 @@ def background_booking_provisioning(
finally: finally:
req_client.close() 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']) @app.route('/booking/payment', methods=['GET', 'POST'])
@login_required @login_required
@@ -3474,6 +3541,40 @@ def tenant_status():
return jsonify({"status": "pending"}), 200 return jsonify({"status": "pending"}), 200
@app.route('/admin/instances/cancel/<instance_id>', 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']) @app.route('/admin/test/drop_all_instances', methods=['POST'])
@login_required @login_required
+65 -3
View File
@@ -26,6 +26,7 @@
<th>Nginx Status</th> <th>Nginx Status</th>
<th>Letzte Meldung</th> <th>Letzte Meldung</th>
<th>Aktualisiert</th> <th>Aktualisiert</th>
<th>Aktionen</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -59,6 +60,13 @@
<td>{{ item.nginx_status or 'unbekannt' }}</td> <td>{{ item.nginx_status or 'unbekannt' }}</td>
<td class="last-msg-cell">{{ item.last_message or '-' }}</td> <td class="last-msg-cell">{{ item.last_message or '-' }}</td>
<td>{{ item.updated_at or '-' }}</td> <td>{{ item.updated_at or '-' }}</td>
<td class="actions-cell">
<form action="{{ url_for('admin_delete_instance', instance_id=item.id) }}" method="POST" class="form-delete-single">
<button type="submit" class="btn-delete-single" onclick="return confirm('Möchten Sie diese Instanz wirklich löschen?');">
Löschen
</button>
</form>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -67,9 +75,13 @@
<p class="empty-state">Aktuell sind keine Tenant-Instanzen in der Datenbank vorhanden.</p> <p class="empty-state">Aktuell sind keine Tenant-Instanzen in der Datenbank vorhanden.</p>
{% endif %} {% endif %}
<form action="{{ url_for('admin_delete_all_instances') }}" method="POST"> <div class="table-actions-bottom">
<button type="submit" class="btn-delete-all">ALLE INSTANZEN LÖSCHEN</button> <form action="{{ url_for('admin_delete_all_instances') }}" method="POST">
</form> <button type="submit" class="btn-delete-all" onclick="return confirm('Möchten Sie WIRKLICH ALLE Instanzen löschen?');">
ALLE INSTANZEN LÖSCHEN
</button>
</form>
</div>
</section> </section>
<style> <style>
@@ -163,5 +175,55 @@ th {
border: 1px solid #cbd5e1; border: 1px solid #cbd5e1;
color: #475569; color: #475569;
} }
/* Actions & Buttons */
.actions-cell {
white-space: nowrap;
}
.form-delete-single {
margin: 0;
display: inline-block;
}
.btn-delete-single {
background-color: #fef2f2;
border: 1px solid #e3adad;
color: #8b2a27;
padding: 0.35rem 0.75rem;
border-radius: 6px;
font-weight: 600;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.15s ease-in-out;
}
.btn-delete-single:hover {
background-color: #fee2e2;
border-color: #d97706;
color: #991b1b;
}
.table-actions-bottom {
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #e7edf3;
}
.btn-delete-all {
background-color: #991b1b;
color: #ffffff;
border: none;
padding: 0.6rem 1.2rem;
border-radius: 8px;
font-weight: 700;
font-size: 0.85rem;
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.btn-delete-all:hover {
background-color: #7f1d1d;
}
</style> </style>
{% endblock %} {% endblock %}