changes to the booking process
This commit is contained in:
+96
-57
@@ -2166,11 +2166,87 @@ def book_option_package():
|
|||||||
|
|
||||||
return redirect(url_for("booking_payment", packages=packages_raw, flow=booking_flow))
|
return redirect(url_for("booking_payment", packages=packages_raw, flow=booking_flow))
|
||||||
|
|
||||||
|
def background_booking_provisioning(app_instance, prov_id, subdomain, modules_to_provision, user_id, school_name):
|
||||||
|
"""Hintergrund-Worker für die klassische Buchung"""
|
||||||
|
with app_instance.app_context():
|
||||||
|
req_client, req_col = _get_collection("instance_requests")
|
||||||
|
try:
|
||||||
|
# Status auf In Progress setzen
|
||||||
|
req_col.update_one({"_id": ObjectId(prov_id)}, {"$set": {"provision_status": "in_progress"}})
|
||||||
|
|
||||||
|
provision_error = ""
|
||||||
|
provision_config_ok = True
|
||||||
|
provision_port = None
|
||||||
|
admin_password = generate_secure_password()
|
||||||
|
|
||||||
|
# 1. Shell Provisioning
|
||||||
|
try:
|
||||||
|
port = steering.instance.new(subdomain)
|
||||||
|
if port and port is not False:
|
||||||
|
try:
|
||||||
|
provision_port = int(port) if isinstance(port, (int, str)) and str(port).isdigit() else port
|
||||||
|
except Exception:
|
||||||
|
provision_port = port
|
||||||
|
|
||||||
|
# 2. Gewählte Module aktivieren
|
||||||
|
for mod in modules_to_provision:
|
||||||
|
try:
|
||||||
|
edit_ok = steering.instance.edit(subdomain, mod)
|
||||||
|
if not edit_ok:
|
||||||
|
provision_config_ok = False
|
||||||
|
provision_error += f" | Edit failed for {mod}"
|
||||||
|
except Exception as e:
|
||||||
|
provision_config_ok = False
|
||||||
|
provision_error += f" | {mod}: {str(e)}"
|
||||||
|
else:
|
||||||
|
provision_error = "Provisioning script returned failure"
|
||||||
|
provision_config_ok = False
|
||||||
|
except Exception as e:
|
||||||
|
provision_error = str(e)
|
||||||
|
provision_config_ok = False
|
||||||
|
|
||||||
|
# 3. Wenn erfolgreich, Instanz in die 'instances' Collection eintragen! (WICHTIG für Health-Check)
|
||||||
|
if provision_config_ok and not provision_error:
|
||||||
|
domain = f"{subdomain}.invario-software.de"
|
||||||
|
instance_doc = {
|
||||||
|
"owner_id": user_id,
|
||||||
|
"school_name": school_name,
|
||||||
|
"subdomain": subdomain,
|
||||||
|
"domain": domain,
|
||||||
|
"https_port": provision_port,
|
||||||
|
"status": "ready",
|
||||||
|
"nginx_status": "active",
|
||||||
|
"admin_username": "admin",
|
||||||
|
"admin_password": admin_password
|
||||||
|
}
|
||||||
|
inst_client, inst_col = _get_collection("instances")
|
||||||
|
try:
|
||||||
|
inst_col.insert_one(instance_doc)
|
||||||
|
finally:
|
||||||
|
inst_client.close()
|
||||||
|
|
||||||
|
# 4. Finalen Status setzen ("ready" löst den Redirect im Frontend aus)
|
||||||
|
final_status = "ready" if provision_config_ok and not provision_error else "failed"
|
||||||
|
req_col.update_one(
|
||||||
|
{"_id": ObjectId(prov_id)},
|
||||||
|
{"$set": {
|
||||||
|
"provision_status": final_status,
|
||||||
|
"provision_port": provision_port or "",
|
||||||
|
"provision_config_ok": provision_config_ok,
|
||||||
|
"provision_error": provision_error.strip(" | "),
|
||||||
|
"updated_at": _utc_now_iso()
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Async Provisioning failed: {e}")
|
||||||
|
req_col.update_one({"_id": ObjectId(prov_id)}, {"$set": {"provision_status": "failed", "provision_error": str(e)}})
|
||||||
|
finally:
|
||||||
|
req_client.close()
|
||||||
|
|
||||||
|
|
||||||
@app.route('/booking/payment', methods=['GET', 'POST'])
|
@app.route('/booking/payment', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def booking_payment():
|
def booking_payment():
|
||||||
# Accept comma-separated list of items (e.g. "inventarsystem,buecherei,terminverwaltung")
|
|
||||||
packages_str = _sanitize_text(request.values.get("packages") or request.values.get("package") or "", 200).lower()
|
packages_str = _sanitize_text(request.values.get("packages") or request.values.get("package") or "", 200).lower()
|
||||||
booking_flow = _sanitize_text(request.values.get("flow") or "payment", 24).lower()
|
booking_flow = _sanitize_text(request.values.get("flow") or "payment", 24).lower()
|
||||||
|
|
||||||
@@ -2183,7 +2259,6 @@ def booking_payment():
|
|||||||
|
|
||||||
package_list = [p.strip() for p in packages_str.split(',') if p.strip()]
|
package_list = [p.strip() for p in packages_str.split(',') if p.strip()]
|
||||||
|
|
||||||
# Validate each option against BOOKING_PACKAGE_MAP and calculate total price
|
|
||||||
selected_package_names = []
|
selected_package_names = []
|
||||||
total_price = 0.0
|
total_price = 0.0
|
||||||
for pkg in package_list:
|
for pkg in package_list:
|
||||||
@@ -2195,9 +2270,8 @@ def booking_payment():
|
|||||||
flash(f"Ungültige Buchungsoption: {pkg}", "error")
|
flash(f"Ungültige Buchungsoption: {pkg}", "error")
|
||||||
return redirect(url_for("preise"))
|
return redirect(url_for("preise"))
|
||||||
|
|
||||||
# Combined label string for UI and DB (e.g., "Inventarsystem, Bücherei")
|
|
||||||
selected_package = ", ".join(selected_package_names)
|
selected_package = ", ".join(selected_package_names)
|
||||||
package_raw = packages_str # Raw keys string for DB
|
package_raw = packages_str
|
||||||
|
|
||||||
flow_config = BOOKING_FLOW_MAP[booking_flow]
|
flow_config = BOOKING_FLOW_MAP[booking_flow]
|
||||||
|
|
||||||
@@ -2303,46 +2377,6 @@ def booking_payment():
|
|||||||
return render_template("schnell_buchung.html", **review_data)
|
return render_template("schnell_buchung.html", **review_data)
|
||||||
|
|
||||||
subdomain = base[:63]
|
subdomain = base[:63]
|
||||||
provision_status = "not_started"
|
|
||||||
provision_port = None
|
|
||||||
provision_error = ""
|
|
||||||
provision_config_ok = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
port = steering.instance.new(subdomain)
|
|
||||||
except Exception as e:
|
|
||||||
port = False
|
|
||||||
provision_error = str(e)
|
|
||||||
provision_config_ok = False
|
|
||||||
|
|
||||||
if port and port is not False:
|
|
||||||
provision_status = "started"
|
|
||||||
try:
|
|
||||||
provision_port = int(port) if isinstance(port, (int, str)) and str(port).isdigit() else port
|
|
||||||
except Exception:
|
|
||||||
provision_port = port
|
|
||||||
|
|
||||||
# Provision all valid modules found in the list (e.g., "inventarsystem", "buecherei")
|
|
||||||
valid_modules = set(BOOKING_PACKAGE_MAP.keys())
|
|
||||||
modules_to_provision = {pkg for pkg in package_list if pkg in valid_modules}
|
|
||||||
|
|
||||||
for mod in modules_to_provision:
|
|
||||||
try:
|
|
||||||
edit_ok = steering.instance.edit(subdomain, mod)
|
|
||||||
if not edit_ok:
|
|
||||||
provision_config_ok = False
|
|
||||||
provision_error += f" | Edit failed for {mod}"
|
|
||||||
except Exception as e:
|
|
||||||
provision_config_ok = False
|
|
||||||
provision_error += f" | {mod}: {str(e)}"
|
|
||||||
else:
|
|
||||||
provision_status = "failed"
|
|
||||||
if not provision_error:
|
|
||||||
provision_error = "Provisioning script returned failure"
|
|
||||||
except Exception as e:
|
|
||||||
provision_status = "failed"
|
|
||||||
provision_error = str(e)
|
|
||||||
|
|
||||||
provision_payload = {
|
provision_payload = {
|
||||||
"username": session.get("username"),
|
"username": session.get("username"),
|
||||||
@@ -2356,21 +2390,26 @@ def booking_payment():
|
|||||||
"assigned_admin_display_name": assigned_admin_display_name,
|
"assigned_admin_display_name": assigned_admin_display_name,
|
||||||
"assigned_admin_email": assigned_admin_email,
|
"assigned_admin_email": assigned_admin_email,
|
||||||
"subdomain": subdomain,
|
"subdomain": subdomain,
|
||||||
"provision_status": provision_status,
|
"tenant_slug": subdomain,
|
||||||
"provision_port": provision_port or "",
|
"provision_status": "pending",
|
||||||
"provision_config_ok": provision_config_ok,
|
|
||||||
"provision_error": provision_error.strip(" | "),
|
|
||||||
"created_at": _utc_now_iso(),
|
"created_at": _utc_now_iso(),
|
||||||
"source": "booking_payment",
|
"source": "booking_payment",
|
||||||
}
|
}
|
||||||
|
|
||||||
req_client = None
|
req_client, req_col = _get_collection("instance_requests")
|
||||||
try:
|
try:
|
||||||
req_client, req_col = _get_collection("instance_requests")
|
prov_id = str(req_col.insert_one(provision_payload).inserted_id)
|
||||||
req_col.insert_one(provision_payload)
|
|
||||||
finally:
|
finally:
|
||||||
if req_client:
|
req_client.close()
|
||||||
req_client.close()
|
|
||||||
|
valid_modules = set(BOOKING_PACKAGE_MAP.keys())
|
||||||
|
modules_to_provision = {pkg for pkg in package_list if pkg in valid_modules}
|
||||||
|
|
||||||
|
thread = threading.Thread(
|
||||||
|
target=background_booking_provisioning,
|
||||||
|
args=(app, prov_id, subdomain, modules_to_provision, session.get("user_id"), form_data["school_name"])
|
||||||
|
)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
except PyMongoError:
|
except PyMongoError:
|
||||||
flash("Buchungsanfrage konnte nicht gesendet werden. Bitte versuchen Sie es erneut.", "error")
|
flash("Buchungsanfrage konnte nicht gesendet werden. Bitte versuchen Sie es erneut.", "error")
|
||||||
@@ -2382,7 +2421,7 @@ def booking_payment():
|
|||||||
success_text = (
|
success_text = (
|
||||||
f"Gesprächstermin für {selected_package} wurde an das Team gesendet."
|
f"Gesprächstermin für {selected_package} wurde an das Team gesendet."
|
||||||
if booking_flow == "consultation"
|
if booking_flow == "consultation"
|
||||||
else f"Buchung für {selected_package} wurde erfolgreich erfasst. Die Rechnung wird per E-Mail versendet."
|
else f"Buchung für {selected_package} wird eingerichtet. Sie werden gleich weitergeleitet."
|
||||||
)
|
)
|
||||||
flash(success_text, "success")
|
flash(success_text, "success")
|
||||||
|
|
||||||
@@ -3789,10 +3828,11 @@ def tenant_status():
|
|||||||
finally:
|
finally:
|
||||||
req_client.close()
|
req_client.close()
|
||||||
|
|
||||||
if not prov_req or prov_req.get("provision_status") != "completed":
|
current_status = prov_req.get("provision_status") if prov_req else None
|
||||||
|
|
||||||
|
if not prov_req or current_status not in ["ready", "completed"]:
|
||||||
return jsonify({"status": "pending"}), 200
|
return jsonify({"status": "pending"}), 200
|
||||||
|
|
||||||
# Retrieve created instance details for health check
|
|
||||||
inst_client, inst_col = _get_collection("instances")
|
inst_client, inst_col = _get_collection("instances")
|
||||||
try:
|
try:
|
||||||
instance = inst_col.find_one({"subdomain": prov_req.get("tenant_slug")})
|
instance = inst_col.find_one({"subdomain": prov_req.get("tenant_slug")})
|
||||||
@@ -3802,7 +3842,6 @@ def tenant_status():
|
|||||||
if not instance:
|
if not instance:
|
||||||
return jsonify({"status": "pending"}), 200
|
return jsonify({"status": "pending"}), 200
|
||||||
|
|
||||||
# Perform health check ping
|
|
||||||
target_url = f"https://{instance['domain']}/health"
|
target_url = f"https://{instance['domain']}/health"
|
||||||
try:
|
try:
|
||||||
response = requests.get(target_url, timeout=3)
|
response = requests.get(target_url, timeout=3)
|
||||||
|
|||||||
Reference in New Issue
Block a user