from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import func, select from sqlalchemy.orm import Session from app.auth import current_user, has_read_scope, has_write_scope, is_allowed from app.db import get_db from app.models import Invite, User router = APIRouter(prefix="/api/me", tags=["me"]) @router.get("/accounts") def my_accounts( request: Request, user: User = Depends(current_user), db: Session = Depends(get_db), ) -> list[dict]: """The accounts that have authenticated in this browser session (switchable without a new Google sign-in). The active one is flagged.""" ids = request.session.get("account_ids") or [user.id] rows = {u.id: u for u in db.query(User).filter(User.id.in_(ids)).all()} out = [] for uid in ids: # preserve recency order from the session u = rows.get(uid) if u is not None: out.append( { "id": u.id, "email": u.email, "display_name": u.display_name, "avatar_url": u.avatar_url, "active": u.id == user.id, } ) return out @router.post("/switch") def switch_account( payload: dict, request: Request, user: User = Depends(current_user), db: Session = Depends(get_db), ) -> dict: """Switch the active account to another one already authenticated in this browser. No Google round-trip — but only accounts present in the session list (proof they signed in here) are allowed, and access is re-checked in case the invite was revoked since.""" target = payload.get("user_id") if target not in (request.session.get("account_ids") or []): raise HTTPException(status_code=403, detail="Not an account you've signed into here.") u = db.get(User, target) if u is None: raise HTTPException(status_code=404, detail="That account no longer exists.") if not is_allowed(db, u.email): raise HTTPException(status_code=403, detail="That account no longer has access.") request.session["user_id"] = target return {"ok": True, "user_id": target} @router.get("") def get_me( user: User = Depends(current_user), db: Session = Depends(get_db) ) -> dict: pending_invites = 0 if user.role == "admin": pending_invites = ( db.scalar( select(func.count()) .select_from(Invite) .where(Invite.status == "pending") ) or 0 ) return { "id": user.id, "email": user.email, "display_name": user.display_name, "avatar_url": user.avatar_url, "role": user.role, "can_read": has_read_scope(user), "can_write": has_write_scope(user), "pending_invites": pending_invites, "preferences": user.preferences or {}, } @router.put("/preferences") def update_preferences( preferences: dict, user: User = Depends(current_user), db: Session = Depends(get_db), ) -> dict: # Merge so partial updates don't wipe other keys. merged = dict(user.preferences or {}) merged.update(preferences) user.preferences = merged db.add(user) db.commit() return {"preferences": merged}