"""Authentication request and response schemas.""" from __future__ import annotations from datetime import datetime from typing import Literal from pydantic import BaseModel, ConfigDict, Field, field_validator from app.schemas.user import UserResponse class LoginRequest(BaseModel): model_config = ConfigDict(extra="forbid") username: str = Field(min_length=1) password: str = Field(min_length=1) @field_validator("username") @classmethod def normalize_username(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("username은 비어 있을 수 없습니다") return normalized class TokenResponse(BaseModel): access_token: str token_type: Literal["bearer"] = "bearer" expires_at: datetime | None = None permission: Literal["read", "trade-write", "admin"] user: UserResponse class AuthTokenResponse(BaseModel): id: str user_id: str name: str token_prefix: str permission: Literal["read", "trade-write", "admin"] token_type: Literal["session", "client", "service"] daemon_id: str | None = None expires_at: datetime | None = None last_used_at: datetime | None = None revoked_at: datetime | None = None created_at: datetime | None = None class IssuedServiceToken(BaseModel): token: str token_type: Literal["bearer"] = "bearer" auth_token: AuthTokenResponse class IssueServiceTokenRequest(BaseModel): name: str = Field(min_length=1) username: str = Field(min_length=1) permission: Literal["read", "trade-write", "admin"] = "read" expires_in_days: int | None = Field(default=None, ge=1, le=3650) no_expiry: bool = False daemon_id: str | None = None class IssueServiceTokenApiResponse(BaseModel): token: str token_type: Literal["bearer"] = "bearer" runtime_id: str | None = None daemon_id: str | None = None auth_token: AuthTokenResponse class ServiceTokenIssueCliResponse(BaseModel): token_id: str name: str expires_at: datetime | None = None revoked: bool runtime_id: str | None = None daemon_id: str | None = None token: str | None = None class ServiceTokenRebindCliResponse(BaseModel): token_id: str name: str expires_at: datetime | None = None revoked: bool runtime_id: str | None = None daemon_id: str | None = None previous_daemon_id: str | None = None class ServiceTokenRevokeCliResponse(BaseModel): token_id: str name: str revoked: bool revoked_at: datetime | None = None