Introduction of pasword and acount changes for the user

This commit is contained in:
2026-08-26 23:38:22 +02:00
parent c9e4c64cb5
commit 54ca1ae70d
2 changed files with 2 additions and 209 deletions
-206
View File
@@ -2599,212 +2599,6 @@ def admin_instances():
parent_domain=INSTANCE_PARENT_DOMAIN,
)
@app.route('/admin/instances/stats')
@admin_required
def admin_instances_stats():
instances = _list_school_instances()
return jsonify(_collect_runtime_stats(instances))
@app.route('/admin/system', methods=['GET', 'POST'])
@admin_required
def admin_system_tools():
if request.method == 'POST':
action = _sanitize_text(request.form.get("action") or "", 40)
subdomain = _sanitize_text(request.form.get("subdomain") or "", 63)
if action == "restart_core":
compose_file = os.path.join(BASE_DIR, "docker-compose.yml")
ok, output = _run_command(
["docker", "compose", "-f", compose_file, "restart", "website", "mongodb"],
cwd=BASE_DIR,
timeout=180,
)
flash("Core-Services wurden neugestartet." if ok else f"Core-Restart fehlgeschlagen:\n{_tail_output(output, 10)}", "success" if ok else "error")
return redirect(url_for("admin_system_tools"))
if action in {"backup_instance", "update_instance", "restart_instance"}:
instance_dir = _resolve_instance_dir(subdomain)
if not instance_dir:
flash("Instanz nicht gefunden oder ungültige Subdomain.", "error")
return redirect(url_for("admin_system_tools"))
if action == "backup_instance":
command = ["bash", "./backup.sh", "--mode", "auto"]
timeout = 1800
title = f"Backup für {subdomain}"
elif action == "update_instance":
command = ["bash", "./update.sh"]
timeout = 2400
title = f"Update für {subdomain}"
else:
title = f"Restart für {subdomain}"
ok, output = _restart_instance_stack(instance_dir)
if ok:
flash(f"{title} erfolgreich.\n{_tail_output(output, 12)}", "success")
_upsert_school_instance(
{
"subdomain": subdomain,
"status": "Läuft",
"nginx_status": "ok",
"last_message": "Instanz über System-Tools neu gestartet.",
}
)
else:
flash(f"{title} fehlgeschlagen.\n{_tail_output(output, 18)}", "error")
_upsert_school_instance(
{
"subdomain": subdomain,
"status": "Fehler",
"nginx_status": "error",
"last_message": _sanitize_text(_tail_output(output, 6), 500),
}
)
return redirect(url_for("admin_system_tools"))
ok, output = _run_command(command, cwd=instance_dir, timeout=timeout)
if ok:
flash(f"{title} erfolgreich.\n{_tail_output(output, 12)}", "success")
else:
flash(f"{title} fehlgeschlagen.\n{_tail_output(output, 18)}", "error")
return redirect(url_for("admin_system_tools"))
if action == "create_instance_admin":
username = _sanitize_text(request.form.get("admin_username") or "", 80)
password = (request.form.get("admin_password") or "").strip()
first_name = _sanitize_text(request.form.get("admin_first_name") or "Admin", 80)
last_name = _sanitize_text(request.form.get("admin_last_name") or "User", 80)
ok, msg = _create_instance_admin_user(subdomain, username, password, first_name, last_name)
flash(msg, "success" if ok else "error")
return redirect(url_for("admin_system_tools"))
flash("Unbekannte System-Aktion.", "error")
return redirect(url_for("admin_system_tools"))
instances = _list_school_instances()
system_snapshot = _build_server_management_snapshot(instances)
return render_template("admin_system.html", instances=instances, system_snapshot=system_snapshot)
@app.route('/admin/system/stats')
@admin_required
def admin_system_stats():
instances = _list_school_instances()
return jsonify(_build_server_management_snapshot(instances))
@app.route('/admin/system/logs/live')
@admin_required
def admin_system_live_logs():
return jsonify(_collect_homepage_service_logs())
@app.route('/admin/system/logs/core')
@admin_required
def admin_download_core_logs():
ok, output = _collect_core_logs()
if not ok:
flash(f"Core-Logs konnten nicht geladen werden.\n{_tail_output(output, 14)}", "error")
return redirect(url_for("admin_system_tools"))
filename = f"core-logs-{datetime.utcnow().strftime('%Y%m%d-%H%M%S')}.log"
return send_file(
BytesIO(output.encode("utf-8")),
mimetype="text/plain",
as_attachment=True,
download_name=filename,
)
@app.route('/admin/system/logs/instance/<subdomain>')
@admin_required
def admin_download_instance_logs(subdomain):
ok, output = _collect_instance_logs(subdomain)
if not ok:
flash(f"Instanz-Logs konnten nicht geladen werden.\n{_tail_output(output, 14)}", "error")
return redirect(url_for("admin_system_tools"))
safe_name = _slugify_subdomain(subdomain)
filename = f"instance-{safe_name}-logs-{datetime.utcnow().strftime('%Y%m%d-%H%M%S')}.log"
return send_file(
BytesIO(output.encode("utf-8")),
mimetype="text/plain",
as_attachment=True,
download_name=filename,
)
@app.route('/admin/system/backup/export/<subdomain>')
@admin_required
def admin_export_instance_backup(subdomain):
ok, filename, archive_path = _build_instance_backup_archive(subdomain)
if not ok or not archive_path:
flash(filename or "Backup-Export fehlgeschlagen.", "error")
return redirect(url_for("admin_system_tools"))
@after_this_request
def _cleanup_archive(response):
try:
os.remove(archive_path)
except OSError:
pass
return response
return send_file(
archive_path,
mimetype="application/gzip",
as_attachment=True,
download_name=filename,
)
@app.route('/admin/system/backup/import/<subdomain>', methods=['POST'])
@admin_required
def admin_import_instance_backup(subdomain):
instance_dir = _resolve_instance_dir(subdomain)
if not instance_dir:
flash("Instanzverzeichnis nicht gefunden.", "error")
return redirect(url_for("admin_system_tools"))
upload = request.files.get("backup_file")
if not upload or not upload.filename:
flash("Bitte eine Backup-Datei auswählen.", "error")
return redirect(url_for("admin_system_tools"))
filename = secure_filename(upload.filename)
if not (filename.endswith(".tar.gz") or filename.endswith(".tgz")):
flash("Nur .tar.gz oder .tgz Backups sind erlaubt.", "error")
return redirect(url_for("admin_system_tools"))
fd, temp_path = tempfile.mkstemp(prefix="instance-restore-", suffix=".tar.gz")
os.close(fd)
try:
upload.save(temp_path)
ok, message = _safe_extract_tar_archive(temp_path, instance_dir)
if not ok:
flash(message, "error")
return redirect(url_for("admin_system_tools"))
restart_ok, restart_output = _restart_instance_stack(instance_dir)
if restart_ok:
flash(f"Backup für {subdomain} erfolgreich eingespielt und Instanz neu gestartet.", "success")
if restart_output:
flash(_tail_output(restart_output, 10), "info")
else:
flash(f"Backup eingespielt, aber Neustart fehlgeschlagen:\n{_tail_output(restart_output, 14)}", "error")
except Exception as exc:
flash(f"Backup konnte nicht eingespielt werden: {exc}", "error")
finally:
try:
os.remove(temp_path)
except OSError:
pass
return redirect(url_for("admin_system_tools"))
@app.route('/admin/blog', methods=['GET', 'POST'])
@admin_required
def admin_blog():
+2 -3
View File
@@ -540,11 +540,11 @@
<details class="nav-dropdown">
<summary class="nav-btn">Admin</summary>
<div class="dropdown-menu">
<a href="{{ url_for('admin_users') }}">Userverwaltung</a> <!-- War bereits hier vorhanden -->
<a href="{{ url_for('user_profile') }}">Mein Profil</a>
<a href="{{ url_for('admin_users') }}">Userverwaltung</a>
<a href="{{ url_for('admin_invoices') }}">Rechnungsverwaltung</a>
<a href="{{ url_for('admin_chats') }}">Admin-Chat</a>
<a href="{{ url_for('admin_tickets') }}">Support-Center</a>
<a href="{{ url_for('admin_system_tools') }}">System-Tools</a>
<a href="{{ url_for('admin_blog') }}">Blog verwalten</a>
<a href="{{ url_for('admin_instances') }}">Schul-Instanzen</a>
</div>
@@ -554,7 +554,6 @@
<details class="nav-dropdown">
<summary class="nav-btn">Nutzerbereich</summary>
<div class="dropdown-menu">
<!-- NEU: Profilverwaltung für reguläre Nutzer im Desktop Dropdown -->
<a href="{{ url_for('user_profile') }}">Mein Profil</a>
<a href="{{ url_for('my_tutorials') }}">Tutorials</a>
<a href="{{ url_for('my_invoices') }}">Meine Rechnungen</a>