Sprint 14: Multi-Fix-Batch — SW by-v428, APP_VER 407

KI/Symptom-Check: JSON-Code-Fence stripping in ki.py, Dringlichkeit-Map mit Phosphor-Icons
Gewicht-Sync: health.js aktualisiert appState.activeDog.gewicht_kg auch bei Bearbeitung
Giftköder: icon:'check-circle' → UI.icon('check-circle') in emptyState-Call
Forum-Pills: overflow:hidden + text-overflow:ellipsis auf Desktop und Mobile
Moderation: Admins für Moderatoren unsichtbar, keine Aktions-Buttons auf Admins
Notizblock: Filter-Chips wrap 2-zeilig auf Desktop (min-width:1024px)
Tagebuch: Datenschutz-Hinweis "nur du kannst sie sehen", Sitter sieht keine bestehenden Einträge
diary.py: Sitter-Zugriff gibt leere Liste zurück (GET), Erstellen bleibt erlaubt
This commit is contained in:
rene 2026-04-26 11:06:59 +02:00
parent 02120bb532
commit 016eb52d83
12 changed files with 111 additions and 33 deletions

View file

@ -214,6 +214,18 @@ async def list_diary(dog_id: int, limit: int = 20, offset: int = 0,
user=Depends(get_current_user)):
with db() as conn:
_can_read_dog(dog_id, user["id"], conn)
# Sitter darf keine bestehenden Einträge lesen
dog = conn.execute("SELECT user_id FROM dogs WHERE id=?", (dog_id,)).fetchone()
is_owner = dog and dog["user_id"] == user["id"]
if not is_owner:
# Prüfen ob geteilter Hund (dog_shares) — darf lesen
shared = conn.execute(
"""SELECT 1 FROM dog_shares WHERE dog_id=? AND shared_with_id=? AND accepted_at IS NOT NULL""",
(dog_id, user["id"])
).fetchone()
if not shared:
# Weder Besitzer noch geteilter Nutzer → Sitter → leere Liste
return []
extra = "AND (d.is_milestone=1 OR d.typ='meilenstein')" if milestone else ""
if q:
pattern = f"%{q}%"

View file

@ -105,6 +105,8 @@ async def mod_users(
offset: int = 0,
user=Depends(require_moderator),
):
is_admin = user["rolle"] == "admin"
with db() as conn:
where = "WHERE 1=1"
params = []
@ -114,8 +116,12 @@ async def mod_users(
if banned:
where += " AND is_banned=1"
# Moderatoren sehen keine Admins
if not is_admin:
where += " AND rolle != 'admin' AND COALESCE(is_admin, 0) = 0"
# E-Mail nur für Admins; Moderatoren sehen maskierte Version
email_col = "email" if user["rolle"] == "admin" else \
email_col = "email" if is_admin else \
"SUBSTR(email,1,2)||'***@'||SUBSTR(email,INSTR(email,'@')+1) AS email"
rows = conn.execute(f"""
@ -145,12 +151,15 @@ async def mod_patch_user(uid: int, data: dict, user=Depends(require_moderator)):
with db() as conn:
target = conn.execute(
"SELECT id, rolle, name FROM users WHERE id=?", (uid,)
"SELECT id, rolle, is_admin, name FROM users WHERE id=?", (uid,)
).fetchone()
if not target:
raise HTTPException(404, "User nicht gefunden.")
if target["rolle"] == "admin" and user["rolle"] != "admin":
raise HTTPException(403, "Admins können nur von Admins verwaltet werden.")
# Moderatoren dürfen keine Admins bearbeiten
if user["rolle"] != "admin" and (
target["rolle"] == "admin" or target["is_admin"]
):
raise HTTPException(403, "Admins können nicht von Moderatoren bearbeitet werden.")
cols = ", ".join(f"{k}=?" for k in updates)
conn.execute(