Files
MClogger/web/blueprints/group_admin.py
simon 63ce0f9c5b modified: web/blueprints/group_admin.py
modified:   web/blueprints/site_admin.py
	modified:   web/config.py
	new file:   web/mailer.py
	modified:   web/panel_db.py
	modified:   web/templates/admin/base.html
	modified:   web/templates/admin/dashboard.html
	new file:   web/templates/admin/mail_settings.html
2026-04-13 10:29:48 +02:00

242 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
MCLogger Gruppen-Admin-Bereich
Gruppen-Admins können ihre Mitglieder und MC-DB-Verbindung verwalten.
"""
import json
from functools import wraps
from flask import Blueprint, render_template, request, redirect, url_for, session, flash
from config import Config
from mailer import send_mail
import panel_db as db
group_admin = Blueprint("group_admin", __name__, url_prefix="/group-admin")
ALL_PERMISSIONS = [
("view_dashboard", "Dashboard"),
("view_players", "Players"),
("view_sessions", "Sessions"),
("view_chat", "Chat"),
("view_commands", "Commands"),
("view_deaths", "Deaths"),
("view_blocks", "Block Events"),
("view_proxy", "Proxy Events"),
("view_server_events", "Server Events"),
("view_perms", "Permissions"),
]
def group_admin_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if not session.get("user_id"):
return redirect(url_for("auth.login"))
if session.get("is_site_admin"):
return redirect(url_for("site_admin.dashboard"))
if session.get("role") != "admin":
flash("You do not have group admin permission.", "danger")
return redirect(url_for("panel.dashboard"))
return f(*args, **kwargs)
return decorated
@group_admin.route("/")
@group_admin_required
def dashboard():
group_id = session["group_id"]
group = db.get_group_by_id(group_id)
members = db.get_group_members(group_id)
has_db = db.has_db_configured(group_id)
stats = {
"member_count": len(members),
"admin_count": sum(1 for m in members if m.get("role") == "admin"),
"db_configured": bool(has_db),
}
return render_template("group_admin/dashboard.html",
group=group, members=members, has_db=has_db, stats=stats)
# ──────────────────────────────────────────────────────────────
# Mitglieder
# ──────────────────────────────────────────────────────────────
@group_admin.route("/members")
@group_admin_required
def members():
group_id = session["group_id"]
group = db.get_group_by_id(group_id)
members = db.get_group_members(group_id)
pending_invites = db.list_active_group_invites(group_id)
all_users = db.list_all_users()
member_ids = {m["id"] for m in members}
non_members = [u for u in all_users if u["id"] not in member_ids and not u["is_site_admin"]]
return render_template("group_admin/members.html",
group=group, members=members, non_members=non_members, pending_invites=pending_invites,
all_permissions=ALL_PERMISSIONS)
@group_admin.route("/members/add", methods=["POST"])
@group_admin_required
def member_add():
group_id = session["group_id"]
user_id = request.form.get("user_id", type=int)
role = request.form.get("role", "member")
if user_id:
db.add_group_member(user_id, group_id, role)
flash("Member added.", "success")
return redirect(url_for("group_admin.members"))
@group_admin.route("/members/invite", methods=["POST"])
@group_admin_required
def member_invite():
group_id = session["group_id"]
username = request.form.get("username", "").strip()
email = request.form.get("email", "").strip()
role = request.form.get("role", "member")
if not username or not email:
flash("Username and email are required.", "danger")
return redirect(url_for("group_admin.members"))
if "@" not in email:
flash("Please provide a valid email address.", "danger")
return redirect(url_for("group_admin.members"))
if role not in {"member", "admin"}:
flash("Invalid role selected.", "danger")
return redirect(url_for("group_admin.members"))
if db.get_user_by_username(username):
flash("Username already exists.", "danger")
return redirect(url_for("group_admin.members"))
if db.get_user_by_email(email):
flash("Email address is already in use.", "danger")
return redirect(url_for("group_admin.members"))
if db.get_active_invite_by_email(group_id, email):
flash("There is already an active invitation for this email in the group.", "danger")
return redirect(url_for("group_admin.members"))
token = db.create_group_invite(group_id, username, email, role, session["user_id"])
invite_url = url_for("auth.accept_invite", token=token, _external=True)
mail_settings = db.get_site_mail_settings()
if mail_settings:
subject = f"Invitation to join {session.get('group_name', 'your group')}"
text_body = (
f"Hello {username},\n\n"
f"You have been invited to join the group '{session.get('group_name', 'your group')}' on MCLogger as {role}.\n"
f"Open this link to create your account:\n\n{invite_url}\n\n"
f"This invite expires in {Config.INVITE_EXPIRY_HOURS} hours.\n"
)
try:
send_mail(mail_settings, email, subject, text_body)
flash(f"Invitation email sent to '{email}'.", "success")
except Exception:
flash(f"Invitation created, but email delivery failed. Share this link manually: {invite_url}", "warning")
else:
flash(f"Invitation created for '{username}'. Share this link: {invite_url}", "success")
return redirect(url_for("group_admin.members"))
@group_admin.route("/invites/<int:invite_id>/revoke", methods=["POST"])
@group_admin_required
def revoke_invite(invite_id):
db.revoke_group_invite(invite_id, session["group_id"])
flash("Invitation revoked.", "success")
return redirect(url_for("group_admin.members"))
@group_admin.route("/members/<int:user_id>/edit", methods=["GET", "POST"])
@group_admin_required
def member_edit(user_id):
group_id = session["group_id"]
group = db.get_group_by_id(group_id)
member = db.get_group_member(user_id, group_id)
user = db.get_user_by_id(user_id)
if not member or not user:
flash("Member not found.", "danger")
return redirect(url_for("group_admin.members"))
raw_perms = member.get("permissions")
current_perms = json.loads(raw_perms) if isinstance(raw_perms, str) else (raw_perms or {})
if request.method == "POST":
role = request.form.get("role", "member")
new_perms = {key: bool(request.form.get(f"perm_{key}")) for key, _ in ALL_PERMISSIONS}
db.update_member(user_id, group_id, role, new_perms)
flash("Permissions updated.", "success")
return redirect(url_for("group_admin.members"))
return render_template("group_admin/member_edit.html",
group=group, user=user, member=member,
current_perms=current_perms, all_permissions=ALL_PERMISSIONS)
@group_admin.route("/members/<int:user_id>/remove", methods=["POST"])
@group_admin_required
def member_remove(user_id):
if user_id == session["user_id"]:
flash("You cannot remove yourself.", "danger")
else:
db.remove_group_member(user_id, session["group_id"])
flash("Member removed.", "success")
return redirect(url_for("group_admin.members"))
# ──────────────────────────────────────────────────────────────
# Datenbank-Konfiguration
# ──────────────────────────────────────────────────────────────
@group_admin.route("/database", methods=["GET", "POST"])
@group_admin_required
def database():
group_id = session["group_id"]
group = db.get_group_by_id(group_id)
has_db = db.has_db_configured(group_id)
error = None
creds = db.get_group_db_creds(group_id)
if request.method == "POST":
host = request.form.get("host", "").strip()
port = request.form.get("port", "3306").strip()
user = request.form.get("user", "").strip()
password = request.form.get("password", "")
database_name = request.form.get("database", "").strip()
# If password left blank and creds already exist, keep the stored password
if not password and creds:
password = creds["password"]
if not all([host, port, user, database_name]):
error = "Host, Port, User and Database name are required."
elif not password:
error = "Password is required."
else:
try:
import pymysql
test_conn = pymysql.connect(
host=host, port=int(port), user=user,
password=password, database=database_name,
connect_timeout=5
)
test_conn.close()
db.set_group_db_creds(group_id, host, int(port), user, password, database_name)
flash("Database connection saved and tested ✓", "success")
return redirect(url_for("group_admin.database"))
except Exception as e:
error = f"Connection test failed: {e}"
return render_template("group_admin/database.html",
group=group, has_db=has_db, creds=creds, error=error)
@group_admin.route("/database/delete", methods=["POST"])
@group_admin_required
def database_delete():
db.delete_group_db_creds(session["group_id"])
flash("Database connection removed.", "success")
return redirect(url_for("group_admin.database"))