Feature+Fix: Referral-Admin, Pro-Gates, Karten-Layer, onDogChange, Staging-Media (SW by-v855)

Features:
- Admin: Referral-Tab (Virality Factor, Top-Werber, letzte Einladungen)
- Karte: Regenradar (RainViewer, zoom→7, color=4), Temperatur-Layer (OWM) mit Zahlen-Grid + Legende
- Wetter-Chip: Umschwung-Warnung bei ≥40%-Sprung in Niederschlagswahrscheinlichkeit
- Freundschaftsanfragen: Accept/Decline direkt in Notifications (kein Pro nötig)
- Freunde-Seite für Standard-User freigeschaltet

Pro-Gates:
- KI-Trainer, Routenvorschläge, Regenradar, Temperatur-Layer jetzt Pro-Feature
- Pro-Badge (P) auf Chips für Admins/Mods in allen Welten + Welten-einrichten
- Oranger Banner auf Pro-Seiten für Admin/Mod/Manager

Bugfixes:
- onDogChange: uebungen.js (Cache leeren + _render), trainingsplaene.js (war leer)
- robots.txt vereinfacht (nur Disallow, kein Allow-Durcheinander)
- Hintergrund-Foto: Querformat-Filter korrigiert (kein Fallback auf Hochformat)
- Staging Media: FileResponse mit korrektem MIME-Type, no-cache statt immutable
- Staging Docker: MEDIA_DIR=/data/media + /prod-media:ro Fallback-Handler
- Staging-Fix: Bild-Upload auf zweitem Hund (war Read-only file system)
This commit is contained in:
rene 2026-05-11 17:23:29 +02:00
parent 2f021f54c2
commit 79fa5684b9
22 changed files with 570 additions and 58 deletions

View file

@ -583,6 +583,48 @@ async def scheduler_trigger(job_id: str, user=Depends(require_admin)):
return {"ok": True, "job_id": job_id}
# ------------------------------------------------------------------
# GET /api/admin/referrals — User-wirbt-User Top 100
# ------------------------------------------------------------------
@router.get("/referrals")
async def referral_stats(user=Depends(require_mod)):
with db() as conn:
# Top-Werber mit Anzahl
top = conn.execute("""
SELECT r.id, r.name, r.email,
COUNT(u.id) AS invited_count,
r.created_at AS member_since
FROM users u
JOIN users r ON r.id = u.referred_by
GROUP BY r.id
ORDER BY invited_count DESC
LIMIT 100
""").fetchall()
# Alle Einladungen (für Detail-Ansicht)
invites = conn.execute("""
SELECT u.id, u.name, u.email, u.created_at,
r.id AS referrer_id, r.name AS referrer_name
FROM users u
JOIN users r ON r.id = u.referred_by
ORDER BY u.created_at DESC
LIMIT 500
""").fetchall()
total_users = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
total_referred = conn.execute(
"SELECT COUNT(*) FROM users WHERE referred_by IS NOT NULL"
).fetchone()[0]
return {
"top_referrers": [dict(r) for r in top],
"recent_invites": [dict(r) for r in invites],
"total_users": total_users,
"total_referred": total_referred,
"viral_factor": round(total_referred / max(total_users - total_referred, 1), 2),
}
# ------------------------------------------------------------------
# GET /api/admin/ki/history — 30-Tage-Verlauf + Top-User (all-time)
# ------------------------------------------------------------------