modified: .gitignore

This commit is contained in:
simon
2026-04-13 11:44:30 +02:00
parent 63ce0f9c5b
commit d25536e9c4
40 changed files with 248 additions and 69 deletions

View File

@@ -3,11 +3,13 @@ MCLogger Gruppen-Admin-Bereich
Gruppen-Admins können ihre Mitglieder und MC-DB-Verbindung verwalten.
"""
import json
from datetime import datetime, timedelta
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
from roles import GROUP_MANAGEMENT_ROLES, GROUP_ROLE_OPTIONS, GROUP_ROLE_SET, role_label
group_admin = Blueprint("group_admin", __name__, url_prefix="/group-admin")
@@ -32,7 +34,7 @@ def group_admin_required(f):
return redirect(url_for("auth.login"))
if session.get("is_site_admin"):
return redirect(url_for("site_admin.dashboard"))
if session.get("role") != "admin":
if session.get("role") not in GROUP_MANAGEMENT_ROLES:
flash("You do not have group admin permission.", "danger")
return redirect(url_for("panel.dashboard"))
return f(*args, **kwargs)
@@ -48,7 +50,7 @@ def dashboard():
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"),
"admin_count": sum(1 for m in members if m.get("role") in GROUP_MANAGEMENT_ROLES),
"db_configured": bool(has_db),
}
return render_template("group_admin/dashboard.html",
@@ -71,7 +73,9 @@ def 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)
all_permissions=ALL_PERMISSIONS,
role_options=GROUP_ROLE_OPTIONS,
role_label=role_label)
@group_admin.route("/members/add", methods=["POST"])
@@ -79,7 +83,10 @@ def members():
def member_add():
group_id = session["group_id"]
user_id = request.form.get("user_id", type=int)
role = request.form.get("role", "member")
role = request.form.get("role", "viewer")
if role not in GROUP_ROLE_SET:
flash("Invalid role selected.", "danger")
return redirect(url_for("group_admin.members"))
if user_id:
db.add_group_member(user_id, group_id, role)
flash("Member added.", "success")
@@ -92,7 +99,7 @@ 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")
role = request.form.get("role", "viewer")
if not username or not email:
flash("Username and email are required.", "danger")
@@ -102,14 +109,22 @@ def member_invite():
flash("Please provide a valid email address.", "danger")
return redirect(url_for("group_admin.members"))
if role not in {"member", "admin"}:
if role not in GROUP_ROLE_SET:
flash("Invalid role selected.", "danger")
return redirect(url_for("group_admin.members"))
if db.count_active_group_invites(group_id) >= Config.INVITE_MAX_ACTIVE_PER_GROUP:
flash("Active invite limit reached for this group. Revoke old invites or wait for expiry.", "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_active_invite_by_username(group_id, username):
flash("There is already an active invitation for this username in the group.", "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"))
@@ -119,6 +134,7 @@ def member_invite():
return redirect(url_for("group_admin.members"))
token = db.create_group_invite(group_id, username, email, role, session["user_id"])
invite = db.get_invite_by_token(token)
invite_url = url_for("auth.accept_invite", token=token, _external=True)
mail_settings = db.get_site_mail_settings()
@@ -126,12 +142,14 @@ def member_invite():
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"You have been invited to join the group '{session.get('group_name', 'your group')}' on MCLogger as {role_label(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)
if invite:
db.mark_group_invite_sent(invite["id"], group_id)
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")
@@ -140,6 +158,46 @@ def member_invite():
return redirect(url_for("group_admin.members"))
@group_admin.route("/invites/<int:invite_id>/resend", methods=["POST"])
@group_admin_required
def resend_invite(invite_id):
group_id = session["group_id"]
invite = db.get_group_invite_by_id(invite_id, group_id)
if not invite:
flash("Invitation not found.", "danger")
return redirect(url_for("group_admin.members"))
if invite.get("accepted_at") or invite.get("revoked_at") or invite["expires_at"] <= datetime.utcnow():
flash("Invitation is no longer active.", "danger")
return redirect(url_for("group_admin.members"))
last_sent_at = invite.get("last_sent_at")
if last_sent_at and (datetime.utcnow() - last_sent_at) < timedelta(seconds=Config.INVITE_RESEND_COOLDOWN_SECONDS):
flash("Please wait before resending this invite again.", "warning")
return redirect(url_for("group_admin.members"))
mail_settings = db.get_site_mail_settings()
if not mail_settings:
flash("No SMTP settings configured by Site Admin.", "danger")
return redirect(url_for("group_admin.members"))
invite_url = url_for("auth.accept_invite", token=invite["token"], _external=True)
subject = f"Invitation to join {session.get('group_name', 'your group')}"
text_body = (
f"Hello {invite['invited_username']},\n\n"
f"You have been invited to join the group '{session.get('group_name', 'your group')}' on MCLogger as {role_label(invite['role'])}.\n"
f"Open this link to create your account:\n\n{invite_url}\n\n"
f"This invite expires on {invite['expires_at']}.\n"
)
try:
send_mail(mail_settings, invite["invited_email"], subject, text_body)
db.mark_group_invite_sent(invite_id, group_id)
flash("Invitation email resent.", "success")
except Exception:
flash("Resend failed. Please verify SMTP settings and try again.", "danger")
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):
@@ -163,7 +221,10 @@ def member_edit(user_id):
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")
role = request.form.get("role", "viewer")
if role not in GROUP_ROLE_SET:
flash("Invalid role selected.", "danger")
return redirect(url_for("group_admin.members"))
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")
@@ -171,7 +232,9 @@ def member_edit(user_id):
return render_template("group_admin/member_edit.html",
group=group, user=user, member=member,
current_perms=current_perms, all_permissions=ALL_PERMISSIONS)
current_perms=current_perms, all_permissions=ALL_PERMISSIONS,
role_options=GROUP_ROLE_OPTIONS,
role_label=role_label)
@group_admin.route("/members/<int:user_id>/remove", methods=["POST"])