"""Users & Settings router.""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.database import get_db from app.models import User from app.schemas import UserPublic, UserMe, UserUpdate, ByokKeysUpdate from app.middleware.auth import get_current_user, require_tier from app.services.byok import encrypt_key, get_stored_providers router = APIRouter() @router.get("/users/{username}", response_model=UserPublic) async def get_user_profile(username: str, db: AsyncSession = Depends(get_db)): result = await db.execute(select(User).where(User.username == username)) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") return user @router.get("/me", response_model=UserMe) async def get_me(user: User = Depends(get_current_user)): return user @router.put("/me", response_model=UserMe) async def update_me( body: UserUpdate, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): updates = body.model_dump(exclude_unset=True) if not updates: return user # Check uniqueness for username/email changes if "username" in updates and updates["username"] != user.username: existing = await db.execute( select(User).where(User.username == updates["username"]) ) if existing.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Username already taken") if "email" in updates and updates["email"] != user.email: existing = await db.execute( select(User).where(User.email == updates["email"]) ) if existing.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Email already taken") for field, value in updates.items(): setattr(user, field, value) return user @router.put("/me/ai-keys") async def update_byok_keys( body: ByokKeysUpdate, db: AsyncSession = Depends(get_db), user: User = Depends(require_tier("pro", "studio")), ): """Store encrypted BYOK API keys for AI providers.""" from app.services.byok import save_user_keys await save_user_keys(db, user, body) providers = await get_stored_providers(db, user) return {"status": "ok", "configured_providers": providers} @router.get("/me/ai-keys") async def get_byok_keys( db: AsyncSession = Depends(get_db), user: User = Depends(require_tier("pro", "studio")), ): """List which providers have BYOK keys configured (never returns actual keys).""" providers = await get_stored_providers(db, user) return {"configured_providers": providers} # ── Creator Economy Stubs (501) ───────────────────────────── @router.get("/dashboard") async def creator_dashboard(user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Creator dashboard coming in future release") @router.get("/shaders/{shader_id}/unlock-status") async def unlock_status(shader_id: str, user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Source unlock coming in future release") @router.post("/shaders/{shader_id}/unlock") async def unlock_source(shader_id: str, user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Source unlock coming in future release") @router.post("/shaders/{shader_id}/commercial") async def purchase_commercial(shader_id: str, user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Commercial licensing coming in future release") @router.post("/me/creator/apply") async def apply_verified(user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Verified creator program coming in future release") @router.get("/me/creator/earnings") async def creator_earnings(user: User = Depends(get_current_user)): raise HTTPException(status_code=501, detail="Creator earnings coming in future release")