diff --git a/backend/models.py b/backend/models.py index 7e110bd..10baa38 100644 --- a/backend/models.py +++ b/backend/models.py @@ -47,17 +47,10 @@ class AdminUserUpdate(BaseModel): password: Optional[str] = None -# ---------- Profiles ("Who's Watching") ---------- +# ---------- Profile (1:1 with account) ---------- RATING_ORDER = ["G", "PG", "PG-13", "R", "NR"] -class ProfileCreate(BaseModel): - name: str - avatar_color: str = "#D9381E" - is_kids: bool = False - max_rating: str = "NR" # one of RATING_ORDER - - class ProfileUpdate(BaseModel): name: Optional[str] = None avatar_color: Optional[str] = None diff --git a/backend/server.py b/backend/server.py index e57b5c0..06bab66 100644 --- a/backend/server.py +++ b/backend/server.py @@ -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") diff --git a/frontend/src/App.js b/frontend/src/App.js index 1fdd8af..5b1c7c7 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -2,7 +2,6 @@ import { BrowserRouter, Routes, Route, Navigate, useLocation } from "react-route import { Toaster } from "sonner"; import { AuthProvider, useAuth } from "./lib/auth"; import { ProfileProvider, useProfile } from "./lib/profile"; -import ProtectedRoute from "./components/ProtectedRoute"; import Navbar from "./components/Navbar"; import GrainOverlay from "./components/GrainOverlay"; import ServiceStatus from "./components/ServiceStatus"; @@ -17,7 +16,6 @@ import Admin from "./pages/Admin"; import AdminUpload from "./pages/AdminUpload"; import Settings from "./pages/Settings"; import RadarrImport from "./pages/RadarrImport"; -import ProfileSelect from "./pages/ProfileSelect"; import TranscodeQueue from "./pages/TranscodeQueue"; import Shows from "./pages/Shows"; import ShowDetail from "./pages/ShowDetail"; @@ -30,11 +28,10 @@ const ProfileGate = ({ children }) => { const { user, loading: authLoading } = useAuth(); const { active, loading: profLoading } = useProfile(); const loc = useLocation(); - if (authLoading || profLoading) { + if (authLoading || profLoading || (user && !active)) { return