Добавлена минимальная работа с вебсокетом чата
This commit is contained in:
parent
f2b813300d
commit
355415e819
12 changed files with 168 additions and 44 deletions
|
@ -54,7 +54,7 @@ class IncorrectLengthOfNicknameException(BlackPhoenixException):
|
|||
detail = "Ник должен быть не короче 2 и не длиннее 30 символов"
|
||||
|
||||
|
||||
class UDontHavePermissionException(BlackPhoenixException):
|
||||
class UserDontHavePermissionException(BlackPhoenixException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = "У вас нет прав для этого действия"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 ###
|
|
@ -12,3 +12,11 @@ templates = Jinja2Templates(directory="app/templates")
|
|||
@router.get("/base")
|
||||
async def base(request: Request):
|
||||
return templates.TemplateResponse("base.html", {"request": request})
|
||||
|
||||
|
||||
@router.get("/chat")
|
||||
def get_chat_page(request: Request):
|
||||
return templates.TemplateResponse("chat.html", {"request": request})
|
||||
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
@ -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=1`);
|
||||
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 %}
|
|
@ -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()
|
||||
|
|
|
@ -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():
|
||||
print(await validate_user_access_to_chat(1, 2))
|
||||
pass
|
||||
|
||||
|
||||
@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}: себался")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Reference in a new issue