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:
@@ -1,8 +1,10 @@
|
||||
"""Admin Configuration page API: read/edit the DB-overridable operational settings defined in
|
||||
app.sysconfig. Secret values (e.g. SMTP password) are write-only — never returned."""
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app import audit
|
||||
@@ -30,17 +32,24 @@ def get_config(_: User = Depends(admin_user), db: Session = Depends(get_db)) ->
|
||||
return sysconfig.describe(db)
|
||||
|
||||
|
||||
class ConfigSetIn(BaseModel):
|
||||
# A config value is arbitrarily typed (str/int/bool/…), validated downstream by
|
||||
# sysconfig.set_value. `value` defaults to a sentinel so a MISSING value stays a 400
|
||||
# ("Missing 'value'") while an explicit `null` is a real, settable value.
|
||||
value: Any = None
|
||||
|
||||
|
||||
@router.patch("/{key}")
|
||||
def set_config(
|
||||
key: str,
|
||||
payload: dict,
|
||||
body: ConfigSetIn,
|
||||
admin: User = Depends(admin_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
s = sysconfig.spec(key)
|
||||
if s is None:
|
||||
raise HTTPException(status_code=404, detail="Unknown config key")
|
||||
if "value" not in payload:
|
||||
if "value" not in body.model_fields_set:
|
||||
raise HTTPException(status_code=400, detail="Missing 'value'")
|
||||
if s.secret and not sysconfig.secrets_manageable():
|
||||
raise HTTPException(
|
||||
@@ -49,7 +58,7 @@ def set_config(
|
||||
)
|
||||
old = None if s.secret else sysconfig.get(db, key)
|
||||
try:
|
||||
sysconfig.set_value(db, key, payload["value"])
|
||||
sysconfig.set_value(db, key, body.value)
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc) or "Invalid value")
|
||||
# Never log a secret's value — only that it was changed, by whom. Non-secret values are logged
|
||||
|
||||
Reference in New Issue
Block a user