Feature: Referral-System — User wirbt User

- DB: referral_code (8-stellig, eindeutig) + referred_by zu users
  Bestehende User erhalten automatisch einen Code
- GET /api/auth/referral: Code, Link und Anzahl geworbener User
- POST /api/auth/register: ref_code Parameter für Zuordnung
- Settings: 'App empfehlen'-Karte mit Link, Teilen-Button und Botschafter-Badges
  (Botschafter ab 1, Super ab 5, Top ab 10 Einladungen)
- app.js: ?ref=CODE aus URL in sessionStorage speichern
- APP_VER 222, SW by-v244
This commit is contained in:
rene 2026-04-19 11:09:24 +02:00
parent 82d9e26823
commit 6d757b86c2
6 changed files with 121 additions and 7 deletions

View file

@ -1,5 +1,10 @@
"""BAN YARO — Auth Routes"""
import os
import secrets
import string
from typing import Optional
from fastapi import APIRouter, HTTPException, Response, Depends
from pydantic import BaseModel, EmailStr
from database import db
@ -20,6 +25,12 @@ class RegisterRequest(BaseModel):
email: EmailStr
password: str
name: str
ref_code: Optional[str] = None
def _gen_referral_code() -> str:
alphabet = string.ascii_uppercase + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(8))
def _set_cookie(response: Response, token: str):
@ -45,10 +56,11 @@ async def register(data: RegisterRequest, response: Response):
"SELECT 1 FROM users WHERE name=? COLLATE NOCASE", (name,)
).fetchone():
raise HTTPException(400, "Dieser Name ist bereits vergeben. Bitte wähle einen anderen.")
code = _gen_referral_code()
try:
conn.execute(
"INSERT INTO users (email, pw_hash, name) VALUES (?,?,?)",
(data.email, hash_password(data.password), name)
"INSERT INTO users (email, pw_hash, name, referral_code) VALUES (?,?,?,?)",
(data.email, hash_password(data.password), name, code)
)
except Exception:
# Fallback falls UNIQUE-Index greift (Race Condition)
@ -56,6 +68,16 @@ async def register(data: RegisterRequest, response: Response):
user = conn.execute(
"SELECT id, rolle FROM users WHERE email=?", (data.email,)
).fetchone()
new_user_id = user["id"]
if data.ref_code:
referrer = conn.execute(
"SELECT id FROM users WHERE referral_code=? AND id != ?",
(data.ref_code.strip().upper(), new_user_id)
).fetchone()
if referrer:
conn.execute("UPDATE users SET referred_by=? WHERE id=?",
(referrer['id'], new_user_id))
token = create_token(user["id"], user["rolle"])
_set_cookie(response, token)
@ -90,6 +112,21 @@ async def logout(response: Response):
return {"ok": True}
@router.get("/referral")
async def get_referral_info(user=Depends(get_current_user)):
with db() as conn:
row = conn.execute(
"SELECT referral_code, (SELECT COUNT(*) FROM users WHERE referred_by=?) AS count FROM users WHERE id=?",
(user['id'], user['id'])
).fetchone()
base = os.getenv("APP_URL", "https://banyaro.app")
return {
"code": row["referral_code"],
"count": row["count"],
"link": f"{base}/?ref={row['referral_code']}",
}
@router.get("/me")
async def me(user=Depends(get_current_user)):
with db() as conn: