This commit is contained in:
암냥 2025-09-13 16:18:28 +09:00
commit 40266cc6e5
191 changed files with 5022 additions and 0 deletions

View file

106
Backend/schemas/avatar.py Normal file
View file

@ -0,0 +1,106 @@
from pydantic import BaseModel
import enum
class AvatarType(str, enum.Enum):
MALE = "남성"
FEMALE = "여성"
class TopClothesType(str, enum.Enum):
SCHOOL_CLOTHES = "교복"
SCHOOL_CLOTHES_2 = "교복 조끼"
ANA_CLOTHES = "AnA 동잠"
SUSPENDERS_CLOTHES_1 = "멜빵 바지"
SUSPENDERS_CLOTHES_2 = "멜빵 치마"
RAINBOW_CLOTHES = "무지개 맨투맨"
SANTA_CLOTHES = "산타"
class BottomClothesType(str, enum.Enum):
SCHOOL_CLOTHES = "교복 바지"
SCHOOL_CLOTHES_2 = "교복 치마"
SCHOOL_CLOTHES_3 = "교복 조끼 바지"
SCHOOL_CLOTHES_4 = "교복 조끼 치마"
SANTA_CLOTHES = "산타 바지"
JEANS = "청바지"
avatar_path_ = {"남성": "public/avatar/남자.png", "여성": "public/avatar/여자.png"}
top_clothe_path_ = {
"교복": "public/avatar/교복상의.png",
"교복 조끼": "public/avatar/교복조끼상의.png",
"AnA 동잠": "public/avatar/동잠상의.png",
"멜빵 바지": "public/avatar/멜빵바지상의.png",
"멜빵 치마": "public/avatar/멜빵치마상의.png",
"무지개 맨투맨": "public/avatar/무지개맨투맨상의.png",
"산타": "public/avatar/산타상의.png",
}
bottom_clothe_path_ = {
"교복 바지": "public/avatar/교복하의남.png",
"교복 치마": "public/avatar/교복하의여.png",
"교복 조끼 바지": "public/avatar/교복조끼하의남.png",
"교복 조끼 치마": "public/avatar/교복조끼하의여.png",
"산타 바지": "public/avatar/산타하의.png",
"청바지": "public/avatar/청바지하의.png",
}
class AvatarTypeResponse(BaseModel):
name: str
path: str
class AvatarUpdate(BaseModel):
avatar_type: AvatarType
top_clothe_type: TopClothesType
bottom_clothe_type: BottomClothesType
class AvatarResponse(BaseModel):
id: int
user_id: int
avatar_type: AvatarTypeResponse
top_clothe_type: AvatarTypeResponse
bottom_clothe_type: AvatarTypeResponse
class AvatarOptions(BaseModel):
avatar_types: list[str]
top_clothe_types: list[str]
bottom_clothe_types: list[str]
class Avatar:
def __init__(
self,
id: int,
user_id: int,
avatar_type: AvatarType,
top_clothe_type: TopClothesType,
bottom_clothe_type: BottomClothesType,
):
self.id = id
self.user_id = user_id
self.avatar_type = avatar_type
self.top_clothe_type = top_clothe_type
self.bottom_clothe_type = bottom_clothe_type
def to_response(self) -> AvatarResponse:
return AvatarResponse(
id=self.id,
user_id=self.user_id,
avatar_type=AvatarTypeResponse(
name=self.avatar_type,
path=avatar_path_[self.avatar_type],
),
top_clothe_type=AvatarTypeResponse(
name=self.top_clothe_type,
path=top_clothe_path_[self.top_clothe_type],
),
bottom_clothe_type=AvatarTypeResponse(
name=self.bottom_clothe_type,
path=bottom_clothe_path_[self.bottom_clothe_type],
),
)

109
Backend/schemas/diary.py Normal file
View file

@ -0,0 +1,109 @@
from pydantic import BaseModel, field_validator
from datetime import datetime
from typing import Optional, List
from fastapi import Form
class DiaryCreate(BaseModel):
title: str
content: str
category: str
@classmethod
def as_form(
cls,
title: str = Form(...),
content: str = Form(...),
category: str = Form(...),
) -> "DiaryCreate":
return cls(title=title, content=content, category=category)
@field_validator("title")
@classmethod
def validate_title(cls, v):
if len(v.strip()) < 1:
raise ValueError("Title cannot be empty")
if len(v) > 100:
raise ValueError("Title must be less than 100 characters")
return v.strip()
@field_validator("content")
@classmethod
def validate_content(cls, v):
if len(v.strip()) < 1:
raise ValueError("Content cannot be empty")
if len(v) > 5000:
raise ValueError("Content must be less than 5000 characters")
return v.strip()
class DiaryUpdate(BaseModel):
title: Optional[str] = None
content: Optional[str] = None
category: Optional[str] = None
@classmethod
def as_form(
cls,
title: Optional[str] = Form(None),
content: Optional[str] = Form(None),
category: Optional[str] = Form(None),
) -> "DiaryUpdate":
return cls(title=title, content=content, category=category)
class DiaryResponse(BaseModel):
id: int
user_id: int
title: str
content: str
images: List[str]
category: str
created_at: datetime
is_submitted: bool
email_sent: bool
class Diary:
def __init__(
self,
id: int,
user_id: int,
title: str,
content: str,
images: str, # JSON string
category: str,
created_at: datetime,
is_submitted: bool = False,
email_sent: bool = False,
):
self.id = id
self.user_id = user_id
self.title = title
self.content = content
self.images = images
self.category = category
self.created_at = created_at
self.is_submitted = is_submitted
self.email_sent = email_sent
@property
def image_list(self) -> List[str]:
return (
[img.strip() for img in self.images.split(",") if img.strip()]
if self.images
else []
)
def to_response(self) -> DiaryResponse:
return DiaryResponse(
id=self.id,
user_id=self.user_id,
title=self.title,
content=self.content,
images=self.image_list,
category=self.category,
created_at=self.created_at,
is_submitted=self.is_submitted,
email_sent=self.email_sent,
)

View file

@ -0,0 +1,59 @@
from pydantic import BaseModel, field_validator
from datetime import datetime
import enum
class FriendshipStatus(str, enum.Enum):
PENDING = "pending"
ACCEPTED = "accepted"
REJECTED = "rejected"
class FriendshipRequest(BaseModel):
friend_username: str
@field_validator("friend_username")
@classmethod
def validate_friend_username(cls, v):
if len(v.strip()) < 1:
raise ValueError("Friend username cannot be empty")
return v.strip()
class FriendshipResponse(BaseModel):
id: int
user_id: int
friend_id: int
friend_username: str
status: FriendshipStatus
created_at: datetime
class FriendshipUpdate(BaseModel):
status: FriendshipStatus
class Friendship:
def __init__(
self,
id: int,
user_id: int,
friend_id: int,
status: str,
created_at: datetime,
):
self.id = id
self.user_id = user_id
self.friend_id = friend_id
self.status = status
self.created_at = created_at
def to_response(self, friend_username: str) -> FriendshipResponse:
return FriendshipResponse(
id=self.id,
user_id=self.user_id,
friend_id=self.friend_id,
friend_username=friend_username,
status=FriendshipStatus(self.status),
created_at=self.created_at,
)

View file

@ -0,0 +1,46 @@
from datetime import datetime
from pydantic import BaseModel, field_validator
class GuestBookCreate(BaseModel):
content: str
target_user_id: int
@field_validator("content")
@classmethod
def validate_content(cls, v):
if len(v.strip()) < 1:
raise ValueError("GuestBook content cannot be empty")
if len(v) > 2000:
raise ValueError("GuestBook content must be less than 2000 characters")
return v.strip()
class GuestbookUpdate(BaseModel):
content: str
class GuestbookResponse(BaseModel):
id: int
content: str
target_user_id: int
user_id: int
user_profile_path: str
username: str
created_at: datetime
class GuestBook:
def __init__(
self,
id: int,
target_user_id: int,
user_id,
content,
created_at: datetime,
):
self.id = id
self.target_user_id = target_user_id
self.user_id = user_id
self.content = content
self.created_at = created_at

47
Backend/schemas/letter.py Normal file
View file

@ -0,0 +1,47 @@
# from pydantic import BaseModel, field_validator, EmailStr
#
# # email validator 삭제 및 EmailStr 사용
#
#
# class LetterCreate(BaseModel):
# content: str
#
# @field_validator("content")
# @classmethod
# def validate_content(cls, v):
# if len(v.strip()) < 1:
# raise ValueError("Letter content cannot be empty")
# if len(v) > 2000:
# raise ValueError("Letter content must be less than 2000 characters")
# return v.strip()
#
#
# class LetterResponse(BaseModel):
# id: int
# sender_id: int
# content: str
#
#
# class Letter:
# def __init__(
# self,
# id: int,
# sender_id: int,
# content: str,
# ):
# self.id = id
# self.sender_id = sender_id
# self.content = content
#
# def to_response(self) -> LetterResponse:
# return LetterResponse(
# id=self.id,
# sender_id=self.sender_id,
# content=self.content,
# )
#
#
# class EmailRequest(BaseModel):
# sender_email: EmailStr
# sender_password: str
# sender_name: str

117
Backend/schemas/photo.py Normal file
View file

@ -0,0 +1,117 @@
# from pydantic import BaseModel, field_validator
# from datetime import datetime
#
#
# class PhotoUpload(BaseModel):
# album_name: str
# title: str
#
# @field_validator("album_name")
# @classmethod
# def validate_album_name(cls, v):
# if len(v.strip()) < 1:
# raise ValueError("Album name cannot be empty")
# if len(v) > 50:
# raise ValueError("Album name must be less than 50 characters")
# return v.strip()
#
# @field_validator("title")
# @classmethod
# def validate_title(cls, v):
# if len(v.strip()) < 1:
# raise ValueError("Title cannot be empty")
# if len(v) > 100:
# raise ValueError("Title must be less than 100 characters")
# return v.strip()
#
#
# class PhotoResponse(BaseModel):
# id: int
# user_id: int
# album_name: str
# image_path: str
# title: str
# created_at: datetime
#
#
# class CommentCreate(BaseModel):
# content: str
#
# @field_validator("content")
# @classmethod
# def validate_content(cls, v):
# if len(v.strip()) < 1:
# raise ValueError("Comment cannot be empty")
# if len(v) > 500:
# raise ValueError("Comment must be less than 500 characters")
# return v.strip()
#
#
# class CommentResponse(BaseModel):
# id: int
# photo_id: int
# user_id: int
# username: str
# content: str
# created_at: datetime
#
#
# class FilterRequest(BaseModel):
# photo_id: int
# filter_type: str
# cover: bool
# title: str = None
#
#
# class Photo:
# def __init__(
# self,
# id: int,
# user_id: int,
# album_name: str,
# image_path: str,
# title: str,
# created_at: datetime,
# ):
# self.id = id
# self.user_id = user_id
# self.album_name = album_name
# self.image_path = image_path
# self.title = title
# self.created_at = created_at
#
# def to_response(self) -> PhotoResponse:
# return PhotoResponse(
# id=self.id,
# user_id=self.user_id,
# album_name=self.album_name,
# image_path=self.image_path,
# title=self.title,
# created_at=self.created_at,
# )
#
#
# class PhotoComment:
# def __init__(
# self,
# id: int,
# photo_id: int,
# user_id: int,
# content: str,
# created_at: datetime,
# ):
# self.id = id
# self.photo_id = photo_id
# self.user_id = user_id
# self.content = content
# self.created_at = created_at
#
# def to_response(self, username: str) -> CommentResponse:
# return CommentResponse(
# id=self.id,
# photo_id=self.photo_id,
# user_id=self.user_id,
# username=username,
# content=self.content,
# created_at=self.created_at,
# )

296
Backend/schemas/room.py Normal file
View file

@ -0,0 +1,296 @@
import enum
from pydantic import BaseModel, field_validator
from typing import Optional, List
class RoomNameUpdateRequest(BaseModel):
new_name: str
class RoomTypes(enum.Enum):
ROOM_1 = "room_1"
ROOM_2 = "room_2"
class UpdateRoomTypeRequest(BaseModel):
type: RoomTypes
class RoomTypeResponse(BaseModel):
type: str
image_path: str
class RoomResponse(BaseModel):
id: int
user_id: int
room_name: str
room_type: RoomTypes
room_image_path: str
room_path = {
"room_1": "public/room/room_1.png",
"room_2": "public/room/room_2.png",
}
class FurnitureItem(BaseModel):
name: str
image_path: str
width: int
class Furniture(str, enum.Enum):
BLACK_LAPTOP1_0 = "검정 노트북1-01"
BLACK_LAPTOP1_180 = "검정 노트북1-1801"
BLACK_LAPTOP1_270 = "검정 노트북1-2701"
BLACK_LAPTOP1_90 = "검정 노트북1-901"
BLACK_LAPTOP2_0 = "검정 노트북2-01"
BLACK_LAPTOP2_180 = "검정 노트북2-1801"
BLACK_LAPTOP2_270 = "검정 노트북2-2701"
BLACK_LAPTOP2_90 = "검정 노트북2-901"
BLACK_LAPTOP3_0 = "검정 노트북3-01"
BLACK_LAPTOP3_180 = "검정 노트북3-1801"
BLACK_LAPTOP3_270 = "검정 노트북3-2701"
BLACK_LAPTOP3_90 = "검정 노트북3-901"
WOODEN_TABLE_90 = "나무 탁자-901"
WOODEN_TABLE_0 = "나무 탁자-01"
LAPTOP1_0 = "노트북1-01"
LAPTOP1_180 = "노트북1-1801"
LAPTOP1_270 = "노트북1-2701"
LAPTOP1_90 = "노트북1-901"
LAPTOP2_0 = "노트북2-01"
LAPTOP2_180 = "노트북2-1801"
LAPTOP2_270 = "노트북2-2701"
LAPTOP2_90 = "노트북2-901"
LAPTOP3_0 = "노트북3-01"
LAPTOP3_180 = "노트북3-1801"
LAPTOP3_270 = "노트북3-2701"
LAPTOP3_90 = "노트북3-901"
GREEN_TABLE = "녹색 탁자1"
MINI_FRIDGE_0 = "미니 냉장고-01"
MINI_FRIDGE_180 = "미니 냉장고-1801"
MINI_FRIDGE_90 = "미니 냉장고-901"
BOX_0 = "박스-01"
BOX_90 = "박스-901"
PINK_TABLE = "분홍색 탁자1"
SHELF_0 = "선반-01"
SHELF_180 = "선반-1801"
SHELF_270 = "선반-2701"
SHELF_90 = "선반-901"
TRASH_CAN_CLOSED = "쓰레기통 닫힘1"
TRASH_CAN_OPEN = "쓰레기통 열림1"
FISHBOWL_0 = "어항-01"
FISHBOWL_180 = "어항-1801"
FISHBOWL_270 = "어항-2701"
FISHBOWL_90 = "어항-901"
BEVERAGE_FRIDGE_0 = "음료 냉장고-01"
BEVERAGE_FRIDGE_180 = "음료 냉장고-1801"
BEVERAGE_FRIDGE_270 = "음료 냉장고-2701"
BEVERAGE_FRIDGE_90 = "음료 냉장고-901"
CHAIR_0 = "의자-01"
CHAIR_180 = "의자-1801"
CHAIR_270 = "의자-2701"
CHAIR_90 = "의자-901"
SMALL_SHELF_0 = "작은 선반-01"
SMALL_SHELF_180 = "작은 선반-1801"
SMALL_SHELF_270 = "작은 선반-2701"
SMALL_SHELF_90 = "작은 선반-901"
SMALL_PLANT = "작은 식물1"
BOOKSHELF_0 = "책장-01"
BOOKSHELF_180 = "책장-1801"
BOOKSHELF_270 = "책장-2701"
BOOKSHELF_90 = "책장-901"
LARGE_PLANT = "큰 식물1"
TV_0 = "티비-01"
TV_180 = "티비-1801"
TV_270 = "티비-2701"
TV_90 = "티비-901"
BLUE_TABLE = "파란색 탁자1"
GRAY_TABLE = "회색 탁자1"
WHITE_LAPTOP1_0 = "흰 노트북1-01"
WHITE_LAPTOP1_180 = "흰 노트북1-1801"
WHITE_LAPTOP1_270 = "흰 노트북1-2701"
WHITE_LAPTOP1_90 = "흰 노트북1-901"
WHITE_LAPTOP2_0 = "흰 노트북2-01"
WHITE_LAPTOP2_180 = "흰 노트북2-1801"
WHITE_LAPTOP2_270 = "흰 노트북2-2701"
WHITE_LAPTOP2_90 = "흰 노트북2-901"
WHITE_LAPTOP3_0 = "흰 노트북3-01"
WHITE_LAPTOP3_180 = "흰 노트북3-1801"
WHITE_LAPTOP3_270 = "흰 노트북3-2701"
WHITE_LAPTOP3_90 = "흰 노트북3-901"
WHITE_SHELF_0 = "흰색 선반-01"
WHITE_SHELF_180 = "흰색 선반-1801"
WHITE_SHELF_270 = "흰색 선반-2701"
WHITE_SHELF_90 = "흰색 선반-901"
WHITE_SMALL_SHELF_0 = "흰색 작은 선반-01"
WHITE_SMALL_SHELF_180 = "흰색 작은 선반-1801"
WHITE_SMALL_SHELF_270 = "흰색 작은 선반-2701"
WHITE_SMALL_SHELF_90 = "흰색 작은 선반-901"
WHITE_TABLE = "흰색 탁자1"
EMPTY = "1"
class RoomFurniturePlacement(BaseModel):
id: int
room_id: int
furniture_name: Furniture
x: int
y: int
class FurniturePlacementRequest(BaseModel):
furniture_name: Furniture
x: int
y: int
@field_validator("x", "y")
@classmethod
def validate_coordinates(cls, v):
if v < 0 or v >= 10:
raise ValueError("position must be between 0 and 10")
return v
class FurniturePlacementResponse(BaseModel):
furniture_name: Furniture
x: int
y: int
image_path: str
class RoomFurnitureResponse(BaseModel):
room: RoomResponse
furniture: List[FurniturePlacementResponse]
furniture_path = {
"검정 노트북1-01": "public/funiture/검정 노트북1-0.png",
"검정 노트북1-1801": "public/funiture/검정 노트북1-180.png",
"검정 노트북1-2701": "public/funiture/검정 노트북1-270.png",
"검정 노트북1-901": "public/funiture/검정 노트북1-90.png",
"검정 노트북2-01": "public/funiture/검정 노트북2-0.png",
"검정 노트북2-1801": "public/funiture/검정 노트북2-180.png",
"검정 노트북2-2701": "public/funiture/검정 노트북2-270.png",
"검정 노트북2-901": "public/funiture/검정 노트북2-90.png",
"검정 노트북3-01": "public/funiture/검정 노트북3-0.png",
"검정 노트북3-1801": "public/funiture/검정 노트북3-180.png",
"검정 노트북3-2701": "public/funiture/검정 노트북3-270.png",
"검정 노트북3-901": "public/funiture/검정 노트북3-90.png",
"나무 탁자-901": "public/funiture/나무 탁자-90.png",
"나무 탁자-01": "public/funiture/나무탁자-0.png",
"노트북1-01": "public/funiture/노트북1-0.png",
"노트북1-1801": "public/funiture/노트북1-180.png",
"노트북1-2701": "public/funiture/노트북1-270.png",
"노트북1-901": "public/funiture/노트북1-90.png",
"노트북2-01": "public/funiture/노트북2-0.png",
"노트북2-1801": "public/funiture/노트북2-180.png",
"노트북2-2701": "public/funiture/노트북2-270.png",
"노트북2-901": "public/funiture/노트북2-90.png",
"노트북3-01": "public/funiture/노트북3-0.png",
"노트북3-1801": "public/funiture/노트북3-180.png",
"노트북3-2701": "public/funiture/노트북3-270.png",
"노트북3-901": "public/funiture/노트북3-90.png",
"녹색 침대-02": "public/funiture/녹색 침대-0.png",
"녹색 침대-1802": "public/funiture/녹색 침대-180.png",
"녹색 침대-2702": "public/funiture/녹색 침대-270.png",
"녹색 침대-902": "public/funiture/녹색 침대-90.png",
"녹색 탁자1": "public/funiture/녹색 탁자.png",
"미니 냉장고-01": "public/funiture/미니 냉장고-0.png",
"미니 냉장고-1801": "public/funiture/미니 냉장고-180.png",
"미니 냉장고-901": "public/funiture/미니 냉장고-90.png",
"박스-01": "public/funiture/박스-0.png",
"박스-901": "public/funiture/박스-90.png",
"분홍색 탁자1": "public/funiture/분홍색 탁자.png",
"빨간 침대-02": "public/funiture/빨간 침대-0.png",
"빨간 침대-1802": "public/funiture/빨간 침대-180.png",
"빨간 침대-2702": "public/funiture/빨간 침대-270.png",
"빨간 침대-902": "public/funiture/빨간 침대-90.png",
"선반-01": "public/funiture/선반-0.png",
"선반-1801": "public/funiture/선반-180.png",
"선반-2701": "public/funiture/선반-270.png",
"선반-901": "public/funiture/선반-90.png",
"소파-02": "public/funiture/소파-0.png",
"소파-1802": "public/funiture/소파-180.png",
"소파-2702": "public/funiture/소파-270.png",
"소파-902": "public/funiture/소파-90.png",
"쓰레기통 닫힘1": "public/funiture/쓰레기통닫힘.png",
"쓰레기통 열림1": "public/funiture/쓰레기통열림.png",
"어항-01": "public/funiture/어항-0.png",
"어항-1801": "public/funiture/어항-180.png",
"어항-2701": "public/funiture/어항-270.png",
"어항-901": "public/funiture/어항-90.png",
"음료 냉장고-01": "public/funiture/음료 냉장고-0.png",
"음료 냉장고-1801": "public/funiture/음료 냉장고-180.png",
"음료 냉장고-2701": "public/funiture/음료 냉장고-270.png",
"음료 냉장고-901": "public/funiture/음료 냉장고-90.png",
"의자-01": "public/funiture/의자-0.png",
"의자-1801": "public/funiture/의자-180.png",
"의자-2701": "public/funiture/의자-270.png",
"의자-901": "public/funiture/의자-90.png",
"작은 선반-01": "public/funiture/작은 선반-0.png",
"작은 선반-1801": "public/funiture/작은 선반-180.png",
"작은 선반-2701": "public/funiture/작은 선반-270.png",
"작은 선반-901": "public/funiture/작은 선반-90.png",
"작은 식물1": "public/funiture/작은 식물.png",
"주황 침대-02": "public/funiture/주황 침대-0.png",
"주황 침대-1802": "public/funiture/주황 침대-180.png",
"주황 침대-2702": "public/funiture/주황 침대-270.png",
"주황 침대-902": "public/funiture/주황 침대-90.png",
"책장-01": "public/funiture/책장-0.png",
"책장-1801": "public/funiture/책장-180.png",
"책장-2701": "public/funiture/책장-270.png",
"책장-901": "public/funiture/책장-90.png",
"큰 식물1": "public/funiture/큰 식물.png",
"티비-01": "public/funiture/티비-0.png",
"티비-1801": "public/funiture/티비-180.png",
"티비-2701": "public/funiture/티비-270.png",
"티비-901": "public/funiture/티비-90.png",
"파란 침대-02": "public/funiture/파란 침대-0.png",
"파란 침대-1802": "public/funiture/파란 침대-180.png",
"파란 침대-2702": "public/funiture/파란 침대-270.png",
"파란 침대-902": "public/funiture/파란 침대-90.png",
"파란색 탁자1": "public/funiture/파란색 탁자.png",
"회색 탁자1": "public/funiture/회색 탁자.png",
"흰 노트북1-01": "public/funiture/흰 노트북1-0.png",
"흰 노트북1-1801": "public/funiture/흰 노트북1-180.png",
"흰 노트북1-2701": "public/funiture/흰 노트북1-270.png",
"흰 노트북1-901": "public/funiture/흰 노트북1-90.png",
"흰 노트북2-01": "public/funiture/흰 노트북2-0.png",
"흰 노트북2-1801": "public/funiture/흰 노트북2-180.png",
"흰 노트북2-2701": "public/funiture/흰 노트북2-270.png",
"흰 노트북2-901": "public/funiture/흰 노트북2-90.png",
"흰 노트북3-01": "public/funiture/흰 노트북3-0.png",
"흰 노트북3-1801": "public/funiture/흰 노트북3-180.png",
"흰 노트북3-2701": "public/funiture/흰 노트북3-270.png",
"흰 노트북3-901": "public/funiture/흰 노트북3-90.png",
"흰색 선반-01": "public/funiture/흰색 선반-0.png",
"흰색 선반-1801": "public/funiture/흰색 선반-180.png",
"흰색 선반-2701": "public/funiture/흰색 선반-270.png",
"흰색 선반-901": "public/funiture/흰색 선반-90.png",
"흰색 작은 선반-01": "public/funiture/흰색 작은 선반-0.png",
"흰색 작은 선반-1801": "public/funiture/흰색 작은 선반-180.png",
"흰색 작은 선반-2701": "public/funiture/흰색 작은 선반-270.png",
"흰색 작은 선반-901": "public/funiture/흰색 작은 선반-90.png",
"흰색 탁자1": "public/funiture/흰색 탁자.png",
"1": "1",
}
class Room:
def __init__(self, id: int, user_id: int, room_name: str, room_type: RoomTypes):
self.id = id
self.user_id = user_id
self.room_name = room_name
self.room_type = room_type
def to_response(self) -> RoomResponse:
return RoomResponse(
id=self.id,
user_id=self.user_id,
room_name=self.room_name,
room_type=self.room_type,
room_image_path=room_path[self.room_type],
)

93
Backend/schemas/user.py Normal file
View file

@ -0,0 +1,93 @@
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Optional
import hashlib
import secrets
from fastapi import Form
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
@classmethod
def as_form(
cls,
username: str = Form(...),
email: EmailStr = Form(...),
password: str = Form(...),
) -> "UserCreate":
return cls(username=username, email=email, password=password)
class UserUpdate(BaseModel):
email: Optional[EmailStr] = None
password: Optional[str] = None
@classmethod
def as_form(
cls,
email: Optional[EmailStr] = Form(default=None),
password: Optional[str] = Form(default=None),
) -> "UserUpdate":
return cls(email=email, password=password)
class UserLogin(BaseModel):
username: str
password: str
class UserResponse(BaseModel):
id: int
username: str
email: str
created_at: datetime
profile_image_path: str
is_active: bool
class User:
def __init__(
self,
id: int,
username: str,
email: str,
password_hash: str,
salt: str,
created_at: datetime,
profile_image_path: str,
is_active: bool = True,
):
self.id = id
self.username = username
self.email = email
self.password_hash = password_hash
self.salt = salt
self.created_at = created_at
self.profile_image_path = profile_image_path
self.is_active = is_active
@staticmethod
def hash_password(password: str, salt: Optional[str] = None) -> tuple[str, str]:
if salt is None:
salt = secrets.token_hex(32)
password_hash = hashlib.pbkdf2_hmac(
"sha256", password.encode("utf-8"), salt.encode("utf-8"), 100000
)
return password_hash.hex(), salt
def verify_password(self, password: str) -> bool:
password_hash, _ = self.hash_password(password, self.salt)
return password_hash == self.password_hash
def to_response(self) -> UserResponse:
return UserResponse(
id=self.id,
username=self.username,
email=self.email,
created_at=self.created_at,
profile_image_path=self.profile_image_path,
is_active=self.is_active,
)