"""Schemas for /accounts/me/security — session-policy management. See docs/plans/2026-05-13-session-expiration-policy.md §4.7 and §4.11. """ from typing import Literal, Optional from pydantic import BaseModel, Field class SessionPolicyResponse(BaseModel): """GET /accounts/me/security — the policy in effect for this account. Surfaces both the override (which may be NULL) and the effective value (after defaults applied) so the frontend can show the current state without re-implementing the defaults logic. """ # Per-account override values, NULL = "use system default." idle_minutes: Optional[int] = Field( default=None, description="Account override; NULL means use the system default.", ) absolute_minutes: Optional[int] = Field(default=None) # Effective values after defaults applied (always non-NULL). effective_idle_minutes: int effective_absolute_minutes: int # System-imposed bounds for the Custom-preset form inputs. idle_minutes_min: int idle_minutes_max: int absolute_minutes_min: int absolute_minutes_max: int class SessionPolicyUpdateRequest(BaseModel): """PATCH /accounts/me/security — set or clear the per-account override. Pass `null` for either field to clear the override and fall back to the system default. Both bounds checks and the idle <= absolute invariant are validated against the *effective* values at the endpoint, since the DB CHECK constraint only covers the both-set case. """ idle_minutes: Optional[int] = None absolute_minutes: Optional[int] = None class RevokeSessionsRequest(BaseModel): """POST /accounts/me/security/revoke-sessions — bulk-revoke refresh tokens.""" scope: Literal["all", "others"] = "all" class RevokeSessionsResponse(BaseModel): revoked_count: int