diff --git a/Website/main.py b/Website/main.py
index ae17c75..7bcd3ec 100644
--- a/Website/main.py
+++ b/Website/main.py
@@ -2076,10 +2076,22 @@ def booking_payment():
# Do NOT create an invoice directly. Instead request provisioning via server_steering
booking_number = f"BOOK-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
- # Build a subdomain from the school name (fallback to username) and ensure uniqueness
- base = _slugify_subdomain(form_data.get("school_name") or session.get("username") or booking_number)
- subdomain = f"{base}-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
- subdomain = subdomain[:63]
+ # Build a subdomain from the school name (fallback to username)
+ # Use lowercase, sanitized school name and check for existing instances.
+ raw_base = form_data.get("school_name") or session.get("username") or booking_number
+ base = _slugify_subdomain(raw_base).lower()[:51]
+
+ # Check existing instances and require the user to choose a different name on conflict
+ try:
+ existing = steering.instace.list() or []
+ except Exception:
+ existing = []
+
+ if base in existing:
+ flash("Die gewünschte Subdomain (aus dem Schulnamen) existiert bereits. Bitte wähle einen anderen Schulnamen.", "error")
+ return render_template("schnell_buchung.html", **review_data)
+
+ subdomain = base[:63]
provision_status = "not_started"
provision_port = None
@@ -2141,13 +2153,13 @@ def booking_payment():
"source": "booking_payment",
}
- prov_client = None
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov_col.insert_one(provision_payload)
+ req_client, req_col = _get_collection("instance_requests")
+ req_col.insert_one(provision_payload)
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
except PyMongoError:
flash("Buchungsanfrage konnte nicht gesendet werden.", "error")
@@ -2169,20 +2181,20 @@ def booking_payment():
-@app.route('/provision/delete', methods=['POST'])
+@app.route('/instance_request/delete', methods=['POST'])
@login_required
-def provision_delete():
+def instance_request_delete():
prov_id = _sanitize_text(request.form.get("prov_id") or "", 64)
if not prov_id:
flash("Ungültige Anfrage.", "error")
return redirect(url_for("my_invoices"))
- prov_client = None
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov = prov_col.find_one({"_id": ObjectId(prov_id)})
+ req_client, req_col = _get_collection("instance_requests")
+ prov = req_col.find_one({"_id": ObjectId(prov_id)})
if not prov:
- flash("Provisionierungsanfrage nicht gefunden.", "error")
+ flash("Instanz-Anfrage nicht gefunden.", "error")
return redirect(url_for("my_invoices"))
# Only owner or admin can delete
@@ -2190,13 +2202,13 @@ def provision_delete():
flash("Keine Berechtigung zum Löschen dieser Anfrage.", "error")
return redirect(url_for("my_invoices"))
- prov_col.delete_one({"_id": prov.get("_id")})
- flash("Provisionierungsanfrage gelöscht.", "success")
+ req_col.delete_one({"_id": prov.get("_id")})
+ flash("Instanz-Anfrage gelöscht.", "success")
except Exception:
- flash("Provisionierungsanfrage konnte nicht gelöscht werden.", "error")
+ flash("Instanz-Anfrage konnte nicht gelöscht werden.", "error")
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
return redirect(url_for("my_invoices"))
@@ -2776,19 +2788,19 @@ def my_invoices():
if client:
client.close()
- # Load provisioning requests for this user
- provision_requests = []
+ # Load instance requests for this user
+ instance_requests = []
try:
- prov_client, prov_col = _get_collection("provision_requests")
- provision_requests = list(prov_col.find({"username": session.get("username")}).sort("created_at", -1))
- for item in provision_requests:
+ req_client, req_col = _get_collection("instance_requests")
+ instance_requests = list(req_col.find({"username": session.get("username")}).sort("created_at", -1))
+ for item in instance_requests:
item["id"] = str(item.get("_id"))
except PyMongoError:
- flash("Provisionierungsanfragen konnten nicht geladen werden.", "error")
+ flash("Instanz-Anfragen konnten nicht geladen werden.", "error")
finally:
- if 'prov_client' in locals() and prov_client:
- prov_client.close()
- return render_template("my_invoices.html", invoices=invoices, provision_requests=provision_requests)
+ if 'req_client' in locals() and req_client:
+ req_client.close()
+ return render_template("my_invoices.html", invoices=invoices, instance_requests=instance_requests)
@app.route('/my/instance', methods=['GET', 'POST'])
@@ -3140,13 +3152,13 @@ def admin_invoices():
flash("Rechnung angelegt.", "success")
elif action == "create_from_provision" and prov_id:
- # Create an invoice from a provision request
- prov_client = None
+ # Create an invoice from an instance request
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov = prov_col.find_one({"_id": ObjectId(prov_id)})
+ req_client, req_col = _get_collection("instance_requests")
+ prov = req_col.find_one({"_id": ObjectId(prov_id)})
if not prov:
- flash("Provisionierungsanfrage nicht gefunden.", "error")
+ flash("Instanz-Anfrage nicht gefunden.", "error")
return redirect(url_for("admin_invoices"))
username = prov.get("username") or ""
@@ -3176,35 +3188,35 @@ def admin_invoices():
}
)
- prov_col.update_one({"_id": prov.get("_id")}, {"$set": {"provision_status": "invoice_created", "updated_at": _utc_now_iso()}})
- flash("Rechnung aus Provisionierungsanfrage erstellt.", "success")
+ req_col.update_one({"_id": prov.get("_id")}, {"$set": {"provision_status": "invoice_created", "updated_at": _utc_now_iso()}})
+ flash("Rechnung aus Instanz-Anfrage erstellt.", "success")
except Exception:
- flash("Fehler beim Erstellen der Rechnung aus Provisionierungsanfrage.", "error")
+ flash("Fehler beim Erstellen der Rechnung aus Instanz-Anfrage.", "error")
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
elif action == "update_provision" and prov_id:
# Update provision request status (accept/reject/complete)
new_status = _sanitize_text(request.form.get("prov_status") or "", 40)
- prov_client = None
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov_col.update_one({"_id": ObjectId(prov_id)}, {"$set": {"provision_status": new_status, "updated_at": _utc_now_iso()}})
- flash("Provisionierungsanfrage aktualisiert.", "success")
+ req_client, req_col = _get_collection("instance_requests")
+ req_col.update_one({"_id": ObjectId(prov_id)}, {"$set": {"provision_status": new_status, "updated_at": _utc_now_iso()}})
+ flash("Instanz-Anfrage aktualisiert.", "success")
except Exception:
- flash("Provisionierungsanfrage konnte nicht aktualisiert werden.", "error")
+ flash("Instanz-Anfrage konnte nicht aktualisiert werden.", "error")
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
elif action in {"run_provision", "retry_provision", "configure_provision"} and prov_id:
# Admin-triggered provisioning actions: start/retry/configure
- prov_client = None
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov = prov_col.find_one({"_id": ObjectId(prov_id)})
+ req_client, req_col = _get_collection("instance_requests")
+ prov = req_col.find_one({"_id": ObjectId(prov_id)})
if not prov:
- flash("Provisionierungsanfrage nicht gefunden.", "error")
+ flash("Instanz-Anfrage nicht gefunden.", "error")
return redirect(url_for("admin_invoices"))
subdomain = prov.get("subdomain") or _slugify_subdomain(prov.get("booking_data", {}).get("school_name") or prov.get("username") or prov.get("booking_number") or "prov")
@@ -3243,25 +3255,25 @@ def admin_invoices():
provision_config_ok = False
provision_error = str(e)
- prov_col.update_one({"_id": prov.get("_id")}, {"$set": {"provision_status": provision_status, "provision_port": provision_port or "", "provision_config_ok": provision_config_ok, "provision_error": provision_error, "updated_at": _utc_now_iso()}})
+ req_col.update_one({"_id": prov.get("_id")}, {"$set": {"provision_status": provision_status, "provision_port": provision_port or "", "provision_config_ok": provision_config_ok, "provision_error": provision_error, "updated_at": _utc_now_iso()}})
flash("Provisionierungsaktion ausgeführt.", "success")
except Exception as e:
flash(f"Provisionierungsaktion fehlgeschlagen: {str(e)}", "error")
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
- elif action == "delete_provision" and prov_id:
- prov_client = None
+ elif action == "delete_instance_request" and prov_id:
+ req_client = None
try:
- prov_client, prov_col = _get_collection("provision_requests")
- prov_col.delete_one({"_id": ObjectId(prov_id)})
- flash("Provisionierungsanfrage gelöscht.", "success")
+ req_client, req_col = _get_collection("instance_requests")
+ req_col.delete_one({"_id": ObjectId(prov_id)})
+ flash("Instanz-Anfrage gelöscht.", "success")
except Exception:
- flash("Provisionierungsanfrage konnte nicht gelöscht werden.", "error")
+ flash("Instanz-Anfrage konnte nicht gelöscht werden.", "error")
finally:
- if prov_client:
- prov_client.close()
+ if req_client:
+ req_client.close()
elif action == "update" and invoice_id:
amount_text = _sanitize_text(request.form.get("amount_eur") or "0", 20)
@@ -3315,21 +3327,21 @@ def admin_invoices():
if client:
client.close()
- # Load provisioning requests for admin review
- provision_requests = []
+ # Load instance requests for admin review
+ instance_requests = []
try:
- prov_client, prov_col = _get_collection("provision_requests")
- provision_requests = list(prov_col.find().sort("created_at", -1))
- for item in provision_requests:
+ req_client, req_col = _get_collection("instance_requests")
+ instance_requests = list(req_col.find().sort("created_at", -1))
+ for item in instance_requests:
item["id"] = str(item.get("_id"))
except PyMongoError:
- flash("Provisionierungsanfragen konnten nicht geladen werden.", "error")
+ flash("Instanz-Anfragen konnten nicht geladen werden.", "error")
finally:
- if 'prov_client' in locals() and prov_client:
- prov_client.close()
+ if 'req_client' in locals() and req_client:
+ req_client.close()
users = _list_users_for_admin()
- return render_template("admin_invoices.html", invoices=invoices, users=users, provision_requests=provision_requests)
+ return render_template("admin_invoices.html", invoices=invoices, users=users, provision_requests=instance_requests)
@app.route('/datenschutz')
@@ -3337,6 +3349,24 @@ def datenschutz():
return render_template("datenschutz.html")
+@app.route('/instance_request/check_subdomain', methods=['GET'])
+@login_required
+def check_subdomain():
+ name = (request.values.get('school_name') or '').strip()
+ slug = _slugify_subdomain(name)
+ if not slug:
+ return jsonify({"available": False, "slug": "", "message": "Ungültiger Name für Subdomain."})
+
+ try:
+ existing = steering.instace.list() or []
+ except Exception:
+ existing = []
+
+ if slug in existing:
+ return jsonify({"available": False, "slug": slug, "message": "Subdomain bereits vergeben."})
+ return jsonify({"available": True, "slug": slug, "message": "Subdomain verfügbar."})
+
+
@app.route('/impressum')
def impressum():
return render_template("impressum.html")
diff --git a/Website/templates/admin_invoices.html b/Website/templates/admin_invoices.html
index 1e261cb..62d7a29 100644
--- a/Website/templates/admin_invoices.html
+++ b/Website/templates/admin_invoices.html
@@ -64,7 +64,7 @@
{% endfor %}
{% if provision_requests %}
-
Provisionierungsanfragen
+ Instanz-Anfragen
{% for prov in provision_requests %}
{{ prov.booking_number or prov.booking_number }}
@@ -105,7 +105,7 @@
diff --git a/Website/templates/my_invoices.html b/Website/templates/my_invoices.html
index 94c0f10..73e6645 100644
--- a/Website/templates/my_invoices.html
+++ b/Website/templates/my_invoices.html
@@ -46,8 +46,8 @@
- Provisionierungsanfragen
- Hier siehst du den Status deiner angeforderten Instanzen/ Buchungen, die vom Admin geprüft werden.
+ Instanz-Anfragen
+ Hier siehst du den Status deiner angeforderten Instanzen/Buchungen, die vom Admin geprüft werden.
@@ -62,7 +62,7 @@
- {% for prov in provision_requests %}
+ {% for prov in instance_requests %}
| {{ prov.booking_number or prov.booking_number }} |
{{ prov.package_label or prov.package }} |
@@ -70,7 +70,7 @@
{{ '%.2f'|format(prov.amount_eur or 0) }} EUR |
{{ prov.provision_status or 'not_started' }}
-
diff --git a/Website/templates/schnell_buchung.html b/Website/templates/schnell_buchung.html
index 4760d77..8e723c3 100644
--- a/Website/templates/schnell_buchung.html
+++ b/Website/templates/schnell_buchung.html
@@ -208,6 +208,7 @@
@@ -242,10 +243,54 @@
+
{% endblock %}
\ No newline at end of file
|