changes to the User validation of the subdomain for the Booking flow

This commit is contained in:
2026-10-02 18:44:06 +02:00
parent fc9deb6e4d
commit fa6fa56fd4
2 changed files with 86 additions and 26 deletions
+65 -25
View File
@@ -2796,6 +2796,7 @@ def booking_payment():
"billing_city": _sanitize_text(request.values.get("billing_city") or "", 120),
"billing_reference": _sanitize_text(request.values.get("billing_reference") or "", 100),
"tenant_slug": _sanitize_text(request.values.get("tenant_slug") or "", 63).lower(),
"confirm_subdomain_change": (request.values.get("confirm_subdomain_change") or "").strip().lower() in {"1", "on", "true", "yes"},
"notes": _sanitize_text(request.values.get("notes") or "", 600),
"agreements_accepted": (request.values.get("agreements_accepted") or "").strip().lower() in {"1", "on", "true", "yes"},
}
@@ -2803,6 +2804,10 @@ def booking_payment():
if not form_data["tenant_slug"] and trial_instance:
form_data["tenant_slug"] = trial_instance.get("subdomain") or ""
owned_instance = _get_instance_for_user(session.get("username"), user_id=session.get("user_id"))
existing_subdomain = _sanitize_text((owned_instance or {}).get("subdomain") or (trial_instance or {}).get("subdomain") or "", 63)
form_data["existing_subdomain"] = existing_subdomain
if request.method == "GET" and not form_data["contact_person"]:
form_data["contact_person"] = session.get("display_name") or session.get("username") or ""
@@ -2909,17 +2914,34 @@ def booking_payment():
finally:
active_trial_client.close()
requested_subdomain = effective_subdomain
if existing_subdomain and requested_subdomain != existing_subdomain and not form_data["confirm_subdomain_change"]:
err_msg = (
f"Sie möchten Ihre bestehende Subdomain {existing_subdomain} ändern. "
"Bitte bestätigen Sie diese Änderung im Popup."
)
if is_json_request:
return jsonify({"success": False, "error": err_msg, "requires_subdomain_confirmation": True}), 409
flash(err_msg, "error")
return render_template("schnell_buchung.html", **review_data)
inst_check_client, inst_col_check = _get_collection("instances")
try:
existing_docs = list(inst_col_check.find({}, {"subdomain": 1}))
existing_docs = list(inst_col_check.find({}, {"subdomain": 1, "owner_username": 1, "username": 1}))
existing_subdomains = [d.get("subdomain") for d in existing_docs if d.get("subdomain")]
except Exception as e:
print(f"[WARNING] Konnte bestehende Subdomains nicht prüfen: {e}")
existing_docs = []
existing_subdomains = []
finally:
inst_check_client.close()
if effective_subdomain in existing_subdomains and not active_trial:
existing_requested = next((doc for doc in existing_docs if doc.get("subdomain") == effective_subdomain), None)
owns_requested = bool(
existing_requested
and (existing_requested.get("owner_username") or existing_requested.get("username")) == session.get("username")
)
if effective_subdomain in existing_subdomains and not owns_requested:
err_msg = "Die gewünschte Subdomain existiert bereits. Bitte wähle eine andere Subdomain."
if is_json_request:
return jsonify({"success": False, "error": err_msg}), 400
@@ -2928,8 +2950,14 @@ def booking_payment():
subdomain = effective_subdomain[:63]
if active_trial:
subdomain = active_trial.get("subdomain") or subdomain
reuse_instance = None
if active_trial and active_trial.get("subdomain") == requested_subdomain:
reuse_instance = active_trial
elif owned_instance and owned_instance.get("subdomain") == requested_subdomain:
reuse_instance = owned_instance
if reuse_instance:
subdomain = reuse_instance.get("subdomain") or subdomain
effective_subdomain = subdomain
provision_payload = {
@@ -2986,39 +3014,44 @@ def booking_payment():
finally:
invoice_client.close()
if active_trial:
if reuse_instance:
module_config = build_module_config_string(package_list)
billing_update = {
"package": package_raw,
"invoice_id": invoice_id,
"billing_state": "active",
"payment_status": "pending",
"payment_due_at": (date.today() + timedelta(days=PAYMENT_GRACE_DAYS)).isoformat(),
"status": "ready",
"last_message": "Bestehende Instanz für die reguläre Buchung übernommen; Zahlungsziel beträgt 14 Tage.",
"updated_at": _utc_now_iso(),
}
if reuse_instance.get("is_trial"):
billing_update.update({
"is_trial": False,
"trial_state": "upgraded",
"last_message": "Trial in reguläre Buchung übernommen; Zahlungsziel beträgt 14 Tage.",
})
instance_client, instance_col = _get_collection("instances")
try:
instance_col.update_one(
{"_id": active_trial.get("_id")},
{"$set": {
"is_trial": False,
"trial_state": "upgraded",
"package": package_raw,
"invoice_id": invoice_id,
"billing_state": "active",
"payment_status": "pending",
"payment_due_at": (date.today() + timedelta(days=PAYMENT_GRACE_DAYS)).isoformat(),
"status": "ready",
"last_message": "Trial in reguläre Buchung übernommen; Zahlungsziel beträgt 14 Tage.",
"updated_at": _utc_now_iso(),
}},
{"_id": reuse_instance.get("_id")},
{"$set": billing_update},
)
finally:
instance_client.close()
_queue_instance_module_action(active_trial, module_config)
_queue_instance_module_action(reuse_instance, module_config)
req_client, req_col = _get_collection("instance_requests")
try:
req_col.update_one(
{"_id": ObjectId(prov_id)},
{"$set": {"provision_status": "ready", "tenant_slug": subdomain, "instance_id": str(active_trial.get("_id")), "url": f"https://{active_trial.get('domain') or subdomain + '.invario-software.de'}"}},
{"$set": {"provision_status": "ready", "tenant_slug": subdomain, "instance_id": str(reuse_instance.get("_id")), "url": f"https://{reuse_instance.get('domain') or subdomain + '.invario-software.de'}"}},
)
finally:
req_client.close()
if is_json_request:
return jsonify({"success": True, "slug": subdomain, "message": "Ihre bestehende Trial-Instanz wurde für die Buchung reaktiviert."}), 200
flash(f"Buchung für {selected_package} wurde erfasst und Ihre Trial-Instanz reaktiviert.", "success")
return jsonify({"success": True, "slug": subdomain, "message": "Ihre bestehende Instanz wurde für die Buchung übernommen."}), 200
flash(f"Buchung für {selected_package} wurde erfasst und Ihre bestehende Instanz übernommen.", "success")
return redirect(url_for("my_instance_management"))
app_obj = current_app._get_current_object()
@@ -3861,7 +3894,13 @@ def check_subdomain():
finally:
inst_client.close()
if existing_instance:
current_username = session.get("username") or ""
owns_instance = bool(
existing_instance
and current_username
and (existing_instance.get("owner_username") or existing_instance.get("username")) == current_username
)
if existing_instance and not owns_instance:
return jsonify({"available": False}), 200
# 2. Prüfen, ob bereits eine Anfrage (Instance Request) dafür läuft
@@ -3874,7 +3913,8 @@ def check_subdomain():
finally:
req_client.close()
if existing_request:
owns_request = bool(existing_request and existing_request.get("username") == current_username)
if existing_request and not owns_request:
return jsonify({"available": False}), 200
# 3. Optional: Gegen reservierte System-Subdomains prüfen (z.B. www, admin, api, mail)
@@ -3883,7 +3923,7 @@ def check_subdomain():
return jsonify({"available": False}), 200
# Wenn alles frei ist:
return jsonify({"available": True}), 200
return jsonify({"available": True, "owned": owns_instance or owns_request}), 200
@app.route('/impressum')
def impressum():
+21 -1
View File
@@ -483,6 +483,7 @@
const slugInput = document.getElementById('tenant_slug');
const feedback = document.getElementById('tenant_slug_feedback');
const submitBtn = document.getElementById('booking_submit');
const existingSubdomain = "{{ form_data.existing_subdomain|default('') }}";
let slugTimer = null;
slugInput.addEventListener('input', (e) => {
@@ -502,7 +503,7 @@
const res = await fetch(`/instance_request/check_subdomain?slug=${name}`);
const j = await res.json();
if (j.available) {
feedback.textContent = 'Adresse ist frei!';
feedback.textContent = j.owned ? 'Ihre bestehende Adresse kann weiterverwendet werden.' : 'Adresse ist frei!';
feedback.style.color = '#1b8a3e';
submitBtn.disabled = false;
} else {
@@ -517,6 +518,10 @@
}
}
if (slugInput.value) {
checkSubdomain(slugInput.value);
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
errorAlert.style.display = 'none';
@@ -528,6 +533,21 @@
const formData = new FormData(form);
const tenantSlug = formData.get('tenant_slug');
if (existingSubdomain && tenantSlug !== existingSubdomain && !formData.get('confirm_subdomain_change')) {
const confirmed = window.confirm(
`Sie ändern Ihre bestehende Subdomain ${existingSubdomain} zu ${tenantSlug}. ` +
'Soll die neue Subdomain wirklich verwendet werden?'
);
if (!confirmed) {
errorAlert.textContent = 'Die Subdomainänderung wurde nicht bestätigt.';
errorAlert.style.display = 'block';
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
return;
}
formData.set('confirm_subdomain_change', '1');
}
try {
const response = await fetch(form.action, {
method: 'POST',