changes to the deletion process
This commit is contained in:
+101
@@ -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/<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'])
|
||||
@login_required
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<th>Nginx Status</th>
|
||||
<th>Letzte Meldung</th>
|
||||
<th>Aktualisiert</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -59,6 +60,13 @@
|
||||
<td>{{ item.nginx_status or 'unbekannt' }}</td>
|
||||
<td class="last-msg-cell">{{ item.last_message 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -67,9 +75,13 @@
|
||||
<p class="empty-state">Aktuell sind keine Tenant-Instanzen in der Datenbank vorhanden.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="table-actions-bottom">
|
||||
<form action="{{ url_for('admin_delete_all_instances') }}" method="POST">
|
||||
<button type="submit" class="btn-delete-all">ALLE INSTANZEN LÖSCHEN</button>
|
||||
<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>
|
||||
|
||||
<style>
|
||||
@@ -163,5 +175,55 @@ th {
|
||||
border: 1px solid #cbd5e1;
|
||||
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>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user