diff --git a/.env_template b/.env_template index 628a0c7..73d9eda 100644 --- a/.env_template +++ b/.env_template @@ -24,3 +24,6 @@ SMTP_USER= SMTP_PASS= IMAGE_UPLOAD_SERVER= + +INVITATION_LINK_HOST= +INVITATION_LINK_TOKEN_KEY= \ No newline at end of file diff --git a/app/config.py b/app/config.py index fc9fac1..8eb1bac 100644 --- a/app/config.py +++ b/app/config.py @@ -34,5 +34,8 @@ class Settings(BaseSettings): IMAGE_UPLOAD_SERVER: str + INVITATION_LINK_HOST: str + INVITATION_LINK_TOKEN_KEY: bytes + settings = Settings() diff --git a/app/users/chat/models.py b/app/users/chat/models.py index 618a7ef..744a580 100644 --- a/app/users/chat/models.py +++ b/app/users/chat/models.py @@ -22,7 +22,7 @@ class Chats(Base): user_who_create = relationship("Users", primaryjoin='Chats.created_by == Users.id', back_populates="creator") def __str__(self): - return f"Чат #{self.id}. Виден: {self.visibility}. Сделан {self.created_by}" + return f"Чат #{self.id}. Виден: {self.visibility}. Сделан {self.created_by} для {self.chat_for}" class Messages(Base): diff --git a/app/users/chat/router.py b/app/users/chat/router.py index d9d9a48..f0567f1 100644 --- a/app/users/chat/router.py +++ b/app/users/chat/router.py @@ -1,5 +1,8 @@ +from cryptography.fernet import Fernet + from fastapi import APIRouter, Depends, status +from app.config import settings from app.exceptions import UserDontHavePermissionException, MessageNotFoundException, UserCanNotReadThisChatException from app.users.chat.dao import ChatDAO from app.users.chat.shemas import SMessage, SLastMessages @@ -91,3 +94,24 @@ async def get_some_messages( for mes in messages: mes["created_at"] = mes["created_at"].isoformat() return messages + + +@router.get("/create_invitation_link", response_model=dict[str, str]) +async def create_invitation_link(chat_id: int, user: Users = Depends(get_current_user)): + await validate_user_access_to_chat(chat_id=chat_id, user_id=user.id) + cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY) + invitation_token = cipher_suite.encrypt(str(chat_id).encode()) + invitation_link = settings.INVITATION_LINK_HOST + '/api/chat/invite_to_chat/' + str(invitation_token).split("'")[1] + return {'invitation_link': invitation_link} + + +@router.get("/invite_to_chat/{invitation_token}") +async def invite_to_chat(invitation_token: str, user: Users = Depends(get_current_user)): + invitation_token = bytes(invitation_token, 'utf-8') + cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY) + chat_id = int(cipher_suite.decrypt(invitation_token)) + chat = await ChatDAO.find_one_or_none(id=chat_id) + if user.id == chat.chat_for: + raise UserCanNotReadThisChatException + return await ChatDAO.add_user_to_chat(chat_id=chat_id, user_id=user.id) + diff --git a/app/users/router.py b/app/users/router.py index ad16e2b..957d02d 100644 --- a/app/users/router.py +++ b/app/users/router.py @@ -1,7 +1,5 @@ -from fastapi import APIRouter, Response, Depends +from fastapi import APIRouter, Response, Depends, status from fastapi.responses import RedirectResponse -from sqlalchemy.sql.functions import current_user -from starlette import status from app.exceptions import UsernameAlreadyInUseException, \ IncorrectPasswordException, PasswordsМismatchException, WrongCodeException, UserNotFoundException diff --git a/requirements.txt b/requirements.txt index 9db9295..950f89c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,14 +9,17 @@ async-timeout==4.0.3 asyncpg==0.29.0 attrs==23.2.0 autoflake==2.2.1 +bcrypt==4.1.2 billiard==4.2.0 black==23.12.1 celery==5.3.6 certifi==2023.11.17 +cffi==1.16.0 click==8.1.7 click-didyoumean==0.3.0 click-plugins==1.1.1 click-repl==0.3.0 +cryptography==42.0.5 dnspython==2.4.2 ecdsa==0.18.0 email-validator==2.1.0.post1 @@ -60,6 +63,7 @@ prometheus-fastapi-instrumentator==6.1.0 prompt-toolkit==3.0.43 pyasn1==0.5.1 pycodestyle==2.11.1 +pycparser==2.21 pydantic==2.6.0 pydantic-extra-types==2.5.0 pydantic-settings==2.1.0