diff --git a/app/main.py b/app/main.py
index 0a18cd3..ad74651 100644
--- a/app/main.py
+++ b/app/main.py
@@ -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(
+ "
Der Gründer Kai muss Admin bleiben.
",
+ 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(
+ "Der Gründer Kai kann nicht gelöscht werden.
",
+ 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/")
diff --git a/app/templates/admin_users.html b/app/templates/admin_users.html
index 1bcc831..8a795af 100644
--- a/app/templates/admin_users.html
+++ b/app/templates/admin_users.html
@@ -12,6 +12,7 @@
.role-form { display: flex; gap: 8px; }
.role-form label { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.role-form label input { width: auto; margin: 0; }
+ .founder-lock { color: #fbbf24; font-weight: 700; white-space: nowrap; }
@media (max-width: 700px) { .user-row { grid-template-columns: 1fr; } }
@@ -31,15 +32,19 @@
{% for managed_user in users %}
-
{{ managed_user.display_name }}@{{ managed_user.username }} · {{ managed_user.email }} · seit {{ managed_user.created_at }}
-
+
{% if managed_user.username|lower == 'kai' %}🏴☠️ {% endif %}{{ managed_user.display_name }}@{{ managed_user.username }} · {{ managed_user.email }} · seit {{ managed_user.created_at }}
+ {% if managed_user.username|lower == 'kai' %}
+
🏴☠️ Gründer · Admin (fest)
+ {% else %}
+
+ {% endif %}
- {% if managed_user.id != user.id %}
{% endif %}
+ {% if managed_user.id != user.id and managed_user.username|lower != 'kai' %}
{% endif %}
{% endfor %}