mirror of
https://github.com/myronblair/kino-app
synced 2026-07-27 21:18:43 -05:00
Merge watch profiles into user accounts — one profile per login, no picker
- Removed the "Who's Watching" sub-profile picker entirely: ProfileProvider now auto-resolves the single profile tied to the account immediately after login - Backend: removed POST/DELETE /profiles (no more creating/deleting extra profiles — lifecycle is entirely driven by user create/delete, already wired in the admin Users CRUD); kept GET (internal) and PATCH (edit your own profile's avatar/kids-mode/rating cap) - Deleted ProfileSelect page and the now-unused ProtectedRoute component - Navbar: removed the "switch profile" action, account avatar is now just a static display - api.js: dropped the now-dead X-Profile-Id header logic (backend already falls back to the account's single profile when the header is absent)
This commit is contained in:
+5
-25
@@ -17,7 +17,7 @@ from motor.motor_asyncio import AsyncIOMotorClient
|
||||
from models import (
|
||||
UserCreate, UserLogin, UserPublic, TokenResponse, PasswordChange,
|
||||
AdminUserCreate, AdminUserUpdate,
|
||||
Profile, ProfileCreate, ProfileUpdate, RATING_ORDER,
|
||||
Profile, ProfileUpdate, RATING_ORDER,
|
||||
Movie, MovieCreate, MovieUpdate,
|
||||
Subtitle,
|
||||
WatchlistItem, ProgressUpsert,
|
||||
@@ -308,25 +308,17 @@ async def admin_delete_user(user_id: str, user: dict = Depends(require_admin)):
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
# ============ PROFILES ============
|
||||
# ============ PROFILE (1:1 with account — no separate sub-profile management) ============
|
||||
@api.get("/profiles", response_model=List[Profile])
|
||||
async def list_profiles(user: dict = Depends(get_current_user)):
|
||||
docs = await db.profiles.find({"user_id": user["id"]}, {"_id": 0}).sort("created_at", 1).to_list(20)
|
||||
"""Always exactly one profile per account; get_active_profile auto-creates it if missing."""
|
||||
docs = await db.profiles.find({"user_id": user["id"]}, {"_id": 0}).sort("created_at", 1).to_list(1)
|
||||
return docs
|
||||
|
||||
|
||||
@api.post("/profiles", response_model=Profile)
|
||||
async def create_profile(payload: ProfileCreate, user: dict = Depends(get_current_user)):
|
||||
count = await db.profiles.count_documents({"user_id": user["id"]})
|
||||
if count >= 5:
|
||||
raise HTTPException(status_code=400, detail="Max 5 profiles per account")
|
||||
prof = Profile(user_id=user["id"], **payload.model_dump()).model_dump()
|
||||
await db.profiles.insert_one(prof)
|
||||
return _strip(prof)
|
||||
|
||||
|
||||
@api.patch("/profiles/{pid}", response_model=Profile)
|
||||
async def update_profile(pid: str, payload: ProfileUpdate, user: dict = Depends(get_current_user)):
|
||||
"""Edit your own single profile (avatar color, kids mode, rating cap) — not a multi-profile switcher."""
|
||||
updates = {k: v for k, v in payload.model_dump().items() if v is not None}
|
||||
if not updates:
|
||||
raise HTTPException(status_code=400, detail="No fields to update")
|
||||
@@ -340,18 +332,6 @@ async def update_profile(pid: str, payload: ProfileUpdate, user: dict = Depends(
|
||||
return res
|
||||
|
||||
|
||||
@api.delete("/profiles/{pid}")
|
||||
async def delete_profile(pid: str, user: dict = Depends(get_current_user)):
|
||||
if await db.profiles.count_documents({"user_id": user["id"]}) <= 1:
|
||||
raise HTTPException(status_code=400, detail="Cannot delete the only profile")
|
||||
res = await db.profiles.delete_one({"id": pid, "user_id": user["id"]})
|
||||
if res.deleted_count == 0:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
await db.watchlist.delete_many({"profile_id": pid})
|
||||
await db.progress.delete_many({"profile_id": pid})
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
# ============ MOVIES ============
|
||||
def _movie_filter_for_profile(profile: dict) -> dict:
|
||||
cap = profile.get("max_rating", "NR")
|
||||
|
||||
Reference in New Issue
Block a user