Protect founder account and add attendance badge levels

This commit is contained in:
kai
2026-08-26 18:56:02 +02:00
parent c4fa6f077c
commit 41b2c5cd61
2 changed files with 39 additions and 13 deletions
+28 -7
View File
@@ -78,12 +78,14 @@ INITIAL_ADMIN_EMAIL = os.environ.get("INITIAL_ADMIN_EMAIL")
BADGE_DEFINITIONS = (
("founder", "Gründer", "⚔️", None, "Von Anfang an dabei und Pingu Concerts mit aufgebaut", "special"),
("admin", "Admin", "🛡", None, "Verantwortung für Pingu Concerts", "special"),
("admin", "Admin", "🏴‍☠", None, "Verantwortung für Pingu Concerts", "special"),
("beta_tester", "Beta Tester", "🧪", None, "In der Beta dabei", "beta"),
("first_gig", "Erster Gig", "🎸", 1, "Dein erstes besuchtes Konzert", "attendance"),
("regular", "Stammgast", "🤘", 5, "5 Konzerte besucht", "attendance"),
("ten_gigs", "Zehnerrunde", "🔥", 10, "10 Konzerte besucht", "attendance"),
("tour_veteran", "Tourveteran", "", 25, "25 Konzerte besucht", "attendance"),
("fifty_gigs", "Fünfzig Konzerte", "💀", 50, "50 Konzerte besucht", "attendance"),
("hundred_gigs", "Hunderterclub", "👑", 100, "100 Konzerte besucht", "attendance"),
)
ATTENDANCE_BADGE_CODES = tuple(
badge_code
@@ -119,6 +121,11 @@ def ensure_schema():
ADD COLUMN IF NOT EXISTS is_admin BOOLEAN NOT NULL DEFAULT FALSE
""",
"""
UPDATE users
SET is_admin = TRUE
WHERE LOWER(username) = 'kai'
""",
"""
ALTER TABLE users
ADD COLUMN IF NOT EXISTS avatar_path TEXT
""",
@@ -1107,11 +1114,13 @@ def grant_earned_badges(user_id: int, attended_count: int, registered_at):
(user_id,),
)
# Konzert-Patches sind eine Level-Leiste: immer nur die höchste
# erreichte Stufe anzeigen (ältere Stufen werden ersetzt).
cursor.execute(
"DELETE FROM user_badges WHERE user_id = %s AND badge_code = ANY(%s)",
(user_id, list(ATTENDANCE_BADGE_CODES)),
)
if highest_attendance_badge:
cursor.execute(
"DELETE FROM user_badges WHERE user_id = %s AND badge_code = ANY(%s)",
(user_id, list(ATTENDANCE_BADGE_CODES)),
)
cursor.execute(
"""
INSERT INTO user_badges (user_id, badge_code)
@@ -1685,6 +1694,13 @@ def update_user_role(
with get_db_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT username FROM users WHERE id = %s", (user_id,))
target_user = cursor.fetchone()
if target_user and (target_user[0] or "").casefold() == "kai" and role != "admin":
return HTMLResponse(
"<h1>Der Gründer Kai muss Admin bleiben.</h1>",
status_code=400,
)
cursor.execute(
"UPDATE users SET is_admin = %s WHERE id = %s",
(role == "admin", user_id),
@@ -1708,15 +1724,20 @@ def delete_user(request: Request, user_id: int):
with get_db_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT avatar_path FROM users WHERE id = %s", (user_id,))
cursor.execute("SELECT username, avatar_path FROM users WHERE id = %s", (user_id,))
target_user = cursor.fetchone()
if target_user and (target_user[0] or "").casefold() == "kai":
return HTMLResponse(
"<h1>Der Gründer Kai kann nicht gelöscht werden.</h1>",
status_code=400,
)
cursor.execute("SELECT path FROM concert_photos WHERE user_id = %s", (user_id,))
photo_paths = [row[0] for row in cursor.fetchall()]
cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
connection.commit()
if target_user:
remove_uploaded_file(target_user[0], AVATAR_DIR, "/static/uploads/avatars/")
remove_uploaded_file(target_user[1], AVATAR_DIR, "/static/uploads/avatars/")
for photo_path in photo_paths:
remove_uploaded_file(photo_path, PHOTO_DIR, "/static/uploads/photos/")