Skip to content

Commit 10d40e6

Browse files
Merge PR: real-time chats via Socket.IO
2 parents f507702 + d7cfbb6 commit 10d40e6

File tree

10 files changed

+348
-32
lines changed

10 files changed

+348
-32
lines changed

package-lock.json

Lines changed: 172 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"passport-jwt": "^4.0.1",
6161
"passport-local": "^1.0.0",
6262
"sharp": "^0.34.2",
63+
"socket.io": "^4.8.3",
6364
"winston": "^3.17.0",
6465
"zod": "^3.24.3"
6566
},

src/api/v1/chats/chats.router.ts

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as Storage from '@/lib/storage';
66
import * as Service from './chats.service';
77
import * as Middlewares from '@/middlewares';
88
import { Request, Response, Router } from 'express';
9+
import logger from '@/lib/logger';
910

1011
export const chatsRouter = Router();
1112

@@ -28,32 +29,53 @@ chatsRouter.get('/', Middlewares.authValidator, async (req, res) => {
2829
const userId = Utils.getCurrentUserIdFromReq(req)!;
2930
const filters = Utils.getBasePaginationFiltersFromReqQuery(req);
3031
const chats = await Service.getUserChats(userId, filters);
31-
res.json(chats);
32+
res.json(chats).on('finish', () => {
33+
const rooms = Service.getOtherChatsMemberIds(userId, chats);
34+
Utils.emitToRoomsIfAny({ rooms, event: 'chats:received', volatile: true });
35+
});
3236
});
3337

3438
chatsRouter.get('/members/:profileId', Middlewares.authValidator, async (req, res) => {
3539
const userId = Utils.getCurrentUserIdFromReq(req)!;
3640
const chats = await Service.getUserChatsByMember(userId, req.params.profileId);
37-
res.json(chats);
41+
res.json(chats).on('finish', () => {
42+
const rooms = Service.getOtherChatsMemberIds(userId, chats);
43+
Utils.emitToRoomsIfAny({ rooms, event: 'chats:received', volatile: true });
44+
});
3845
});
3946

4047
chatsRouter.get('/:id', Middlewares.authValidator, async (req, res) => {
48+
const chatId = req.params.id;
4149
const userId = Utils.getCurrentUserIdFromReq(req)!;
42-
const chat = await Service.getUserChatById(userId, req.params.id);
43-
res.json(chat);
50+
const chat = await Service.getUserChatById(userId, chatId);
51+
res.json(chat).on('finish', () => {
52+
Service.getOtherChatMemberIds(userId, chatId)
53+
.then((rooms) => Utils.emitToRoomsIfAny({ rooms, event: 'chats:received', volatile: true }))
54+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:received"', error));
55+
});
4456
});
4557

4658
chatsRouter.get('/:id/messages', Middlewares.authValidator, async (req, res) => {
59+
const chatId = req.params.id;
4760
const userId = Utils.getCurrentUserIdFromReq(req)!;
4861
const filters = Utils.getBasePaginationFiltersFromReqQuery(req);
49-
const messages = await Service.getUserChatMessages(userId, req.params.id, filters);
50-
res.json(messages);
62+
const messages = await Service.getUserChatMessages(userId, chatId, filters);
63+
res.json(messages).on('finish', () => {
64+
Service.getOtherChatMemberIds(userId, chatId)
65+
.then((rooms) => Utils.emitToRoomsIfAny({ rooms, event: 'chats:received', volatile: true }))
66+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:received"', error));
67+
});
5168
});
5269

5370
chatsRouter.get('/:id/messages/:msgId', Middlewares.authValidator, async (req, res) => {
71+
const chatId = req.params.id;
5472
const userId = Utils.getCurrentUserIdFromReq(req)!;
55-
const msg = await Service.getUserChatMessageById(userId, req.params.id, req.params.msgId);
56-
res.json(msg);
73+
const msg = await Service.getUserChatMessageById(userId, chatId, req.params.msgId);
74+
res.json(msg).on('finish', () => {
75+
Service.getOtherChatMemberIds(userId, chatId)
76+
.then((rooms) => Utils.emitToRoomsIfAny({ rooms, event: 'chats:received', volatile: true }))
77+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:received"', error));
78+
});
5779
});
5880

5981
chatsRouter.post(
@@ -67,33 +89,65 @@ chatsRouter.post(
6789
);
6890
const preparedImageData = await prepareImageData(req, chatData.message.imagedata, user);
6991
const createdChat = await Service.createChat(user, chatData, ...preparedImageData);
70-
res.status(201).json(createdChat);
92+
res
93+
.status(201)
94+
.json(createdChat)
95+
.on('finish', () => {
96+
Service.getOtherChatMemberIds(user.id, createdChat.id)
97+
.then((rooms) =>
98+
Utils.emitToRoomsIfAny({ rooms, event: 'chats:updated', volatile: true }),
99+
)
100+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:updated"', error));
101+
});
71102
},
72103
);
73104

74105
chatsRouter.patch('/:id/seen', Middlewares.authValidator, async (req, res) => {
106+
const chatId = req.params.id;
75107
const userId = Utils.getCurrentUserIdFromReq(req)!;
76-
res.json(await Service.updateProfileChatLastSeenDate(userId, req.params.id));
108+
res.json(await Service.updateProfileChatLastSeenDate(userId, chatId)).on('finish', () => {
109+
Service.getOtherChatMemberIds(userId, chatId)
110+
.then((rooms) => Utils.emitToRoomsIfAny({ rooms, event: 'chats:updated', volatile: true }))
111+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:updated"', error));
112+
});
77113
});
78114

79115
chatsRouter.post(
80116
'/:id/messages',
81117
Middlewares.authValidator,
82118
Middlewares.createFileProcessor('image'),
83119
async (req: Request, res: Response) => {
120+
const chatId = req.params.id;
84121
const user = Utils.getCurrentUserFromReq(req)!;
85122
const { imagedata, ...msgData } = (
86123
req.file ? Schema.optionalMessageSchema : Schema.messageSchema
87124
).parse(req.body);
88125
const preparedImageData = await prepareImageData(req, imagedata, user);
89-
const msgArgs = [user, req.params.id, msgData, ...preparedImageData] as const;
126+
const msgArgs = [user, chatId, msgData, ...preparedImageData] as const;
90127
const createdMessage = await Service.createUserChatMessage(...msgArgs);
91-
res.status(201).json(createdMessage);
128+
res
129+
.status(201)
130+
.json(createdMessage)
131+
.on('finish', () => {
132+
Service.getOtherChatMemberIds(user.id, chatId)
133+
.then((rooms) =>
134+
Utils.emitToRoomsIfAny({ rooms, event: 'chats:updated', volatile: true }),
135+
)
136+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:updated"', error));
137+
});
92138
},
93139
);
94140

95141
chatsRouter.delete('/:id', Middlewares.authValidator, async (req, res) => {
142+
const chatId = req.params.id;
96143
const userId = Utils.getCurrentUserIdFromReq(req)!;
97-
await Service.deleteChat(userId, req.params.id);
98-
res.status(204).send();
144+
await Service.deleteChat(userId, chatId);
145+
res
146+
.status(204)
147+
.send()
148+
.on('finish', () => {
149+
Service.getOtherChatMemberIds(userId, chatId)
150+
.then((rooms) => Utils.emitToRoomsIfAny({ rooms, event: 'chats:updated', volatile: true }))
151+
.catch((error: unknown) => logger.error('Failed to broadcast "chats:updated"', error));
152+
});
99153
});

0 commit comments

Comments
 (0)