a
This commit is contained in:
commit
40266cc6e5
54 changed files with 5022 additions and 0 deletions
12
Backend/__init__.py
Normal file
12
Backend/__init__.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from .router.router import router
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from Backend.utils.run_server import init_FastAPI
|
||||
|
||||
# 프로젝트 루트를 Python 경로에 추가
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
app = init_FastAPI()
|
||||
|
||||
app.include_router(router)
|
||||
78
Backend/core/security.py
Normal file
78
Backend/core/security.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
from jose import jwt, JWTError
|
||||
from datetime import datetime, timedelta
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from typing import Optional
|
||||
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
|
||||
SECRET_KEY = "WIXYAhAfU6tLOloxqHgI4thAAo6kshkK"
|
||||
ALGORITHM = "HS256"
|
||||
|
||||
security = HTTPBearer()
|
||||
|
||||
|
||||
def create_access_token(data: dict):
|
||||
to_encode = data.copy()
|
||||
expire = datetime.now() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
def verify_token(token: str) -> Optional[dict]:
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
return None
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
):
|
||||
from ..schemas.user import User
|
||||
from ..utils.db import fetch_one
|
||||
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
try:
|
||||
payload = verify_token(credentials.credentials)
|
||||
if payload is None:
|
||||
raise credentials_exception
|
||||
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
# 데이터베이스에서 사용자 조회
|
||||
user_row = await fetch_one("SELECT * FROM users WHERE username = ?", (username,))
|
||||
|
||||
if user_row is None:
|
||||
raise credentials_exception
|
||||
|
||||
user = User(
|
||||
id=user_row["id"],
|
||||
username=user_row["username"],
|
||||
email=user_row["email"],
|
||||
password_hash=user_row["password_hash"],
|
||||
salt=user_row["salt"],
|
||||
created_at=user_row["created_at"],
|
||||
profile_image_path=user_row["profile_image_path"],
|
||||
is_active=user_row["is_active"],
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_active_user(current_user=Depends(get_current_user)):
|
||||
if not current_user.is_active:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
return current_user
|
||||
49
Backend/router/endpoints/avatar.py
Normal file
49
Backend/router/endpoints/avatar.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from ...schemas.avatar import (
|
||||
AvatarUpdate,
|
||||
AvatarResponse,
|
||||
AvatarOptions,
|
||||
)
|
||||
from ...services.avatar_service import AvatarService
|
||||
from ...core.security import get_current_user
|
||||
from ...schemas.user import User
|
||||
|
||||
router = APIRouter(prefix="/avatar", tags=["avatar"])
|
||||
avatar_service = AvatarService()
|
||||
|
||||
|
||||
@router.get("", response_model=AvatarResponse)
|
||||
async def get_my_avatar(
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> AvatarResponse:
|
||||
try:
|
||||
avatar = await avatar_service.get_or_create_avatar(current_user.id)
|
||||
return avatar.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("", response_model=AvatarResponse)
|
||||
async def update_avatar(
|
||||
avatar_data: AvatarUpdate, current_user: User = Depends(get_current_user)
|
||||
) -> AvatarResponse:
|
||||
try:
|
||||
avatar = await avatar_service.update_avatar(current_user.id, avatar_data)
|
||||
return avatar.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/options", response_model=AvatarOptions)
|
||||
async def get_avatar_options() -> AvatarOptions:
|
||||
try:
|
||||
return await avatar_service.get_avatar_options()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.get("/{user_id}", response_model=AvatarResponse)
|
||||
async def get_avatar_by_userId(user_id: int) -> AvatarResponse:
|
||||
avatar = await avatar_service.get_avatar_by_userId(user_id)
|
||||
if not avatar:
|
||||
raise HTTPException(status_code=404, detail="Avatar not found")
|
||||
return avatar.to_response()
|
||||
102
Backend/router/endpoints/diary.py
Normal file
102
Backend/router/endpoints/diary.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
from fastapi import (
|
||||
APIRouter,
|
||||
HTTPException,
|
||||
Depends,
|
||||
UploadFile,
|
||||
File,
|
||||
)
|
||||
from typing import List, Optional
|
||||
from ...schemas.diary import DiaryCreate, DiaryUpdate, DiaryResponse, Diary
|
||||
from ...services.diary_service import DiaryService
|
||||
from ...core.security import get_current_user
|
||||
from ...schemas.user import User
|
||||
|
||||
router = APIRouter(prefix="/diary", tags=["diary"])
|
||||
diary_service = DiaryService()
|
||||
|
||||
|
||||
@router.post("", response_model=DiaryResponse)
|
||||
async def create_diary(
|
||||
diary_data: DiaryCreate = Depends(DiaryCreate.as_form),
|
||||
file: List[UploadFile] = File(default=None),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> DiaryResponse:
|
||||
try:
|
||||
diary = await diary_service.create_diary(current_user.id, diary_data, file)
|
||||
return diary.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("", response_model=List[DiaryResponse])
|
||||
async def get_user_diaries(
|
||||
skip: int = 0,
|
||||
limit: int = 20,
|
||||
category: Optional[str] = None,
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> List[DiaryResponse]:
|
||||
try:
|
||||
diaries = await diary_service.get_user_diaries(
|
||||
current_user.id, skip, limit, category
|
||||
)
|
||||
return [diary.to_response() for diary in diaries]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/w/{target_user_id}", response_model=List[DiaryResponse])
|
||||
async def get_target_user_diaries(
|
||||
target_user_id: int,
|
||||
skip: int = 0,
|
||||
limit: int = 20,
|
||||
category: Optional[str] = None,
|
||||
) -> List[DiaryResponse]:
|
||||
try:
|
||||
diaries = await diary_service.get_user_diaries(
|
||||
target_user_id, skip, limit, category
|
||||
)
|
||||
return [diary.to_response() for diary in diaries]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{diary_id}", response_model=DiaryResponse)
|
||||
async def get_diary(diary_id: int) -> DiaryResponse:
|
||||
try:
|
||||
diary = await diary_service.get_diary_by_id(diary_id)
|
||||
if not diary:
|
||||
raise HTTPException(status_code=404, detail="Diary not found")
|
||||
return diary.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/{diary_id}", response_model=DiaryResponse)
|
||||
async def update_diary(
|
||||
diary_id: int,
|
||||
diary_data: DiaryUpdate = Depends(DiaryUpdate.as_form),
|
||||
file: List[UploadFile] = File(default=None),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> DiaryResponse:
|
||||
try:
|
||||
diary = await diary_service.update_diary(
|
||||
diary_id, current_user.id, diary_data, file
|
||||
)
|
||||
if not diary:
|
||||
raise HTTPException(status_code=404, detail="Diary not found")
|
||||
return diary.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{diary_id}")
|
||||
async def delete_diary(
|
||||
diary_id: int, current_user: User = Depends(get_current_user)
|
||||
) -> dict:
|
||||
try:
|
||||
success = await diary_service.delete_diary(diary_id, current_user.id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Diary not found")
|
||||
return {"message": "Diary deleted successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
81
Backend/router/endpoints/friendship.py
Normal file
81
Backend/router/endpoints/friendship.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from typing import List, Optional
|
||||
from ...schemas.friendship import (
|
||||
FriendshipRequest,
|
||||
FriendshipResponse,
|
||||
)
|
||||
from ...services.friendship_service import FriendshipService
|
||||
from ...core.security import get_current_user
|
||||
from ...schemas.user import User
|
||||
|
||||
router = APIRouter(prefix="/friendship", tags=["friendship"])
|
||||
friendship_service = FriendshipService()
|
||||
|
||||
|
||||
@router.post("/request", response_model=FriendshipResponse)
|
||||
async def send_friendship_request(
|
||||
request_data: FriendshipRequest, current_user: User = Depends(get_current_user)
|
||||
) -> FriendshipResponse:
|
||||
try:
|
||||
friendship = await friendship_service.send_friendship_request(
|
||||
current_user.id, request_data.friend_username
|
||||
)
|
||||
return friendship
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/{friendship_id}/accept", response_model=FriendshipResponse)
|
||||
async def accept_friendship_request(
|
||||
friendship_id: int, current_user: User = Depends(get_current_user)
|
||||
) -> FriendshipResponse:
|
||||
try:
|
||||
friendship = await friendship_service.accept_friendship_request(
|
||||
friendship_id, current_user.id
|
||||
)
|
||||
if not friendship:
|
||||
raise HTTPException(status_code=404, detail="Friendship request not found")
|
||||
return friendship
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("", response_model=List[FriendshipResponse])
|
||||
async def get_friendships(
|
||||
status: Optional[str] = None, current_user: User = Depends(get_current_user)
|
||||
) -> List[FriendshipResponse]:
|
||||
try:
|
||||
friendships = await friendship_service.get_user_friendships(
|
||||
current_user.id, status
|
||||
)
|
||||
return friendships
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{friendship_id}")
|
||||
async def delete_friendship(
|
||||
friendship_id: int, current_user: User = Depends(get_current_user)
|
||||
) -> dict:
|
||||
try:
|
||||
success = await friendship_service.delete_friendship(
|
||||
friendship_id, current_user.id
|
||||
)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Friendship not found")
|
||||
return {"message": "Friendship deleted successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/pending", response_model=List[FriendshipResponse])
|
||||
async def get_pending_requests(
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> List[FriendshipResponse]:
|
||||
try:
|
||||
pending_requests = await friendship_service.get_pending_requests(
|
||||
current_user.id
|
||||
)
|
||||
return pending_requests
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
63
Backend/router/endpoints/guestbook.py
Normal file
63
Backend/router/endpoints/guestbook.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from typing import List
|
||||
from fastapi import APIRouter, HTTPException, Depends, status
|
||||
from Backend.core.security import get_current_user
|
||||
from Backend.schemas.guestbook import (
|
||||
GuestBookCreate,
|
||||
GuestbookUpdate,
|
||||
GuestbookResponse,
|
||||
)
|
||||
from Backend.schemas.user import User
|
||||
from Backend.services.guestbook_service import GuestbookService
|
||||
|
||||
router = APIRouter(prefix="/guestbook", tags=["guestbook"])
|
||||
|
||||
guestbook_service = GuestbookService()
|
||||
|
||||
|
||||
@router.post("", status_code=status.HTTP_201_CREATED, response_model=GuestbookResponse)
|
||||
async def create_guestbook(
|
||||
guestbook: GuestBookCreate, current_user: User = Depends(get_current_user)
|
||||
) -> GuestbookResponse:
|
||||
try:
|
||||
response = await guestbook_service.create_guestbook(guestbook, current_user)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{target_user_id}", response_model=List[GuestbookResponse])
|
||||
async def get_guestbook(target_user_id: int):
|
||||
try:
|
||||
response = await guestbook_service.get_target_user_guestbooks(target_user_id)
|
||||
response.reverse()
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/{id}", response_model=GuestbookResponse)
|
||||
async def update_guestbook(
|
||||
id: int,
|
||||
guestbook: GuestbookUpdate,
|
||||
) -> GuestbookResponse:
|
||||
try:
|
||||
response = await guestbook_service.update_guestbook_by_id(id, guestbook.content)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_guestbook(
|
||||
id: int,
|
||||
user: User = Depends(get_current_user),
|
||||
) -> dict:
|
||||
try:
|
||||
is_success = await guestbook_service.delete_guestbook_by_id(id, user.id)
|
||||
if is_success:
|
||||
return {"detail": "success"}
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail="guestbook not found")
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
79
Backend/router/endpoints/letter.py
Normal file
79
Backend/router/endpoints/letter.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# from fastapi import APIRouter, HTTPException, Depends
|
||||
#
|
||||
# from ...schemas.letter import LetterCreate, LetterResponse, EmailRequest
|
||||
# from ...services.letter_service import LetterService
|
||||
# from ...core.security import get_current_user
|
||||
# from ...schemas.user import User
|
||||
#
|
||||
# router = APIRouter(prefix="/letter", tags=["letter"])
|
||||
# letter_service = LetterService()
|
||||
#
|
||||
#
|
||||
# @router.post("", response_model=LetterResponse)
|
||||
# async def create_letter(
|
||||
# letter_data: LetterCreate,
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ) -> LetterResponse:
|
||||
# try:
|
||||
# letter = await letter_service.create_letter(current_user.id, letter_data)
|
||||
# return letter.to_response()
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.get("/{letter_id}", response_model=LetterResponse)
|
||||
# async def get_letter(
|
||||
# letter_id: int, current_user: User = Depends(get_current_user)
|
||||
# ) -> LetterResponse:
|
||||
# try:
|
||||
# letter = await letter_service.get_letter_by_id(letter_id, current_user.id)
|
||||
# if not letter:
|
||||
# raise HTTPException(status_code=404, detail="Letter not found")
|
||||
# return letter.to_response()
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.delete("/{letter_id}")
|
||||
# async def delete_letter(letter_id: int, current_user: User = Depends(get_current_user)):
|
||||
# try:
|
||||
# is_success = await letter_service.delete_letter(letter_id, current_user.id)
|
||||
# if not is_success:
|
||||
# raise HTTPException(status_code=404, detail="Letter not found")
|
||||
# return {"detail": "Letter deleted"}
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.put("/{letter_id}", response_model=LetterResponse)
|
||||
# async def update_letter(
|
||||
# letter_id: int,
|
||||
# letter_data: LetterCreate,
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ):
|
||||
# try:
|
||||
# letter = await letter_service.update_letter(
|
||||
# letter_id, current_user.id, letter_data.content
|
||||
# )
|
||||
# if not letter:
|
||||
# raise HTTPException(status_code=404, detail="Letter not found")
|
||||
# return letter.to_response()
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.post("/{letter_id}/send")
|
||||
# async def send_letter(
|
||||
# letter_id: int,
|
||||
# letter_data: EmailRequest,
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ):
|
||||
# try:
|
||||
# letter = await letter_service.get_letter_by_id(letter_id, current_user.id)
|
||||
# if not letter:
|
||||
# raise HTTPException(status_code=404, detail="Letter not found")
|
||||
#
|
||||
# await letter_service.send_letter(letter, letter_data)
|
||||
# return {"message": "Email sent successfully!"}
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
117
Backend/router/endpoints/photo.py
Normal file
117
Backend/router/endpoints/photo.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# from fastapi import APIRouter, HTTPException, Depends, UploadFile, File, Query, Form
|
||||
# from typing import List
|
||||
# from ...schemas.photo import (
|
||||
# PhotoUpload,
|
||||
# PhotoResponse,
|
||||
# CommentCreate,
|
||||
# CommentResponse,
|
||||
# FilterRequest,
|
||||
# )
|
||||
# from ...services.photo_service import PhotoService
|
||||
# from ...core.security import get_current_user
|
||||
# from ...schemas.user import User
|
||||
# from pydantic import ValidationError
|
||||
#
|
||||
# router = APIRouter(prefix="/photo", tags=["photo"])
|
||||
# photo_service = PhotoService()
|
||||
#
|
||||
#
|
||||
# @router.post("/upload", response_model=PhotoResponse)
|
||||
# async def upload_photo(
|
||||
# photo_data: UploadFile = Form(...),
|
||||
# file: UploadFile = File(...),
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ) -> PhotoResponse:
|
||||
# import json
|
||||
#
|
||||
# try:
|
||||
# photo_data_bytes = await photo_data.read()
|
||||
# photo_info = json.loads(photo_data_bytes.decode("utf-8"))
|
||||
# photo_data = PhotoUpload(**photo_info)
|
||||
# except (json.JSONDecodeError, ValidationError) as e:
|
||||
# raise HTTPException(status_code=400, detail="Invalid photo data format")
|
||||
# try:
|
||||
# photo = await photo_service.upload_photo(current_user.id, photo_data, file)
|
||||
# return photo.to_response()
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.get("", response_model=List[PhotoResponse])
|
||||
# async def get_user_photos(
|
||||
# skip: int = 0,
|
||||
# limit: int = 20,
|
||||
# album_name: str = None,
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ) -> List[PhotoResponse]:
|
||||
# try:
|
||||
# photos = await photo_service.get_user_photos(
|
||||
# current_user.id, skip, limit, album_name
|
||||
# )
|
||||
# return [photo.to_response() for photo in photos]
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.post("/{photo_id}/comment", response_model=CommentResponse)
|
||||
# async def add_photo_comment(
|
||||
# photo_id: int,
|
||||
# comment_data: CommentCreate,
|
||||
# current_user: User = Depends(get_current_user),
|
||||
# ) -> CommentResponse:
|
||||
# try:
|
||||
# is_friend = await photo_service.check_friendship(current_user.id, photo_id)
|
||||
# if not is_friend:
|
||||
# raise HTTPException(
|
||||
# status_code=403, detail="Only friends can comment on photos"
|
||||
# )
|
||||
#
|
||||
# comment = await photo_service.add_comment(
|
||||
# photo_id, current_user.id, comment_data
|
||||
# )
|
||||
# return comment.to_response(current_user.username)
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.get("/{photo_id}/comments", response_model=List[CommentResponse])
|
||||
# async def get_photo_comments(
|
||||
# photo_id: int, current_user: User = Depends(get_current_user)
|
||||
# ) -> List[CommentResponse]:
|
||||
# try:
|
||||
# comments = await photo_service.get_photo_comments(photo_id)
|
||||
# return comments
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.post("/edit-filter")
|
||||
# async def apply_photo_filter(
|
||||
# filter_request: FilterRequest, current_user: User = Depends(get_current_user)
|
||||
# ) -> dict:
|
||||
# if filter_request.cover and filter_request.title is None:
|
||||
# raise HTTPException(status_code=400, detail="title must be Not Null")
|
||||
# try:
|
||||
# filtered_image_path = await photo_service.apply_filter(
|
||||
# filter_request.photo_id,
|
||||
# filter_request.filter_type,
|
||||
# current_user.id,
|
||||
# filter_request.cover,
|
||||
# filter_request.title,
|
||||
# )
|
||||
# return {"filtered_image_path": filtered_image_path}
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
#
|
||||
#
|
||||
# @router.delete("/{photo_id}")
|
||||
# async def delete_photo(
|
||||
# photo_id: int, current_user: User = Depends(get_current_user)
|
||||
# ) -> dict:
|
||||
# try:
|
||||
# success = await photo_service.delete_photo(photo_id, current_user.id)
|
||||
# if not success:
|
||||
# raise HTTPException(status_code=404, detail="Photo not found")
|
||||
# return {"message": "Photo deleted successfully"}
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=400, detail=str(e))
|
||||
110
Backend/router/endpoints/room.py
Normal file
110
Backend/router/endpoints/room.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import List
|
||||
from Backend.schemas.room import (
|
||||
FurnitureItem,
|
||||
RoomFurniturePlacement,
|
||||
FurniturePlacementRequest,
|
||||
RoomNameUpdateRequest,
|
||||
UpdateRoomTypeRequest,
|
||||
RoomResponse,
|
||||
RoomTypeResponse,
|
||||
RoomFurnitureResponse,
|
||||
)
|
||||
from Backend.schemas.user import User
|
||||
from Backend.core.security import get_current_user
|
||||
from Backend.services.room_service import RoomService
|
||||
|
||||
router = APIRouter(prefix="/room", tags=["room"])
|
||||
room_service = RoomService()
|
||||
|
||||
|
||||
@router.get("/catalog", response_model=List[FurnitureItem])
|
||||
async def get_furniture_catalog() -> List[FurnitureItem]:
|
||||
return await room_service.get_furniture_catalog()
|
||||
|
||||
|
||||
@router.get("/layout", response_model=RoomFurnitureResponse)
|
||||
async def get_my_room_layout(current_user: User = Depends(get_current_user)):
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
return await room_service.get_room_furnitures(room_id)
|
||||
|
||||
|
||||
@router.get("/layout/{user_id}", response_model=RoomFurnitureResponse)
|
||||
async def get_user_room_layout(user_id: int) -> RoomFurnitureResponse:
|
||||
room_id = await room_service.get_or_create_room(user_id)
|
||||
return await room_service.get_room_furnitures(room_id)
|
||||
|
||||
|
||||
@router.post("/furniture")
|
||||
async def place_furniture(
|
||||
request: FurniturePlacementRequest, current_user: User = Depends(get_current_user)
|
||||
):
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
try:
|
||||
await room_service.place_furniture(room_id, request)
|
||||
return {"message": "Furniture placed successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/furniture")
|
||||
async def remove_furniture(
|
||||
x: int, y: int, furniture_name: str, current_user: User = Depends(get_current_user)
|
||||
):
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
await room_service.remove_furniture(room_id, x, y, furniture_name)
|
||||
return {"message": "Furniture removed successfully"}
|
||||
|
||||
|
||||
@router.put("/")
|
||||
async def update_room_name(
|
||||
data: RoomNameUpdateRequest, current_user: User = Depends(get_current_user)
|
||||
):
|
||||
try:
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
await room_service.update_room_name(room_id, data.new_name)
|
||||
return {"message": "Room name updated successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_my_room(current_user: User = Depends(get_current_user)) -> RoomResponse:
|
||||
try:
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
return (await room_service.get_room_by_id(room_id)).to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/types")
|
||||
async def get_room_types() -> List[RoomTypeResponse]:
|
||||
return await room_service.get_room_types()
|
||||
|
||||
|
||||
@router.patch("/")
|
||||
async def update_room_type(
|
||||
data: UpdateRoomTypeRequest, current_user: User = Depends(get_current_user)
|
||||
) -> RoomResponse:
|
||||
try:
|
||||
room_id = await room_service.get_or_create_room(current_user.id)
|
||||
return (await room_service.update_room_type(room_id, data.type)).to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/my")
|
||||
async def get_my_furniture(current_user: User = Depends(get_current_user)):
|
||||
try:
|
||||
list_ = await room_service.get_user_furniture(current_user.id)
|
||||
return list_
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.get("/{user_id}", response_model=RoomResponse)
|
||||
async def get_room_by_userId(user_id: int) -> RoomResponse:
|
||||
try:
|
||||
room = await room_service.get_room_by_userId(user_id)
|
||||
return room.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
48
Backend/router/endpoints/store.py
Normal file
48
Backend/router/endpoints/store.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from ...core.security import get_current_user
|
||||
from ...schemas.user import User
|
||||
from Backend.services.store_service import StoreService
|
||||
from ...services.room_service import RoomService
|
||||
|
||||
router = APIRouter(prefix="/store", tags=["store"])
|
||||
store_service = StoreService()
|
||||
room_service = RoomService()
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_dotory(
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
try:
|
||||
dotory = await store_service.get_dotory_by_id(current_user.id)
|
||||
return {"dotory": dotory}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/{product_id}")
|
||||
async def buy_product(
|
||||
product_id: int,
|
||||
product_name: str,
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
try:
|
||||
response = await store_service.buy_product(product_id, current_user.id)
|
||||
if response["isSuccess"]:
|
||||
await room_service.add_furniture(current_user.id, product_name)
|
||||
return {"dotory": response["dotory"]}
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.put("")
|
||||
async def update_dotory(
|
||||
dotory_num: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
try:
|
||||
dotory = await store_service.update_user_dotory(current_user.id, dotory_num)
|
||||
return {"dotory": dotory}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
116
Backend/router/endpoints/user.py
Normal file
116
Backend/router/endpoints/user.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status, UploadFile, File, Depends
|
||||
from Backend.schemas.user import UserCreate, UserLogin, UserResponse, User, UserUpdate
|
||||
from Backend.services.user_service import UserService
|
||||
from Backend.core.security import create_access_token, get_current_user
|
||||
|
||||
router = APIRouter(prefix="/user", tags=["user"])
|
||||
|
||||
user_service = UserService()
|
||||
|
||||
|
||||
@router.post(
|
||||
"/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
async def register_user(
|
||||
user_data: UserCreate = Depends(UserCreate.as_form),
|
||||
profile_file: UploadFile = File(default=None),
|
||||
) -> UserResponse:
|
||||
existing_user = await user_service.get_user_by_username
|
||||
if existing_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Username already registered"
|
||||
)
|
||||
|
||||
existing_email = await user_service.get_user_by_email(user_data.email)
|
||||
if existing_email:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Email already registered"
|
||||
)
|
||||
|
||||
try:
|
||||
user = await user_service.create_user(user_data, profile_file)
|
||||
return user.to_response()
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to created user: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/login")
|
||||
async def login_user(login_data: UserLogin) -> dict:
|
||||
user = await user_service.authenticate_user(
|
||||
login_data.username, login_data.password
|
||||
)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=
|
||||
detail=
|
||||
)
|
||||
|
||||
if not user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=, detail=""
|
||||
)
|
||||
|
||||
access_token =
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
|
||||
@router.get("/profile/{username}", response_model=UserResponse)
|
||||
async def get_user_profile(username: str) -> UserResponse:
|
||||
user = await user_service.get_user_by_username(username)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
|
||||
)
|
||||
|
||||
return user.to_response()
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserResponse)
|
||||
async def get_user_me(user: User = Depends(get_current_user)) -> UserResponse:
|
||||
return user.to_response()
|
||||
|
||||
|
||||
@router.delete("/{username}")
|
||||
async def delete_user(username: str) -> dict:
|
||||
is_success = await user_service.delete_user(username)
|
||||
if not is_success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
|
||||
)
|
||||
return {"detail": "User deleted"}
|
||||
|
||||
|
||||
@router.get("/find/{username}", response_model=List[UserResponse])
|
||||
async def find_user(username: str) -> List[UserResponse]:
|
||||
users = await user_service.find_user(username)
|
||||
return users
|
||||
|
||||
|
||||
@router.put("/", response_model=UserResponse)
|
||||
async def update_user(
|
||||
current_user: User = Depends(get_current_user),
|
||||
user_data: UserUpdate = Depends(UserUpdate.as_form),
|
||||
profile_file: UploadFile = File(default=None),
|
||||
) -> UserResponse:
|
||||
try:
|
||||
updated_user = await user_service.update_user(
|
||||
user=current_user, user_data=user_data, profile_file=profile_file
|
||||
)
|
||||
return updated_user.to_response()
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to update user: {str(e)}",
|
||||
)
|
||||
21
Backend/router/router.py
Normal file
21
Backend/router/router.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import os
|
||||
import traceback
|
||||
from fastapi import APIRouter
|
||||
from fastapi.logger import logger
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
for router_file in os.listdir("Backend/router/endpoints"):
|
||||
try:
|
||||
if router_file.endswith(".py"):
|
||||
module_name = "Backend.router.endpoints." + router_file[:-3]
|
||||
module = __import__(module_name, fromlist=[""])
|
||||
router_object = getattr(module, "router")
|
||||
prefix = getattr(module, "prefix", router_file[:-3])
|
||||
|
||||
router.include_router(router_object, prefix="/api", tags=[router_file[:-3]])
|
||||
print(f"Loaded router: /api/{prefix} - {router_file}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading router {router_file}:\n{traceback.format_exc()}")
|
||||
0
Backend/schemas/__init__.py
Normal file
0
Backend/schemas/__init__.py
Normal file
106
Backend/schemas/avatar.py
Normal file
106
Backend/schemas/avatar.py
Normal 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
109
Backend/schemas/diary.py
Normal 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,
|
||||
)
|
||||
59
Backend/schemas/friendship.py
Normal file
59
Backend/schemas/friendship.py
Normal 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,
|
||||
)
|
||||
46
Backend/schemas/guestbook.py
Normal file
46
Backend/schemas/guestbook.py
Normal 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
47
Backend/schemas/letter.py
Normal 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
117
Backend/schemas/photo.py
Normal 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
296
Backend/schemas/room.py
Normal 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],
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue