changes to the User validation of the subdomain for the Booking flow
This commit is contained in:
+65
-25
@@ -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():
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user