Compare commits

..

2 commits

12 changed files with 172 additions and 45 deletions

View file

@ -54,7 +54,7 @@ class IncorrectLengthOfNicknameException(BlackPhoenixException):
detail = "Ник должен быть не короче 2 и не длиннее 30 символов"
class UDontHavePermissionException(BlackPhoenixException):
class UserDontHavePermissionException(BlackPhoenixException):
status_code = status.HTTP_409_CONFLICT
detail = "У вас нет прав для этого действия"

View file

@ -4,10 +4,12 @@ from starlette.staticfiles import StaticFiles
from app.users.chat.router import router as chat_router
from app.users.router import router as user_router
from app.pages.router import router as pages_router
from app.users.chat.websocket import router as websocket_router
app = FastAPI()
app.include_router(chat_router)
app.include_router(websocket_router)
# app.include_router(chat_router)
app.include_router(user_router)
app.include_router(pages_router)

View file

@ -0,0 +1,30 @@
"""Убрал обязательность авы
Revision ID: 6d13086c9c2d
Revises: 2913a8a70afb
Create Date: 2024-02-03 13:12:24.466115
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '6d13086c9c2d'
down_revision: Union[str, None] = '2913a8a70afb'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -1,6 +1,9 @@
from fastapi import APIRouter, Request
from fastapi import APIRouter, Request, Depends
from fastapi.templating import Jinja2Templates
from app.users.dependencies import get_current_user
from app.users.models import Users
router = APIRouter(
prefix="/pages",
tags=["Страницы"]
@ -12,3 +15,11 @@ templates = Jinja2Templates(directory="app/templates")
@router.get("/base")
async def base(request: Request):
return templates.TemplateResponse("base.html", {"request": request})
@router.get("/chat")
async def get_chat_page(request: Request):
return templates.TemplateResponse("chat.html", {"request": request})

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,10 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BlackPhoenix</title>
</head>
<body>
{% extends "base.html" %}
</body>
</html>
{% block content %}
<div class="flex flex-col items-center">
<h1>WebSocket Chat</h1>
<h2>Your ID: <span id="ws-id"></span></h2>
<form action="" onsubmit="sendMessage(event)">
<input class="bg-green-300" type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
</div>
<script>
async function getLastMessages() {
const url = 'http://localhost:8000/chat/last_messages'
const response = await fetch(url, {
method: 'GET'
})
return response.json()
}
getLastMessages()
.then(messages => {
appendMessage("Предыдущие 5 сообщений:")
messages.forEach(msg => {
appendMessage(msg.message)
})
appendMessage("\nНовые сообщения:")
})
function appendMessage(msg) {
let messages = document.getElementById('messages')
let message = document.createElement('li')
let content = document.createTextNode(msg)
message.appendChild(content)
messages.appendChild(message)
}
let chat_id = 2
document.querySelector("#ws-id").textContent = chat_id;
let ws = new WebSocket(`ws://localhost:8000/chat/ws/${chat_id}?user_id=2`);
ws.onmessage = function (event) {
appendMessage(event.data)
};
function sendMessage(event) {
let input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
{% endblock %}

View file

@ -18,8 +18,8 @@ class ChatDAO(BaseDAO):
return True
@classmethod
async def send_message(cls, user_id, chat_id, message, image_url):
query = insert(Messages).values(chat_id=chat_id, user_id=user_id, message=message, image_url=image_url)
async def send_message(cls, user_id, chat_id, message):
query = insert(Messages).values(chat_id=chat_id, user_id=user_id, message=message, image_url=None)
async with async_session_maker() as session:
await session.execute(query)
await session.commit()

View file

@ -1,12 +1,12 @@
from fastapi import APIRouter, WebSocket, Depends, status
from starlette.websockets import WebSocketDisconnect
from fastapi import APIRouter, Depends, status
from app.exceptions import UDontHavePermissionException, MessageNotFoundException
from app.exceptions import UserDontHavePermissionException, MessageNotFoundException
from app.users.chat.dao import ChatDAO
from app.users.chat.shemas import SMessage
from app.users.chat.websocket import manager
from app.users.dao import UserDAO
from app.users.dependencies import get_current_user
from app.users.dependencies import get_current_user, validate_user_access_to_chat
from app.users.models import Users
router = APIRouter(
@ -15,6 +15,12 @@ router = APIRouter(
)
@router.get("")
async def root(user: Users = Depends(get_current_user)):
print(user)
return user.id
@router.post("", status_code=status.HTTP_201_CREATED)
async def add_message_to_chat(
chat_id: int,
@ -41,7 +47,7 @@ async def delete_message_from_chat(
if get_message_sender["user_id"] != user.id:
get_user_role = await UserDAO.get_user_role(user_id=user.id)
if not get_user_role == 1:
raise UDontHavePermissionException
raise UserDontHavePermissionException
deleted_message = await ChatDAO.delete_message(message_id=message_id)
return deleted_message
@ -65,15 +71,8 @@ async def create_chat(
created_chat = await ChatDAO.create(user_id=user_to_exclude)
return created_chat
@router.get("/last_messages")
async def get_last_messages():
pass
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, user: Users = Depends(get_current_user)):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"User {user.id}: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"User {user.id}: себался")

View file

@ -1,20 +1,53 @@
from fastapi import WebSocket
from typing import Dict, List
from fastapi import WebSocket, Depends, WebSocketDisconnect
from app.users.chat.dao import ChatDAO
from app.users.dependencies import validate_user_access_to_chat, get_current_user
from app.users.models import Users
from app.users.chat.router import router
class ConnectionManager(WebSocket):
def __init__(self):
self.active_connection: list[WebSocket] = []
self.active_connections: dict[int, list[WebSocket]] = {}
async def connect(self, websocket: WebSocket):
async def connect(self, chat_id: int, websocket: WebSocket):
await websocket.accept()
self.active_connection.append(websocket)
if chat_id not in self.active_connections:
self.active_connections[chat_id] = []
self.active_connections[chat_id].append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connection.remove(websocket)
def disconnect(self, chat_id: int, websocket: WebSocket):
self.active_connections[chat_id].remove(websocket)
async def broadcast(self, message: str):
for websocket in self.active_connection:
async def broadcast(self, user_id: int, chat_id: int, message: str):
await self.add_message_to_database(user_id=user_id, chat_id=chat_id, message=message)
for websocket in self.active_connections[chat_id]:
await websocket.send_text(message)
@staticmethod
async def add_message_to_database(user_id: int, chat_id: int, message: str):
result = await ChatDAO.send_message(user_id=user_id, chat_id=chat_id, message=message)
return result
manager = ConnectionManager()
@router.websocket("/ws/{chat_id}")
async def websocket_endpoint(chat_id: int, user_id: int, websocket: WebSocket):
await 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_text()
await manager.broadcast(user_id=user_id, chat_id=chat_id, message=data)
except WebSocketDisconnect:
manager.disconnect(chat_id, websocket)
@router.post("")
async def chroot():
pass

View file

@ -28,12 +28,12 @@ class UserDAO(BaseDAO):
return result.scalar()
@classmethod
async def get_user_rights(cls, user_id: int):
async def get_user_allowed_chats(cls, user_id: int):
query = select(UsersXChats.__table__.columns).where(UsersXChats.user_id == user_id)
async with async_session_maker() as session:
result = await session.execute(query)
print(result)
return result.mappings().all()
result = result.mappings().all()
return (res['chat_id'] for res in result)
@classmethod
async def get_user_avatar(cls, user_id: int):

View file

@ -6,7 +6,7 @@ from jose import JWTError, jwt
from app.config import settings
from app.exceptions import (IncorrectTokenFormatException,
TokenAbsentException, TokenExpiredException,
UserIsNotPresentException)
UserIsNotPresentException, UserDontHavePermissionException)
from app.users.dao import UserDAO
from app.users.models import Users
@ -37,5 +37,13 @@ async def get_current_user(token: str = Depends(get_token)):
return user
async def validate_user_rights(user: Users = Depends(get_current_user)): # Надо дописать
user_rights = UserDAO.get_user_rights()
async def get_user_allowed_chats(user_id: int):
user_allowed_chats = await UserDAO.get_user_allowed_chats(user_id)
return user_allowed_chats
async def validate_user_access_to_chat(user_id: int, chat_id: int):
user_allowed_chats = await get_user_allowed_chats(user_id=user_id)
if not chat_id in user_allowed_chats:
raise UserDontHavePermissionException
return True

View file

@ -15,7 +15,7 @@ class Users(Base):
hashed_password: Mapped[str]
role: Mapped[int]
black_phoenix: Mapped[int]
avatar_image: Mapped[Optional[str]] = mapped_column(server_default='app/static/images/ту уже пешка BP.png')
avatar_image: Mapped[Optional[str]] = mapped_column(server_default='app/static/images/ты уже пешка BP.png')
date_of_birth: Mapped[date]
message = relationship("Messages", back_populates="user")