Skip to content

Commit 5b61707

Browse files
authored
Merge pull request #19 from azeddine-hmd/dev
fix websocket connects on all pages; adding wrapper for socket listeners
2 parents b458ca1 + fc5d7f8 commit 5b61707

File tree

13 files changed

+2318
-1645
lines changed

13 files changed

+2318
-1645
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ docker compose up --build
1313
## Technologies
1414
*backend:*
1515
* Typescript
16-
* Expressjs 5-beta (async/await)
16+
* Expressjs 5-beta (async/await handlers)
1717
* prisma (orm/migrations)
1818
* postgresql (database)
19-
* Socket.io (advanced websocket)
19+
* Socket.io (websocket)
2020

2121
*frontend:*
2222
* Typescript
23-
* Nextjs 13
24-
* Server/Client component
25-
* Tailwindcss
23+
* Nextjs 13 (Server/Client component)
24+
* Tailwindcss (styling)
2625
* Framer-Motion (animation)
27-
* React-Query (managing network state/caching etc)
28-
* Axios (http request)
26+
* Axios (http client)
27+
* React-Query (network fetching)
28+
* zustand (state management)
2929

3030
## Example :
3131
Check hosted webserver on: [Chichat | talk and hang out with friends](https://chichat.azeddine.xyz)

backend/src/sockets/index.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { COOKIE_MAX_AGE_MILIS } from '../constants';
44
import { corsOptions, pool, prisma } from '../config';
55
import { UserStatus } from '@prisma/client';
66
import { createAdapter } from '@socket.io/postgres-adapter';
7+
import { listenerWrapper } from './listener-wrapper';
78

89
export const io = new Server(server, {
910
cookie: {
@@ -19,38 +20,43 @@ export const io = new Server(server, {
1920

2021
io.adapter(createAdapter(pool));
2122

22-
io.on('connection', async (socket) => {
23-
// handling user status
24-
console.log(`client(id=${socket.user.id}, sid=${socket.id}) connected!`);
25-
await prisma.$transaction([
26-
prisma.user.update({
27-
where: { id: socket.user.id },
28-
data: { status: UserStatus.ONLINE },
29-
}),
30-
prisma.userSockets.create({
31-
data: {
32-
socketId: socket.id,
33-
user: { connect: { id: socket.user.id } },
34-
},
35-
}),
36-
]);
37-
38-
socket.on('disconnect', async () => {
39-
console.log(`client(id=${socket.user.id}, sid=${socket.id}) disconnected!`);
40-
const [_, userSocketsCount] = await prisma.$transaction([
41-
prisma.userSockets.delete({
42-
where: { socketId: socket.id },
23+
io.on(
24+
'connection',
25+
listenerWrapper(async (socket) => {
26+
// handling user status
27+
console.log(`client(id=${socket.user.id}, sid=${socket.id}) connected!`);
28+
await prisma.$transaction([
29+
prisma.user.update({
30+
where: { id: socket.user.id },
31+
data: { status: UserStatus.ONLINE },
32+
}),
33+
prisma.userSockets.create({
34+
data: {
35+
socketId: socket.id,
36+
user: { connect: { id: socket.user.id } },
37+
},
4338
}),
44-
prisma.userSockets.count({ where: { userId: socket.user.id } }),
4539
]);
46-
if (userSocketsCount == 0) {
47-
await prisma.user.update({
48-
where: { id: socket.user.id },
49-
data: { status: UserStatus.OFFLINE },
50-
});
51-
}
52-
});
53-
});
40+
41+
socket.on('disconnect', async () => {
42+
console.log(
43+
`client(id=${socket.user.id}, sid=${socket.id}) disconnected!`
44+
);
45+
const [_, userSocketsCount] = await prisma.$transaction([
46+
prisma.userSockets.delete({
47+
where: { socketId: socket.id },
48+
}),
49+
prisma.userSockets.count({ where: { userId: socket.user.id } }),
50+
]);
51+
if (userSocketsCount == 0) {
52+
await prisma.user.update({
53+
where: { id: socket.user.id },
54+
data: { status: UserStatus.OFFLINE },
55+
});
56+
}
57+
});
58+
})
59+
);
5460

5561
import './middlewares';
5662
import './namespaces';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Socket } from 'socket.io';
2+
3+
export const listenerWrapper = (handler: (socket: Socket) => Promise<void>) => {
4+
return (socket: Socket) => {
5+
handler(socket).catch((error) => {
6+
console.error('Error in socket: ', error);
7+
socket.disconnect();
8+
});
9+
};
10+
};
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import { Socket } from 'socket.io';
12
import { io } from '..';
3+
import { listenerWrapper } from '../listener-wrapper';
24

3-
io.on('friendsPresence', () => {
4-
console.log('subscribe to friendsPresence');
5-
});
5+
io.on(
6+
'friendsPresence',
7+
listenerWrapper(async (socket: Socket) => {
8+
throw new Error('FriendsPresence');
9+
})
10+
);

backend/src/sockets/middlewares.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { io } from '.';
22
import helmet from 'helmet';
33
import cookie from 'cookie';
4-
import { verifyJwt } from '../config';
4+
import { prisma, verifyJwt } from '../config';
55
import { sessionMiddleware } from '../api/middlewares/express-session';
66
import { Socket } from 'socket.io';
77

@@ -11,11 +11,28 @@ io.engine.use(helmet());
1111
function authentication(socket: Socket, next: (err?: Error) => void) {
1212
const cookies = cookie.parse(socket.handshake.headers.cookie);
1313
const jwtKey = process.env.JWT_COOKIE_NAME;
14-
if (!cookies[jwtKey]) next(new Error('auth token not found'));
14+
if (!cookies[jwtKey]) {
15+
next(new Error('auth token not found'));
16+
socket.disconnect();
17+
}
1518
const payload = verifyJwt(cookies[jwtKey]);
16-
if (typeof payload === 'string') next(new Error('invalid payload'));
17-
else {
18-
socket.user = { id: payload.id, username: payload.username };
19+
if (typeof payload === 'string') {
20+
next(new Error('invalid payload'));
21+
socket.disconnect();
22+
} else {
23+
prisma.user
24+
.findFirstOrThrow({
25+
where: { id: payload.id, username: payload.username },
26+
select: { id: true, username: true },
27+
})
28+
.then((user) => {
29+
socket.user = user;
30+
})
31+
.catch((error) => {
32+
console.error('socket:authentication: ', error);
33+
next(new Error('User not found'));
34+
socket.disconnect();
35+
});
1936
next();
2037
}
2138
}

frontend/app/(auth)/login/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export default function Login() {
123123
</PrimaryDotLoadingButton>
124124
</form>
125125
<div className="mt-4 w-full text-sm text-muted">
126-
<h1 className="inline ">Need an account? </h1>
126+
<h1 className="inline text-muted-field">Need an account? </h1>
127127
<Link
128128
href="/register"
129129
className="inline cursor-pointer text-sm text-link hover:underline"

0 commit comments

Comments
 (0)