Убрал лишнее
This commit is contained in:
parent
e87109dde5
commit
d1e8e80be6
10 changed files with 269 additions and 128 deletions
|
@ -16,13 +16,21 @@ from app.users.schemas import SCreateInvitationLink, SUserAddedToChat
|
|||
router = APIRouter(prefix="/chat", tags=["Чат"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[SChat])
|
||||
@router.get(
|
||||
"",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SChat]
|
||||
)
|
||||
async def get_all_chats(user: Users = Depends(check_verificated_user_with_exc)):
|
||||
result = await UserDAO.get_user_allowed_chats(user.id)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("", status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=None,
|
||||
)
|
||||
async def add_message_to_chat(chat_id: int, message: str, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
chats = await AuthService.get_user_allowed_chats_id(user.id)
|
||||
if chat_id not in chats:
|
||||
|
@ -35,7 +43,11 @@ async def add_message_to_chat(chat_id: int, message: str, user: Users = Depends(
|
|||
return send_message_to_chat
|
||||
|
||||
|
||||
@router.delete("/delete_message", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete(
|
||||
"/delete_message",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None,
|
||||
)
|
||||
async def delete_message_from_chat(message_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
get_message_sender = await ChatDAO.get_message_by_id(message_id=message_id)
|
||||
if get_message_sender is None:
|
||||
|
@ -47,7 +59,11 @@ async def delete_message_from_chat(message_id: int, user: Users = Depends(check_
|
|||
return deleted_message
|
||||
|
||||
|
||||
@router.post("/create_chat", status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"/create_chat",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=None,
|
||||
)
|
||||
async def create_chat(user_to_exclude: int, chat_name: str, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
if user.id == user_to_exclude:
|
||||
raise UserCanNotReadThisChatException
|
||||
|
@ -57,7 +73,11 @@ async def create_chat(user_to_exclude: int, chat_name: str, user: Users = Depend
|
|||
return user_added_to_chat
|
||||
|
||||
|
||||
@router.get("/get_last_message/{chat_id}", response_model=list[SMessage])
|
||||
@router.get(
|
||||
"/get_last_message/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage]
|
||||
)
|
||||
async def get_last_message(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
message = await ChatDAO.get_some_messages(chat_id=chat_id, message_number_from=0, messages_to_get=1)
|
||||
|
@ -68,13 +88,18 @@ async def get_last_message(chat_id: int, user: Users = Depends(check_verificated
|
|||
return message
|
||||
|
||||
|
||||
@router.get("/get_some_messages/{chat_id}", response_model=list[SMessage])
|
||||
@router.get(
|
||||
"/get_some_messages/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage]
|
||||
)
|
||||
async def get_some_messages(
|
||||
chat_id: int, last_messages: SLastMessages = Depends(), user: Users = Depends(check_verificated_user_with_exc)
|
||||
chat_id: int, last_messages: SLastMessages = Depends(), user: Users = Depends(check_verificated_user_with_exc)
|
||||
):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
messages = await ChatDAO.get_some_messages(
|
||||
chat_id=chat_id, message_number_from=last_messages.messages_loaded, messages_to_get=last_messages.messages_to_get
|
||||
chat_id=chat_id, message_number_from=last_messages.messages_loaded,
|
||||
messages_to_get=last_messages.messages_to_get
|
||||
)
|
||||
if not messages:
|
||||
raise MessageNotFoundException
|
||||
|
@ -83,7 +108,11 @@ async def get_some_messages(
|
|||
return messages
|
||||
|
||||
|
||||
@router.get("/message/{chat_id}", response_model=SMessage)
|
||||
@router.get(
|
||||
"/message/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SMessage
|
||||
)
|
||||
async def get_message_by_id(chat_id: int, message_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
message = await ChatDAO.get_message_by_id(message_id=message_id)
|
||||
|
@ -94,7 +123,11 @@ async def get_message_by_id(chat_id: int, message_id: int, user: Users = Depends
|
|||
return message
|
||||
|
||||
|
||||
@router.get("/create_invitation_link", response_model=SCreateInvitationLink, status_code=status.HTTP_201_CREATED)
|
||||
@router.get(
|
||||
"/create_invitation_link",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=SCreateInvitationLink,
|
||||
)
|
||||
async def create_invitation_link(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
|
@ -103,7 +136,11 @@ async def create_invitation_link(chat_id: int, user: Users = Depends(check_verif
|
|||
return {"invitation_link": invitation_link}
|
||||
|
||||
|
||||
@router.get("/invite_to_chat/{invitation_token}", response_model=SUserAddedToChat, status_code=status.HTTP_200_OK)
|
||||
@router.get(
|
||||
"/invite_to_chat/{invitation_token}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SUserAddedToChat,
|
||||
)
|
||||
async def invite_to_chat(invitation_token: str, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
invitation_token = invitation_token.encode()
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
|
@ -114,7 +151,11 @@ async def invite_to_chat(invitation_token: str, user: Users = Depends(check_veri
|
|||
return {"user_added_to_chat": await ChatDAO.add_user_to_chat(chat_id=chat_id, user_id=user.id)}
|
||||
|
||||
|
||||
@router.delete("/delete_chat/{chat_id}", response_model=SDeletedChat, status_code=status.HTTP_200_OK)
|
||||
@router.delete(
|
||||
"/delete_chat/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SDeletedChat,
|
||||
)
|
||||
async def delete_chat(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
chat = await ChatDAO.find_one_or_none(id=chat_id)
|
||||
if user.id == chat.created_by:
|
||||
|
@ -122,7 +163,11 @@ async def delete_chat(chat_id: int, user: Users = Depends(check_verificated_user
|
|||
raise UserDontHavePermissionException
|
||||
|
||||
|
||||
@router.delete("/delete_user_from_chat/{chat_id}", response_model=SDeletedUser)
|
||||
@router.delete(
|
||||
"/delete_user_from_chat/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SDeletedUser
|
||||
)
|
||||
async def delete_user_from_chat(chat_id: int, user_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
chat = await ChatDAO.find_one_or_none(id=chat_id)
|
||||
if user.id == chat.created_by:
|
||||
|
@ -130,33 +175,53 @@ async def delete_user_from_chat(chat_id: int, user_id: int, user: Users = Depend
|
|||
raise UserDontHavePermissionException
|
||||
|
||||
|
||||
@router.post("/pinn_chat", response_model=SPinnedChat)
|
||||
@router.post(
|
||||
"/pin_chat",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedChat
|
||||
)
|
||||
async def pinn_chat(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
await ChatDAO.pinn_chat(chat_id=chat_id, user_id=user.id)
|
||||
return {"chat_id": chat_id, "user_id": user.id}
|
||||
|
||||
|
||||
@router.delete("/unpinn_chat", response_model=SPinnedChat)
|
||||
@router.delete(
|
||||
"/unpin_chat",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedChat
|
||||
)
|
||||
async def unpinn_chat(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
await ChatDAO.unpinn_chat(chat_id=chat_id, user_id=user.id)
|
||||
return {"chat_id": chat_id, "user_id": user.id}
|
||||
|
||||
|
||||
@router.get("/get_pinned_chats", response_model=list[SChat])
|
||||
@router.get(
|
||||
"/get_pinned_chats",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SChat]
|
||||
)
|
||||
async def get_pinned_chats(user: Users = Depends(check_verificated_user_with_exc)):
|
||||
return await ChatDAO.get_pinned_chats(user_id=user.id)
|
||||
|
||||
|
||||
@router.post("/pinn_message", response_model=SPinnedMessage)
|
||||
@router.post(
|
||||
"/pin_message",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedMessage
|
||||
)
|
||||
async def pinn_message(chat_id: int, message_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
await ChatDAO.pinn_message(chat_id=chat_id, message_id=message_id, user_id=user.id)
|
||||
return {"message_id": message_id, "user_id": user.id, "chat_id": chat_id}
|
||||
|
||||
|
||||
@router.delete("/unpinn_message", response_model=SPinnedMessage)
|
||||
@router.delete(
|
||||
"/unpin_message",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedMessage
|
||||
)
|
||||
async def unpinn_message(chat_id: int, message_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
message_pinner = await ChatDAO.get_message_pinner(chat_id=chat_id, message_id=message_id)
|
||||
|
@ -166,7 +231,11 @@ async def unpinn_message(chat_id: int, message_id: int, user: Users = Depends(ch
|
|||
raise UserDontHavePermissionException
|
||||
|
||||
|
||||
@router.get("/pinned_messages/{chat_id}", response_model=list[SMessage] | None)
|
||||
@router.get(
|
||||
"/pinned_messages/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage] | None
|
||||
)
|
||||
async def pinned_messages(chat_id: int, user: Users = Depends(check_verificated_user_with_exc)):
|
||||
await AuthService.validate_user_access_to_chat(chat_id=chat_id, user_id=user.id)
|
||||
messages = await ChatDAO.get_pinned_messages(chat_id=chat_id)
|
||||
|
|
|
@ -19,14 +19,9 @@ class SMessage(BaseModel):
|
|||
answer_id: int | None
|
||||
|
||||
|
||||
class SLastMessages:
|
||||
def __init__(
|
||||
self,
|
||||
messages_loaded: int,
|
||||
messages_to_get: int,
|
||||
):
|
||||
self.messages_loaded = messages_loaded
|
||||
self.messages_to_get = messages_to_get
|
||||
class SLastMessages(BaseModel):
|
||||
messages_loaded: int
|
||||
messages_to_get: int
|
||||
|
||||
|
||||
class SPinnedMessage(BaseModel):
|
||||
|
@ -75,3 +70,14 @@ class SEditMessage(BaseModel):
|
|||
id: int
|
||||
new_message: str
|
||||
new_image_url: str
|
||||
|
||||
|
||||
class SPinMessage(BaseModel):
|
||||
flag: str
|
||||
id: int
|
||||
user_id: int
|
||||
|
||||
|
||||
class SUnpinMessage(BaseModel):
|
||||
flag: str
|
||||
id: int
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from fastapi import WebSocket, WebSocketDisconnect
|
||||
from fastapi import WebSocket, WebSocketDisconnect, Depends
|
||||
|
||||
from app.exceptions import IncorrectDataException, UserDontHavePermissionException
|
||||
from app.services.message_service import MessageService
|
||||
from app.users.auth import AuthService
|
||||
from app.chat.router import router
|
||||
from app.chat.shemas import SSendMessage, SMessage, SDeleteMessage, SEditMessage
|
||||
from app.chat.shemas import SSendMessage, SMessage, SDeleteMessage, SEditMessage, SPinMessage, SUnpinMessage
|
||||
from app.users.dependencies import get_current_user_ws
|
||||
from app.users.schemas import SUser
|
||||
|
||||
|
||||
class ConnectionManager(WebSocket):
|
||||
class ConnectionManager:
|
||||
def __init__(self):
|
||||
self.active_connections: dict[int, list[WebSocket]] = {}
|
||||
|
||||
|
@ -49,12 +51,25 @@ class ConnectionManager(WebSocket):
|
|||
|
||||
edited_message = await self.edit_message(message.id, message.new_message, message.new_image_url)
|
||||
new_message = {
|
||||
"edited_message": edited_message,
|
||||
"flag": "edit",
|
||||
"id": message.id,
|
||||
"edited_message": edited_message,
|
||||
"new_message": message.new_message,
|
||||
"new_image_url": message.new_image_url,
|
||||
}
|
||||
|
||||
elif message["flag"] == "pin":
|
||||
message = SPinMessage.model_validate(message)
|
||||
pinned_message = await self.pin_message(chat_id=chat_id, user_id=message.user_id, message_id=message.id)
|
||||
new_message = pinned_message.model_dump()
|
||||
new_message["created_at"] = new_message["created_at"].isoformat()
|
||||
new_message["flag"] = "pin"
|
||||
|
||||
elif message["flag"] == "unpin":
|
||||
message = SUnpinMessage.model_validate(message)
|
||||
unpinned_message = await self.unpin_message(chat_id=chat_id, message_id=message.id)
|
||||
new_message = {"flag": "pin", "id": unpinned_message}
|
||||
|
||||
else:
|
||||
raise IncorrectDataException
|
||||
|
||||
|
@ -82,19 +97,31 @@ class ConnectionManager(WebSocket):
|
|||
)
|
||||
return new_message
|
||||
|
||||
@staticmethod
|
||||
async def pin_message(chat_id: int, user_id: int, message_id: int) -> SMessage:
|
||||
pinned_message = await MessageService.pin_message(chat_id=chat_id, user_id=user_id, message_id=message_id)
|
||||
return pinned_message
|
||||
|
||||
@staticmethod
|
||||
async def unpin_message(chat_id: int, message_id: int) -> int:
|
||||
unpinned_message_id = await MessageService.unpin_message(chat_id=chat_id, message_id=message_id)
|
||||
return unpinned_message_id
|
||||
|
||||
|
||||
manager = ConnectionManager()
|
||||
|
||||
|
||||
@router.websocket("/ws/{chat_id}")
|
||||
async def websocket_endpoint(chat_id: int, user_id: int, websocket: WebSocket):
|
||||
await AuthService.check_verificated_user_with_exc(user_id=user_id)
|
||||
await AuthService.validate_user_access_to_chat(user_id=user_id, chat_id=chat_id)
|
||||
@router.websocket(
|
||||
"/ws/{chat_id}",
|
||||
)
|
||||
async def websocket_endpoint(chat_id: int, websocket: WebSocket, user: SUser = Depends(get_current_user_ws)):
|
||||
await AuthService.check_verificated_user_with_exc(user_id=user.id)
|
||||
await AuthService.validate_user_access_to_chat(user_id=user.id, chat_id=chat_id)
|
||||
await manager.connect(chat_id, websocket)
|
||||
try:
|
||||
while True:
|
||||
data = await websocket.receive_json()
|
||||
|
||||
await manager.broadcast(user_id=user_id, chat_id=chat_id, message=data)
|
||||
await manager.broadcast(user_id=user.id, chat_id=chat_id, message=data)
|
||||
except WebSocketDisconnect:
|
||||
manager.disconnect(chat_id, websocket)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from fastapi import APIRouter, UploadFile, HTTPException
|
||||
from fastapi import APIRouter, UploadFile, HTTPException, status
|
||||
import httpx
|
||||
|
||||
from app.config import settings
|
||||
|
@ -36,19 +36,31 @@ async def upload_avatar_returning_dict(file: UploadFile, sub_str: str) -> dict:
|
|||
return {"image_url": image_url, "hex_color": hex_color}
|
||||
|
||||
|
||||
@router.post("/upload_avatar", response_model=SAvatarUrl)
|
||||
@router.post(
|
||||
"/upload_avatar",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SAvatarUrl
|
||||
)
|
||||
async def upload_avatar(file: UploadFile):
|
||||
image_data = await upload_avatar_returning_dict(file, "upload_avatar")
|
||||
return image_data
|
||||
|
||||
|
||||
@router.post("/upload_image", response_model=SImageUrl)
|
||||
@router.post(
|
||||
"/upload_image",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SImageUrl
|
||||
)
|
||||
async def upload_image(file: UploadFile):
|
||||
image_url = await upload_file_returning_str(file, "upload_image")
|
||||
return {"image_url": image_url}
|
||||
|
||||
|
||||
@router.post("/upload_background", response_model=list[SImageUrl])
|
||||
@router.post(
|
||||
"/upload_background",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SImageUrl]
|
||||
)
|
||||
async def upload_background(file: UploadFile):
|
||||
remote_server_url = settings.IMAGE_UPLOAD_SERVER + "upload_background"
|
||||
response = await upload_file(file, remote_server_url)
|
||||
|
|
|
@ -22,3 +22,11 @@ class MessageService:
|
|||
async def edit_message(message_id: int, new_message: str, new_image_url: str) -> bool:
|
||||
new_message = await ChatDAO.edit_message(message_id=message_id, new_message=new_message, new_image_url=new_image_url)
|
||||
return new_message
|
||||
|
||||
@staticmethod
|
||||
async def pin_message(chat_id: int, user_id: int, message_id: int):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
async def unpin_message(chat_id: int, message_id: int):
|
||||
pass
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, UTC
|
||||
|
||||
from jose import jwt
|
||||
from passlib.context import CryptContext
|
||||
|
@ -32,10 +32,9 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
# Функция создания JWT токена
|
||||
def create_access_token(data: dict) -> str:
|
||||
to_encode = data.copy()
|
||||
expire = datetime.utcnow() + timedelta(minutes=180)
|
||||
expire = datetime.now(UTC) + timedelta(days=30)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, settings.ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
|
|
@ -13,7 +13,7 @@ class UserDAO(BaseDAO):
|
|||
model = Users
|
||||
|
||||
@staticmethod
|
||||
async def find_one_or_none(**filter_by) -> SUser | None: # Метод проверяет наличие строки с заданными параметрами
|
||||
async def find_one_or_none(**filter_by) -> SUser | None:
|
||||
async with async_session_maker() as session:
|
||||
query = select(Users).filter_by(**filter_by)
|
||||
result = await session.execute(query)
|
||||
|
@ -117,8 +117,8 @@ class UserDAO(BaseDAO):
|
|||
return True
|
||||
|
||||
@staticmethod
|
||||
async def get_user_avatars(user_id: int) -> list[Mapping[str, str | int]]:
|
||||
query = select(UsersAvatars.avatar_image, UsersAvatars.id).where(UsersAvatars.user_id == user_id)
|
||||
async def get_user_avatars(user_id: int) -> list[Mapping[str, str]]:
|
||||
query = select(UsersAvatars.avatar_image).where(UsersAvatars.user_id == user_id)
|
||||
async with async_session_maker() as session:
|
||||
result = await session.execute(query)
|
||||
result = result.mappings().all()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, UTC
|
||||
|
||||
from fastapi import Depends, Request, Response
|
||||
from jose import JWTError, jwt
|
||||
from fastapi import Depends, Request, Response, WebSocket
|
||||
from jose import JWTError, jwt, ExpiredSignatureError
|
||||
|
||||
from app.config import settings
|
||||
from app.exceptions import (
|
||||
|
@ -26,13 +26,11 @@ def get_token(request: Request) -> str:
|
|||
async def get_current_user(response: Response, token: str = Depends(get_token)) -> SUser:
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, settings.ALGORITHM)
|
||||
except ExpiredSignatureError:
|
||||
raise TokenExpiredException
|
||||
except JWTError:
|
||||
raise IncorrectTokenFormatException
|
||||
|
||||
expire: str = payload.get("exp")
|
||||
if not expire or int(expire) < datetime.utcnow().timestamp():
|
||||
raise TokenExpiredException
|
||||
|
||||
user_id: str = payload.get("sub")
|
||||
if not user_id:
|
||||
raise UserIsNotPresentException
|
||||
|
@ -50,3 +48,29 @@ async def check_verificated_user_with_exc(user: SUser = Depends(get_current_user
|
|||
if not user.role >= VERIFICATED_USER:
|
||||
raise UserMustConfirmEmailException
|
||||
return user
|
||||
|
||||
|
||||
def get_token_ws(websocket: WebSocket) -> str:
|
||||
token = websocket.cookies.get("black_phoenix_access_token")
|
||||
if not token:
|
||||
raise TokenAbsentException
|
||||
return token
|
||||
|
||||
|
||||
async def get_current_user_ws(token: str = Depends(get_token_ws)):
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, settings.ALGORITHM)
|
||||
except ExpiredSignatureError:
|
||||
raise TokenExpiredException
|
||||
except JWTError:
|
||||
raise IncorrectTokenFormatException
|
||||
|
||||
user_id: str = payload.get("sub")
|
||||
if not user_id:
|
||||
raise UserIsNotPresentException
|
||||
|
||||
user = await UserService.find_one_or_none(user_id=int(user_id))
|
||||
if not user:
|
||||
raise UserIsNotPresentException
|
||||
|
||||
return user
|
||||
|
|
|
@ -2,74 +2,78 @@ import json
|
|||
|
||||
from cryptography.fernet import Fernet
|
||||
from fastapi import APIRouter, Response, Depends, status
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
from app.config import settings
|
||||
from app.exceptions import (
|
||||
UsernameAlreadyInUseException,
|
||||
IncorrectPasswordException,
|
||||
PasswordsMismatchException,
|
||||
WrongCodeException,
|
||||
UserNotFoundException,
|
||||
SomethingWentWrongException,
|
||||
UserAlreadyExistsException,
|
||||
)
|
||||
from app.users.auth import get_password_hash, create_access_token, verify_password, VERIFICATED_USER, AuthService
|
||||
from app.users.auth import get_password_hash, create_access_token, VERIFICATED_USER, AuthService
|
||||
from app.users.dao import UserDAO, UserCodesDAO
|
||||
from app.users.dependencies import get_current_user, check_verificated_user_with_exc
|
||||
from app.users.dependencies import get_current_user
|
||||
from app.users.models import Users
|
||||
from app.users.schemas import (
|
||||
SUserLogin,
|
||||
SUserRegister,
|
||||
SUserResponse,
|
||||
SUserPassword,
|
||||
SUserRename,
|
||||
SUserAvatar,
|
||||
SUserPasswordRecover,
|
||||
SUserCode,
|
||||
SUserPasswordChange,
|
||||
SRecoverEmailSent,
|
||||
SUserToken,
|
||||
SEmailVerification,
|
||||
SUserName,
|
||||
SNewAvatar,
|
||||
SConfirmPasswordRecovery,
|
||||
SPasswordRecovered,
|
||||
SUserAvatars,
|
||||
SUsername,
|
||||
SEmail,
|
||||
)
|
||||
from app.tasks.tasks import send_registration_confirmation_email, send_password_change_email, send_password_recover_email
|
||||
from app.tasks.tasks import send_registration_confirmation_email, send_password_change_email, \
|
||||
send_password_recover_email
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["Пользователи"])
|
||||
|
||||
|
||||
@router.get("/teleport", response_class=RedirectResponse, status_code=status.HTTP_307_TEMPORARY_REDIRECT)
|
||||
async def get_teleport():
|
||||
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||
|
||||
|
||||
@router.get("", response_model=list[SUserResponse])
|
||||
@router.get(
|
||||
"",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SUserResponse]
|
||||
)
|
||||
async def get_all_users():
|
||||
users = await UserDAO.find_all()
|
||||
return users
|
||||
|
||||
|
||||
@router.post("/check_existing_username", status_code=status.HTTP_200_OK)
|
||||
@router.post(
|
||||
"/check_existing_username",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None
|
||||
)
|
||||
async def check_existing_username(username: SUsername):
|
||||
user = await UserDAO.find_one_or_none(username=username.username)
|
||||
if user:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
|
||||
@router.post("/check_existing_email", status_code=status.HTTP_200_OK)
|
||||
@router.post(
|
||||
"/check_existing_email",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None
|
||||
)
|
||||
async def check_existing_email(email: SEmail):
|
||||
user = await UserDAO.find_one_or_none(email=email.email)
|
||||
if user:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
|
||||
@router.post("/register", response_model=SUserToken, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"/register",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=SUserToken,
|
||||
)
|
||||
async def register_user(response: Response, user_data: SUserRegister):
|
||||
if user_data.password != user_data.password2:
|
||||
raise PasswordsMismatchException
|
||||
|
@ -95,7 +99,11 @@ async def register_user(response: Response, user_data: SUserRegister):
|
|||
return {"access_token": access_token}
|
||||
|
||||
|
||||
@router.get("/email_verification/{user_code}", status_code=status.HTTP_200_OK, response_model=SEmailVerification)
|
||||
@router.get(
|
||||
"/email_verification/{user_code}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SEmailVerification
|
||||
)
|
||||
async def email_verification(user_code: str):
|
||||
invitation_token = user_code.encode()
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
|
@ -109,7 +117,11 @@ async def email_verification(user_code: str):
|
|||
return {"email_verification": True}
|
||||
|
||||
|
||||
@router.post("/login", response_model=SUserToken)
|
||||
@router.post(
|
||||
"/login",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SUserToken
|
||||
)
|
||||
async def login_user(response: Response, user_data: SUserLogin):
|
||||
user = await AuthService.authenticate_user(user_data.email_or_username, user_data.password)
|
||||
access_token = create_access_token({"sub": str(user.id)})
|
||||
|
@ -117,51 +129,29 @@ async def login_user(response: Response, user_data: SUserLogin):
|
|||
return {"access_token": access_token}
|
||||
|
||||
|
||||
@router.post("/logout", status_code=status.HTTP_200_OK)
|
||||
@router.post(
|
||||
"/logout",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None
|
||||
)
|
||||
async def logout_user(response: Response):
|
||||
response.delete_cookie("black_phoenix_access_token")
|
||||
|
||||
|
||||
@router.get("/me", response_model=SUserResponse)
|
||||
async def read_users_me(current_user: Users = Depends(get_current_user)):
|
||||
@router.get(
|
||||
"/me",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SUserResponse
|
||||
)
|
||||
async def get_user(current_user: Users = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
@router.patch("/rename", response_model=SUserName)
|
||||
async def rename_user(new_username: SUserRename, current_user: Users = Depends(check_verificated_user_with_exc)):
|
||||
if not verify_password(new_username.password, current_user.hashed_password):
|
||||
raise IncorrectPasswordException
|
||||
existing_user = await UserDAO.find_one_or_none(username=new_username.username)
|
||||
if existing_user:
|
||||
raise UsernameAlreadyInUseException
|
||||
new_username = await UserDAO.change_data(current_user.id, username=new_username.username)
|
||||
return {"username": new_username}
|
||||
|
||||
|
||||
@router.patch("/change_avatar", response_model=SNewAvatar)
|
||||
async def change_avatar(user_data: SUserAvatar, current_user: Users = Depends(check_verificated_user_with_exc)):
|
||||
if not verify_password(user_data.password, current_user.hashed_password):
|
||||
raise IncorrectPasswordException
|
||||
if await UserDAO.change_data(
|
||||
current_user.id, avatar_image=user_data.new_avatar_image, avatar_hex=user_data.avatar_hex
|
||||
) and await UserDAO.add_user_avatar(user_id=current_user.id, avatar=user_data.new_avatar_image):
|
||||
return {"new_avatar_image": user_data.new_avatar_image, "avatar_hex": user_data.avatar_hex}
|
||||
raise SomethingWentWrongException
|
||||
|
||||
|
||||
@router.patch("/change_password", status_code=status.HTTP_200_OK)
|
||||
async def change_password(new_password: SUserPassword, current_user: Users = Depends(check_verificated_user_with_exc)):
|
||||
if new_password.new_password != new_password.new_password2:
|
||||
raise PasswordsMismatchException
|
||||
existing_user = await UserDAO.find_one_or_none(id=current_user.id)
|
||||
if not verify_password(new_password.current_password, existing_user.hashed_password):
|
||||
raise IncorrectPasswordException
|
||||
hashed_password = get_password_hash(new_password.new_password)
|
||||
await UserDAO.change_data(current_user.id, hashed_password=hashed_password)
|
||||
send_password_change_email.delay(current_user.username, current_user.email, MODE=settings.MODE)
|
||||
|
||||
|
||||
@router.patch("/send_recovery_email", status_code=status.HTTP_200_OK, response_model=SRecoverEmailSent)
|
||||
@router.patch(
|
||||
"/send_recovery_email",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SRecoverEmailSent
|
||||
)
|
||||
async def send_recovery_email(email: SUserPasswordRecover):
|
||||
existing_user = await UserDAO.find_one_or_none(email=email.email)
|
||||
if not existing_user:
|
||||
|
@ -170,16 +160,20 @@ async def send_recovery_email(email: SUserPasswordRecover):
|
|||
result = result.get()
|
||||
|
||||
if (
|
||||
await UserCodesDAO.set_user_codes(
|
||||
user_id=existing_user.user_id, code=result, description="Код восстановления пароля"
|
||||
)
|
||||
== result
|
||||
await UserCodesDAO.set_user_codes(
|
||||
user_id=existing_user.user_id, code=result, description="Код восстановления пароля"
|
||||
)
|
||||
== result
|
||||
):
|
||||
return {"recover_email_sent": True}
|
||||
raise SomethingWentWrongException
|
||||
|
||||
|
||||
@router.post("/confirm_password_recovery", status_code=status.HTTP_200_OK, response_model=SConfirmPasswordRecovery)
|
||||
@router.post(
|
||||
"/confirm_password_recovery",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SConfirmPasswordRecovery
|
||||
)
|
||||
async def confirm_password_recovery(user_code: SUserCode):
|
||||
user_codes = await UserCodesDAO.get_user_codes(description="Код восстановления пароля", code=user_code.user_code)
|
||||
if not user_codes:
|
||||
|
@ -187,7 +181,11 @@ async def confirm_password_recovery(user_code: SUserCode):
|
|||
return {"user_id": user_codes[0]["user_id"]}
|
||||
|
||||
|
||||
@router.post("/password_recovery", status_code=status.HTTP_200_OK, response_model=SPasswordRecovered)
|
||||
@router.post(
|
||||
"/password_recovery",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPasswordRecovered
|
||||
)
|
||||
async def password_recovery(passwords: SUserPasswordChange):
|
||||
if passwords.password1 != passwords.password2:
|
||||
raise PasswordsMismatchException
|
||||
|
@ -200,11 +198,10 @@ async def password_recovery(passwords: SUserPasswordChange):
|
|||
return {"username": username}
|
||||
|
||||
|
||||
@router.get("/avatars", response_model=list[SUserAvatars])
|
||||
@router.get(
|
||||
"/avatars",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SUserAvatars]
|
||||
)
|
||||
async def get_user_avatars_history(user: Users = Depends(get_current_user)):
|
||||
return await UserDAO.get_user_avatars(user_id=user.id)
|
||||
|
||||
|
||||
@router.delete("/avatars", response_model=bool)
|
||||
async def delete_form_user_avatars_history(avatar_id: int, user: Users = Depends(get_current_user)):
|
||||
return await UserDAO.delete_user_avatar(avatar_id=avatar_id, user_id=user.id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import date, timedelta, datetime, time
|
||||
|
||||
from pydantic_core import PydanticCustomError
|
||||
from pydantic import BaseModel, EmailStr, ConfigDict, field_validator
|
||||
from pydantic import BaseModel, EmailStr, ConfigDict, field_validator, HttpUrl
|
||||
from fastapi import Query
|
||||
|
||||
|
||||
|
@ -128,8 +128,7 @@ class SUserAddedToChat(BaseModel):
|
|||
|
||||
|
||||
class SUserAvatars(BaseModel):
|
||||
id: int
|
||||
avatar_image: str
|
||||
avatar_image: HttpUrl
|
||||
|
||||
|
||||
class SUsername(BaseModel):
|
||||
|
|
Loading…
Add table
Reference in a new issue