mirror of
https://github.com/lorsanstand/Aether.git
synced 2026-06-19 12:05:16 +03:00
Add func delete message
This commit is contained in:
@@ -35,6 +35,10 @@ async def send_message(message: MessageCreate, user: UserModel = Depends(get_cur
|
|||||||
async def edit_message(message_update: MessageUpdate, user: UserModel = Depends(get_current_verified_user)) -> Message:
|
async def edit_message(message_update: MessageUpdate, user: UserModel = Depends(get_current_verified_user)) -> Message:
|
||||||
return await ChatService.update_message(user, message_update)
|
return await ChatService.update_message(user, message_update)
|
||||||
|
|
||||||
|
@router.delete("/message")
|
||||||
|
async def delete_message(message_id: uuid.UUID, user: UserModel = Depends(get_current_verified_user)):
|
||||||
|
await ChatService.delete_message(user, message_id)
|
||||||
|
return {"status": True, "message": "Message successfully deleted"}
|
||||||
|
|
||||||
@router.websocket("/ws")
|
@router.websocket("/ws")
|
||||||
async def websocket_endpoint(ws: WebSocket, user: UserModel = Depends(get_current_verified_user)):
|
async def websocket_endpoint(ws: WebSocket, user: UserModel = Depends(get_current_verified_user)):
|
||||||
|
|||||||
@@ -147,26 +147,41 @@ class ChatService:
|
|||||||
for user_id in user_ids:
|
for user_id in user_ids:
|
||||||
payload = {
|
payload = {
|
||||||
"user_id": str(user_id),
|
"user_id": str(user_id),
|
||||||
|
"type": "send",
|
||||||
"data": message.model_dump(mode='json')
|
"data": message.model_dump(mode='json')
|
||||||
}
|
}
|
||||||
await redis_client.publish("messenger_updates", json.dumps(payload))
|
await redis_client.publish("messenger_updates", json.dumps(payload))
|
||||||
log.debug(f"Published message for user_id: {user_id}")
|
log.debug(f"Published message for user_id: {user_id}")
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _delete_ws_message(cls, user_ids: List[int], message_id: uuid.UUID):
|
||||||
|
redis_client = await get_redis()
|
||||||
|
for user_id in user_ids:
|
||||||
|
payload = {
|
||||||
|
"user_id": str(user_id),
|
||||||
|
"type": "del",
|
||||||
|
"data": str(message_id)
|
||||||
|
}
|
||||||
|
await redis_client.publish("messenger_updates", json.dumps(payload))
|
||||||
|
log.debug(f"Published message for user_id: {user_id}")
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def update_message(cls, user: UserModel, message_update: MessageUpdate) -> Message:
|
async def update_message(cls, user: UserModel, message_update: MessageUpdate) -> Message:
|
||||||
async with async_session_maker() as session:
|
async with async_session_maker() as session:
|
||||||
message_exist = await MessageDAO.find_one_or_none(
|
message_exist = await MessageDAO.find_one_or_none(
|
||||||
session,
|
session,
|
||||||
and_(
|
MessageModel.id==message_update.id
|
||||||
MessageModel.id==message_update.id,
|
|
||||||
MessageModel.sender_id==user.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if message_exist is None:
|
if message_exist is None:
|
||||||
log.warning("Message not found", extra={"user_id": user.id, "message_id": message_update.id})
|
log.warning("Message not found", extra={"user_id": user.id, "message_id": message_update.id})
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Message not found")
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Message not found")
|
||||||
|
if message_exist.sender_id != user.id:
|
||||||
|
log.warning("User does not have access to this message",
|
||||||
|
extra={"user_id": user.id, "message_id": message_update.id})
|
||||||
|
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Message not allowed")
|
||||||
|
|
||||||
|
|
||||||
message_update_db = await MessageDAO.update(
|
message_update_db = await MessageDAO.update(
|
||||||
@@ -192,3 +207,35 @@ class ChatService:
|
|||||||
await session.commit()
|
await session.commit()
|
||||||
log.info("Message update successfully", extra={"user_id": user.id, "message_id": message_update.id})
|
log.info("Message update successfully", extra={"user_id": user.id, "message_id": message_update.id})
|
||||||
return message_update_db
|
return message_update_db
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def delete_message(cls, user: UserModel, message_id: uuid.UUID):
|
||||||
|
async with async_session_maker() as session:
|
||||||
|
message_exist = await MessageDAO.find_one_or_none(
|
||||||
|
session,
|
||||||
|
MessageModel.id==message_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if message_exist is None:
|
||||||
|
log.warning("Message not found", extra={"user_id": user.id, "message_id": message_id})
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Message not found")
|
||||||
|
if message_exist.sender_id != user.id:
|
||||||
|
log.warning("User does not have access to this message",
|
||||||
|
extra={"user_id": user.id, "message_id": message_id})
|
||||||
|
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Message not allowed")
|
||||||
|
|
||||||
|
members = await ParticipantDAO.find_all(
|
||||||
|
session,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
ParticipantModel.chat_id==message_exist.chat_id
|
||||||
|
)
|
||||||
|
member_ids = [member.user_id for member in members]
|
||||||
|
|
||||||
|
await MessageDAO.delete(session, MessageModel.id==message_id)
|
||||||
|
|
||||||
|
await cls._delete_ws_message(member_ids, message_id)
|
||||||
|
|
||||||
|
await session.commit()
|
||||||
|
log.info("Message delete successfully", extra={"user_id": user.id, "message_id": message_exist.id})
|
||||||
@@ -66,7 +66,7 @@ class Settings(BaseSettings):
|
|||||||
def RABBITMQ_URL(self) -> str:
|
def RABBITMQ_URL(self) -> str:
|
||||||
return f"amqp://{self.RMQ_USER}:{self.RMQ_PASS}@{self.RMQ_HOST}:{self.RMQ_PORT}//"
|
return f"amqp://{self.RMQ_USER}:{self.RMQ_PASS}@{self.RMQ_HOST}:{self.RMQ_PORT}//"
|
||||||
|
|
||||||
model_config = SettingsConfigDict(env_file="../.env.prod", extra="allow")
|
model_config = SettingsConfigDict(env_file="../.env", extra="allow")
|
||||||
|
|
||||||
|
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
@@ -40,12 +40,16 @@ class PubSubMessenger:
|
|||||||
|
|
||||||
handler = cls.get_handlers().get(message['channel'])
|
handler = cls.get_handlers().get(message['channel'])
|
||||||
if handler:
|
if handler:
|
||||||
await handler(payload["data"], ws)
|
await handler(payload, ws)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def handle_message(cls, message, ws: WebSocket):
|
async def handle_message(cls, payload, ws: WebSocket):
|
||||||
await ws.send_json(message)
|
log.debug("Message start sending type: %s", payload["type"])
|
||||||
|
if payload["type"] == "send":
|
||||||
|
await ws.send_json(payload["data"])
|
||||||
|
elif payload["type"] == "del":
|
||||||
|
await ws.send_json({"type": "del", "message_id": payload["data"]})
|
||||||
log.info(f"Message sent to user via WebSocket")
|
log.info(f"Message sent to user via WebSocket")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useAuthStore } from '../store/authStore';
|
|||||||
import { useChatStore } from '../store/chatStore';
|
import { useChatStore } from '../store/chatStore';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { MessageSquarePlus, Settings, User, Send, ArrowLeft, X, Pencil, Check, ArrowDown } from 'lucide-react';
|
import { MessageSquarePlus, Settings, User, Send, ArrowLeft, X, Pencil, Check, ArrowDown, Trash2 } from 'lucide-react';
|
||||||
import miniLogo from '../assets/mini-logo.png';
|
import miniLogo from '../assets/mini-logo.png';
|
||||||
import VerificationBanner from '../components/common/VerificationBanner';
|
import VerificationBanner from '../components/common/VerificationBanner';
|
||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
@@ -120,8 +120,17 @@ export default function ChatPage() {
|
|||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const message: Message = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
console.log('Received message via WebSocket:', message);
|
console.log('Received WebSocket data:', data);
|
||||||
|
|
||||||
|
// Handle message deletion
|
||||||
|
if (data.type === 'del' && data.message_id) {
|
||||||
|
setMessages(prev => prev.filter(m => m.id !== data.message_id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle regular message (add/update)
|
||||||
|
const message: Message = data;
|
||||||
|
|
||||||
// Add or update message in current chat if it belongs to it
|
// Add or update message in current chat if it belongs to it
|
||||||
if (selectedChat && message.chat_id === selectedChat.chat_id) {
|
if (selectedChat && message.chat_id === selectedChat.chat_id) {
|
||||||
@@ -429,6 +438,18 @@ export default function ChatPage() {
|
|||||||
handleCancelEdit();
|
handleCancelEdit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteMessage = async (messageId: string) => {
|
||||||
|
if (!confirm('Удалить сообщение?')) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await chatService.deleteMessage(messageId);
|
||||||
|
// Message will be removed via WebSocket
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('Failed to delete message:', err);
|
||||||
|
alert('Не удалось удалить сообщение');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen h-screen flex" style={{ backgroundColor: 'var(--bg-primary)' }}>
|
<div className="min-h-screen h-screen flex" style={{ backgroundColor: 'var(--bg-primary)' }}>
|
||||||
@@ -817,16 +838,26 @@ export default function ChatPage() {
|
|||||||
border: isMyMessage ? 'none' : '1px solid var(--border-color)'
|
border: isMyMessage ? 'none' : '1px solid var(--border-color)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Edit button - показываем только для своих сообщений */}
|
{/* Edit and Delete buttons - показываем только для своих сообщений */}
|
||||||
{isMyMessage && editingMessageId !== message.id && (
|
{isMyMessage && editingMessageId !== message.id && (
|
||||||
<button
|
<>
|
||||||
onClick={() => handleStartEdit(message)}
|
<button
|
||||||
className="absolute -top-2 -right-2 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md"
|
onClick={() => handleStartEdit(message)}
|
||||||
style={{ backgroundColor: 'var(--bg-card)', color: 'var(--accent-primary)' }}
|
className="absolute -top-2 -right-10 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md"
|
||||||
title="Редактировать"
|
style={{ backgroundColor: 'var(--bg-card)', color: 'var(--accent-primary)' }}
|
||||||
>
|
title="Редактировать"
|
||||||
<Pencil size={14} />
|
>
|
||||||
</button>
|
<Pencil size={14} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteMessage(message.id)}
|
||||||
|
className="absolute -top-2 -right-2 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity shadow-md"
|
||||||
|
style={{ backgroundColor: 'var(--bg-card)', color: '#DC2626' }}
|
||||||
|
title="Удалить"
|
||||||
|
>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Message content or edit input */}
|
{/* Message content or edit input */}
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ const chatService = {
|
|||||||
async updateMessage(data: MessageUpdate): Promise<Message> {
|
async updateMessage(data: MessageUpdate): Promise<Message> {
|
||||||
const response = await apiClient.put('/chats/message', data);
|
const response = await apiClient.put('/chats/message', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteMessage(messageId: string): Promise<void> {
|
||||||
|
await apiClient.delete('/chats/message', {
|
||||||
|
params: { message_id: messageId }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user