221 lines
7.7 KiB
Python
221 lines
7.7 KiB
Python
"""
|
|
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")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"auth_url": "https://kauth.kakao.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8000/api/v1/user/auth/kakao/callback&response_type=code"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class KakaoCallbackRequest(BaseModel):
|
|
"""카카오 콜백 요청 (인가 코드)"""
|
|
|
|
code: str = Field(..., min_length=1, description="카카오 인가 코드")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"code": "AUTHORIZATION_CODE_FROM_KAKAO"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# 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="액세스 토큰 만료 시간 (초)")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzA1MzE1MjAwfQ.xxx",
|
|
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy",
|
|
"token_type": "Bearer",
|
|
"expires_in": 3600
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class AccessTokenResponse(BaseModel):
|
|
"""액세스 토큰 갱신 응답"""
|
|
|
|
access_token: str = Field(..., description="액세스 토큰")
|
|
token_type: str = Field(default="Bearer", description="토큰 타입")
|
|
expires_in: int = Field(..., description="액세스 토큰 만료 시간 (초)")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzA1MzE1MjAwfQ.new_token",
|
|
"token_type": "Bearer",
|
|
"expires_in": 3600
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class RefreshTokenRequest(BaseModel):
|
|
"""토큰 갱신 요청"""
|
|
|
|
refresh_token: str = Field(..., min_length=1, description="리프레시 토큰")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# 사용자 정보 스키마
|
|
# =============================================================================
|
|
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,
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"id": 1,
|
|
"kakao_id": 1234567890,
|
|
"email": "user@kakao.com",
|
|
"nickname": "홍길동",
|
|
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
|
|
"thumbnail_image_url": "https://k.kakaocdn.net/dn/.../thumb.jpg",
|
|
"is_active": True,
|
|
"is_admin": False,
|
|
"last_login_at": "2026-01-15T10:30:00",
|
|
"created_at": "2026-01-01T09:00:00"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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,
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"id": 1,
|
|
"nickname": "홍길동",
|
|
"email": "user@kakao.com",
|
|
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
|
|
"is_new_user": False
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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="사용자 정보")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"example": {
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzA1MzE1MjAwfQ.xxx",
|
|
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDU4MzM2MDB9.yyy",
|
|
"token_type": "Bearer",
|
|
"expires_in": 3600,
|
|
"user": {
|
|
"id": 1,
|
|
"nickname": "홍길동",
|
|
"email": "user@kakao.com",
|
|
"profile_image_url": "https://k.kakaocdn.net/dn/.../profile.jpg",
|
|
"is_new_user": False
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# 내부 사용 스키마 (카카오 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
|