refactor(api): Pydantic request models for small route modules (R6 S1b)

payload:dict → typed request models across the single/double-endpoint modules:
- tags: TagCreateIn / TagUpdateIn (color:null clears → reads model_fields_set);
  _own_tag now delegates to owned_or_404.
- config: ConfigSetIn(value:Any) — keeps the "Missing 'value'" 400 via model_fields_set.
- me: SwitchAccountIn(user_id:int).
- scheduler: JobIntervalIn / MaintenanceSettingsIn — the manual int()+400 becomes a
  clean 422 on a non-number; the range checks stay 400.
- search: ClearSearchIn(video_ids:list[str]).
- public: WatchUnlockIn(password:str).

Gate: ruff clean, pytest 164 green.
This commit is contained in:
2026-07-26 04:51:25 +02:00
parent d982e8b9ae
commit a622470a28
6 changed files with 75 additions and 34 deletions
+7 -2
View File
@@ -1,4 +1,5 @@
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request
from pydantic import BaseModel
from sqlalchemy import func, select
from sqlalchemy.orm import Session
@@ -45,9 +46,13 @@ def my_accounts(
return out
class SwitchAccountIn(BaseModel):
user_id: int
@router.post("/switch")
def switch_account(
payload: dict,
body: SwitchAccountIn,
request: Request,
user: User = Depends(current_user),
db: Session = Depends(get_db),
@@ -55,7 +60,7 @@ def switch_account(
"""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")
target = body.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)