""" User 모듈 Pydantic 스키마 정의 API 요청/응답 검증을 위한 스키마들입니다. """ from datetime import datetime from typing import Optional from pydantic import BaseModel, Field # ============================================================================= # 카카오 OAuth 스키마 # ============================================================================= class KakaoLoginResponse(BaseModel): """카카오 로그인 URL 응답""" auth_url: str = Field(..., description="카카오 인증 페이지 URL") class KakaoCallbackRequest(BaseModel): """카카오 콜백 요청 (인가 코드)""" code: str = Field(..., min_length=1, description="카카오 인가 코드") # ============================================================================= # JWT 토큰 스키마 # ============================================================================= class TokenResponse(BaseModel): """토큰 발급 응답""" access_token: str = Field(..., description="액세스 토큰") refresh_token: str = Field(..., description="리프레시 토큰") token_type: str = Field(default="Bearer", description="토큰 타입") expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)") class AccessTokenResponse(BaseModel): """액세스 토큰 갱신 응답""" access_token: str = Field(..., description="액세스 토큰") token_type: str = Field(default="Bearer", description="토큰 타입") expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)") class RefreshTokenRequest(BaseModel): """토큰 갱신 요청""" refresh_token: str = Field(..., min_length=1, description="리프레시 토큰") # ============================================================================= # 사용자 정보 스키마 # ============================================================================= class UserResponse(BaseModel): """사용자 정보 응답""" id: int = Field(..., description="사용자 ID") kakao_id: int = Field(..., description="카카오 회원번호") email: Optional[str] = Field(None, description="이메일") nickname: Optional[str] = Field(None, description="닉네임") profile_image_url: Optional[str] = Field(None, description="프로필 이미지 URL") thumbnail_image_url: Optional[str] = Field(None, description="썸네일 이미지 URL") is_active: bool = Field(..., description="계정 활성화 상태") is_admin: bool = Field(..., description="관리자 여부") last_login_at: Optional[datetime] = Field(None, description="마지막 로그인 일시") created_at: datetime = Field(..., description="가입 일시") model_config = {"from_attributes": True} class UserBriefResponse(BaseModel): """사용자 간략 정보 (토큰 응답에 포함)""" id: int = Field(..., description="사용자 ID") nickname: Optional[str] = Field(None, description="닉네임") email: Optional[str] = Field(None, description="이메일") profile_image_url: Optional[str] = Field(None, description="프로필 이미지 URL") is_new_user: bool = Field(..., description="신규 가입 여부") model_config = {"from_attributes": True} class LoginResponse(BaseModel): """로그인 응답 (토큰 + 사용자 정보)""" access_token: str = Field(..., description="액세스 토큰") refresh_token: str = Field(..., description="리프레시 토큰") token_type: str = Field(default="Bearer", description="토큰 타입") expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)") user: UserBriefResponse = Field(..., description="사용자 정보") # ============================================================================= # 내부 사용 스키마 (카카오 API 응답 파싱) # ============================================================================= class KakaoTokenResponse(BaseModel): """카카오 토큰 응답 (내부 사용)""" access_token: str token_type: str refresh_token: Optional[str] = None expires_in: int scope: Optional[str] = None refresh_token_expires_in: Optional[int] = None class KakaoProfile(BaseModel): """카카오 프로필 정보 (내부 사용)""" nickname: Optional[str] = None profile_image_url: Optional[str] = None thumbnail_image_url: Optional[str] = None is_default_image: Optional[bool] = None class KakaoAccount(BaseModel): """카카오 계정 정보 (내부 사용)""" email: Optional[str] = None profile: Optional[KakaoProfile] = None class KakaoUserInfo(BaseModel): """카카오 사용자 정보 (내부 사용)""" id: int kakao_account: Optional[KakaoAccount] = None