"""Authentication request and response schemas.""" from __future__ import annotations from datetime import datetime from typing import Literal from pydantic import BaseModel, Field, field_validator from app.schemas.user import UserResponse class LoginRequest(BaseModel): 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 CreateClientTokenRequest(BaseModel): name: str = Field(min_length=1) permission: Literal["read", "trade-write", "admin"] | None = None expires_in_days: int | None = Field(default=None, ge=1, le=3650) @field_validator("name") @classmethod def normalize_name(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("name은 비어 있을 수 없습니다") return normalized class AuthTokenResponse(BaseModel): id: str user_id: str name: str token_prefix: str permission: Literal["read", "trade-write", "admin"] token_type: Literal["session", "client", "bot"] bot_principal_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 IssuedClientTokenResponse(BaseModel): token: str token_type: Literal["bearer"] = "bearer" auth_token: AuthTokenResponse class CreateBotPrincipalRequest(BaseModel): name: str = Field(min_length=1) owner_user_id: str = Field(min_length=1) permission: Literal["read", "trade-write"] description: str | None = None @field_validator("name") @classmethod def normalize_bot_name(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("name은 비어 있을 수 없습니다") return normalized @field_validator("owner_user_id") @classmethod def normalize_owner_user_id(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("owner_user_id는 비어 있을 수 없습니다") return normalized class CreateSelfBotPrincipalRequest(BaseModel): name: str = Field(min_length=1) permission: Literal["read", "trade-write"] description: str | None = None @field_validator("name") @classmethod def normalize_bot_name(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("name은 비어 있을 수 없습니다") return normalized class BotPrincipalResponse(BaseModel): id: str name: str owner_user_id: str permission: Literal["read", "trade-write"] description: str | None = None is_active: bool created_at: datetime | None = None updated_at: datetime | None = None deactivated_at: datetime | None = None class CreateBotTokenRequest(BaseModel): name: str = Field(min_length=1) permission: Literal["read", "trade-write"] | None = None expires_in_days: int | None = Field(default=None, ge=1, le=3650) no_expiry: bool = False @field_validator("name") @classmethod def normalize_token_name(cls, value: str) -> str: normalized = value.strip() if not normalized: raise ValueError("name은 비어 있을 수 없습니다") return normalized class IssuedBotTokenResponse(BaseModel): token: str token_type: Literal["bearer"] = "bearer" auth_token: AuthTokenResponse class BotTokenIssueCliResponse(BaseModel): bot_id: str token_id: str expires_at: datetime | None = None revoked: bool token: str | None = None class BotTokenRevokeCliResponse(BaseModel): bot_id: str token_id: str revoked: bool revoked_at: datetime | None = None