This commit is contained in:
2026-04-01 18:31:33 +02:00
parent 6e23e32bb0
commit b5c7c5305a
95 changed files with 9609 additions and 2374 deletions

147
backend/routers/profile.py Normal file
View File

@@ -0,0 +1,147 @@
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session
from core.database import get_db
from core.dependencies import get_current_user
from core.models import Card as CardModel
from core.models import Deck as DeckModel
from core.models import User as UserModel
router = APIRouter()
def _serialize_card_public(card: CardModel) -> dict:
"""Card fields safe to expose on public profiles (no user_id)."""
return {
"id": str(card.id),
"name": card.name,
"image_link": card.image_link,
"card_rarity": card.card_rarity,
"card_type": card.card_type,
"text": card.text,
"attack": card.attack,
"defense": card.defense,
"cost": card.cost,
"is_favorite": card.is_favorite,
"willing_to_trade": card.willing_to_trade,
}
class UpdateProfileRequest(BaseModel):
trade_wishlist: str
@router.get("/profile")
def get_profile(user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
total_games = user.wins + user.losses
most_played_deck = (
db.query(DeckModel)
.filter(DeckModel.user_id == user.id, DeckModel.times_played > 0)
.order_by(DeckModel.times_played.desc())
.first()
)
most_played_card = (
db.query(CardModel)
.filter(CardModel.user_id == user.id, CardModel.times_played > 0)
.order_by(CardModel.times_played.desc())
.first()
)
return {
"username": user.username,
"email": user.email,
"email_verified": user.email_verified,
"created_at": user.created_at,
"wins": user.wins,
"losses": user.losses,
"shards": user.shards,
"win_rate": round((user.wins / total_games) * 100) if total_games > 0 else None,
"trade_wishlist": user.trade_wishlist or "",
"most_played_deck": {
"name": most_played_deck.name,
"times_played": most_played_deck.times_played,
} if most_played_deck else None,
"most_played_card": {
"name": most_played_card.name,
"times_played": most_played_card.times_played,
"card_type": most_played_card.card_type,
"card_rarity": most_played_card.card_rarity,
"image_link": most_played_card.image_link,
} if most_played_card else None,
}
@router.get("/profile/refresh-status")
def refresh_status(user: UserModel = Depends(get_current_user)):
if not user.last_refresh_at:
return {"can_refresh": True, "next_refresh_at": None}
next_refresh = user.last_refresh_at + timedelta(minutes=10)
can_refresh = datetime.now() >= next_refresh
return {
"can_refresh": can_refresh,
"next_refresh_at": next_refresh.isoformat() if not can_refresh else None,
}
@router.patch("/profile")
def update_profile(req: UpdateProfileRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
user.trade_wishlist = req.trade_wishlist
db.commit()
return {"trade_wishlist": user.trade_wishlist}
@router.get("/users")
def search_users(q: str, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
# Require auth to prevent scraping
if len(q) < 2:
return []
results = (
db.query(UserModel)
.filter(UserModel.username.ilike(f"%{q}%"))
.limit(20)
.all()
)
return [
{
"username": u.username,
"wins": u.wins,
"losses": u.losses,
"win_rate": round(u.wins / (u.wins + u.losses) * 100) if (u.wins + u.losses) > 0 else 0,
}
for u in results
]
@router.get("/users/{username}")
def get_public_profile(username: str, db: Session = Depends(get_db)):
user = db.query(UserModel).filter(UserModel.username == username).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
total_games = user.wins + user.losses
favorite_cards = (
db.query(CardModel)
.filter(CardModel.user_id == user.id, CardModel.is_favorite == True)
.order_by(CardModel.received_at.desc())
.all()
)
wtt_cards = (
db.query(CardModel)
.filter(CardModel.user_id == user.id, CardModel.willing_to_trade == True)
.order_by(CardModel.received_at.desc())
.all()
)
return {
"username": user.username,
"wins": user.wins,
"losses": user.losses,
"win_rate": round((user.wins / total_games) * 100) if total_games > 0 else None,
"trade_wishlist": user.trade_wishlist or "",
"last_active_at": user.last_active_at.isoformat() if user.last_active_at else None,
"favorite_cards": [_serialize_card_public(c) for c in favorite_cards],
"wtt_cards": [_serialize_card_public(c) for c in wtt_cards],
}