server steering implementation
This commit is contained in:
+163
-16
@@ -26,8 +26,10 @@ from bson.objectid import ObjectId
|
||||
import user as user_store
|
||||
import buchungen
|
||||
import server_steering as steering
|
||||
from modules.emailservice.email import send_register_token
|
||||
from modules.emailservice.email import send_register_token, send_accreditation_email
|
||||
import secrets
|
||||
import requests
|
||||
from server_steering import Instance
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "ASDfhbsdfseiufhgildsrfrjg874368546987s6e8468f4!?FAUS/&s"
|
||||
@@ -3607,26 +3609,171 @@ def logout():
|
||||
flash('Logged out successfully', 'info')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
@app.route('/booking_payment_async', methods=['POST'])
|
||||
def booking_payment_async():
|
||||
# ... Speichere Daten in DB ...
|
||||
# ... Starte das manage-tenants.sh Skript ASYNCHRON (z.B. Celery) ...
|
||||
return jsonify({"success": True, "tenant_slug": request.form['tenant_slug']}), 200
|
||||
def generate_secure_password(length=14):
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
def _utc_now_iso():
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
def run_tenant_provisioning_async(app, user_id, tenant_slug, school_name, user_email, prov_id):
|
||||
with app.app_context():
|
||||
# Helper to update provisioning status in 'instance_requests' collection
|
||||
def _update_prov_status(status, extra_fields=None):
|
||||
req_client, req_col = _get_collection("instance_requests")
|
||||
try:
|
||||
update_data = {
|
||||
"provision_status": status,
|
||||
"updated_at": _utc_now_iso()
|
||||
}
|
||||
if extra_fields:
|
||||
update_data.update(extra_fields)
|
||||
|
||||
req_col.update_one(
|
||||
{"_id": ObjectId(prov_id)},
|
||||
{"$set": update_data}
|
||||
)
|
||||
finally:
|
||||
req_client.close()
|
||||
|
||||
# Set status to processing
|
||||
_update_prov_status("in_progress")
|
||||
|
||||
# 1. Determine next available port dynamically
|
||||
existing_tenants = Instance.list()
|
||||
used_ports = [t['port'] for t in existing_tenants if 'port' in t]
|
||||
next_port = max(used_ports) + 1 if used_ports else 10002
|
||||
|
||||
admin_password = generate_secure_password()
|
||||
|
||||
# 2. Execute script to create tenant
|
||||
success = Instance.new(tenant_slug, next_port, admin_password)
|
||||
if not success:
|
||||
_update_prov_status("failed", {"last_error": "Tenant shell creation failed"})
|
||||
return
|
||||
|
||||
# Enable default modules
|
||||
Instance.edit(tenant_slug, "starter")
|
||||
|
||||
# 3. Create instance document & link to user
|
||||
instance_doc = {
|
||||
"owner_id": user_id,
|
||||
"school_name": school_name,
|
||||
"subdomain": tenant_slug,
|
||||
"domain": f"{tenant_slug}.invario-software.de",
|
||||
"https_port": next_port,
|
||||
"status": "ready",
|
||||
"nginx_status": "active",
|
||||
"admin_username": "admin",
|
||||
"admin_password": admin_password
|
||||
}
|
||||
|
||||
inst_client, inst_col = _get_collection("instances")
|
||||
try:
|
||||
inst_result = inst_col.insert_one(instance_doc)
|
||||
created_inst_id = str(inst_result.inserted_id)
|
||||
finally:
|
||||
inst_client.close()
|
||||
|
||||
# 4. Mark request as completed in instance_requests
|
||||
_update_prov_status("completed", {
|
||||
"instance_id": created_inst_id,
|
||||
"domain": instance_doc["domain"]
|
||||
})
|
||||
|
||||
# 5. Send Email with documents and access credentials
|
||||
send_accreditation_email(
|
||||
recipient=user_email,
|
||||
domain=instance_doc["domain"],
|
||||
username="admin",
|
||||
password=admin_password,
|
||||
invoice_pdf_path=f"/var/invoices/{tenant_slug}_invoice.pdf",
|
||||
contract_pdf_path=f"/var/contracts/{tenant_slug}_contract.pdf"
|
||||
)
|
||||
|
||||
@app.route('/booking_payment_async', methods=['POST'])
|
||||
@login_required
|
||||
def booking_payment_async():
|
||||
user_id = session.get("user_id")
|
||||
user_email = session.get("email")
|
||||
tenant_slug = _slugify_subdomain(request.form.get('tenant_slug'))
|
||||
school_name = request.form.get('school_name', tenant_slug)
|
||||
|
||||
# Insert request record into instance_requests
|
||||
req_client, req_col = _get_collection("instance_requests")
|
||||
try:
|
||||
prov_doc = {
|
||||
"user_id": user_id,
|
||||
"tenant_slug": tenant_slug,
|
||||
"school_name": school_name,
|
||||
"provision_status": "pending",
|
||||
"created_at": _utc_now_iso(),
|
||||
"updated_at": _utc_now_iso()
|
||||
}
|
||||
prov_id = str(req_col.insert_one(prov_doc).inserted_id)
|
||||
finally:
|
||||
req_client.close()
|
||||
|
||||
# Start async worker passing prov_id
|
||||
thread = threading.Thread(
|
||||
target=run_tenant_provisioning_async,
|
||||
args=(app._get_current_object(), user_id, tenant_slug, school_name, user_email, prov_id)
|
||||
)
|
||||
thread.start()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"tenant_slug": tenant_slug,
|
||||
"request_id": prov_id
|
||||
}), 200
|
||||
|
||||
@app.route('/instance_request/status')
|
||||
def tenant_status():
|
||||
slug = request.args.get('slug')
|
||||
# Prüfe in der Datenbank, ob das Skript für diesen Slug schon fertig ist
|
||||
if ist_fertig(slug):
|
||||
return jsonify({
|
||||
"status": "ready",
|
||||
"url": f"https://{slug}.invario-software.de",
|
||||
"username": "admin",
|
||||
"password": generiertes_passwort
|
||||
})
|
||||
else:
|
||||
return jsonify({"status": "pending"})
|
||||
req_id = request.args.get('request_id')
|
||||
|
||||
req_client, req_col = _get_collection("instance_requests")
|
||||
try:
|
||||
query = {}
|
||||
if req_id:
|
||||
query["_id"] = ObjectId(req_id)
|
||||
elif slug:
|
||||
query["tenant_slug"] = slug
|
||||
else:
|
||||
return jsonify({"status": "error", "message": "Missing slug or request_id"}), 400
|
||||
|
||||
prov_req = req_col.find_one(query)
|
||||
finally:
|
||||
req_client.close()
|
||||
|
||||
if not prov_req or prov_req.get("provision_status") != "completed":
|
||||
return jsonify({"status": "pending"}), 200
|
||||
|
||||
# Retrieve created instance details for health check
|
||||
inst_client, inst_col = _get_collection("instances")
|
||||
try:
|
||||
instance = inst_col.find_one({"subdomain": prov_req.get("tenant_slug")})
|
||||
finally:
|
||||
inst_client.close()
|
||||
|
||||
if not instance:
|
||||
return jsonify({"status": "pending"}), 200
|
||||
|
||||
# Perform health check ping
|
||||
target_url = f"https://{instance['domain']}/health"
|
||||
try:
|
||||
response = requests.get(target_url, timeout=3)
|
||||
if response.status_code == 200:
|
||||
return jsonify({
|
||||
"status": "ready",
|
||||
"url": f"https://{instance['domain']}",
|
||||
"username": instance.get("admin_username", "admin"),
|
||||
"password": instance.get("admin_password")
|
||||
}), 200
|
||||
except requests.RequestException:
|
||||
pass
|
||||
|
||||
return jsonify({"status": "pending"}), 200
|
||||
|
||||
def main():
|
||||
app.run(host="0.0.0.0", port=4999, debug=False)
|
||||
|
||||
Reference in New Issue
Block a user