Skip to content

Commit 288e6f5

Browse files
Support Bidirectional communication via Socket.IO
1 parent f507702 commit 288e6f5

6 files changed

Lines changed: 235 additions & 15 deletions

File tree

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/app.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as API from '@/api';
22
import * as Middlewares from '@/middlewares';
3-
import { ALLOWED_ORIGINS } from '@/lib/config';
3+
import { ALLOWED_ORIGINS, corsOptions } from '@/lib/config';
44
import { AppBaseError } from '@/lib/app-error';
55
import cookieParser from 'cookie-parser';
66
import logger from '@/lib/logger';
@@ -21,13 +21,7 @@ app.use(Middlewares.creationRegistrar);
2121
app.use(Middlewares.createNonAdminDataPurger());
2222

2323
logger.info('ALLOWED_ORIGINS: ', ALLOWED_ORIGINS);
24-
app.use(
25-
cors({
26-
origin: ALLOWED_ORIGINS,
27-
credentials: true, // Enable cookies and credentials
28-
optionsSuccessStatus: 200, // Align more than 204 with legacy browsers
29-
})
30-
);
24+
app.use(cors(corsOptions));
3125

3226
app.use('/api/v1', API.V1.apiRouter);
3327

@@ -38,4 +32,6 @@ app.use((req) => {
3832

3933
app.use(Middlewares.errorHandler);
4034

35+
export { app };
36+
4137
export default app;

src/lib/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CorsOptions } from 'cors';
2+
13
const ERR = 'EnvVarMissed';
24

35
if (!process.env.SECRET) console.error(`${ERR}: SECRET`);
@@ -14,3 +16,9 @@ export const MAX_FILE_SIZE_MB = Number(process.env.MAX_FILE_SIZE_MB) || 2;
1416
export const TOKEN_EXP_PERIOD = process.env.TOKEN_EXP_PERIOD ?? '3d';
1517
export const NODE_ENV = process.env.NODE_ENV;
1618
export const CI = Boolean(process.env.CI);
19+
20+
export const corsOptions: CorsOptions = {
21+
origin: ALLOWED_ORIGINS,
22+
credentials: true, // Enable cookies and credentials
23+
optionsSuccessStatus: 200, // Align more than 204 with legacy browsers
24+
};

src/lib/io.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { corsOptions } from '@/lib/config';
2+
import { AuthResponse } from '@/types';
3+
import { Server } from 'socket.io';
4+
import logger from '@/lib/logger';
5+
import db from '@/lib/db';
6+
7+
const io = new Server({ cors: corsOptions, serveClient: false });
8+
9+
io.on('connection', (socket) => {
10+
const { user } = socket.handshake.auth as Partial<AuthResponse>;
11+
12+
if (user) {
13+
void socket.join(user.id)?.catch();
14+
if (user.profile) {
15+
socket
16+
.join(user.profile.id)
17+
?.catch((error: unknown) => logger.error('Failed to join a socket room', error));
18+
}
19+
db.profile
20+
.update({ where: { userId: user.id }, data: { lastSeen: new Date() } })
21+
.then(() => logger.info(`Marked ${user.username} as seen`))
22+
.catch((error: unknown) => logger.error(`Failed to mark ${user.username} as seen`, error));
23+
}
24+
25+
const username = user?.username;
26+
const { clientsCount } = io.engine;
27+
28+
logger.info('A socket client is connected', { username, clientsCount });
29+
30+
socket.on('disconnect', () =>
31+
logger.info('A socket client is disconnected.', { username, clientsCount }),
32+
);
33+
});
34+
35+
export { io };
36+
37+
export default io;

src/server.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import logger from '@/lib/logger';
1+
import { logger } from '@/lib/logger';
2+
import { createServer } from 'http';
3+
import io from '@/lib/io';
24
import app from '@/app';
35

4-
const PORT = Number(process.env.PORT) || 3001;
6+
export const PORT = Number(process.env.PORT) || 3001;
57

6-
app.listen(PORT, (error) => {
7-
if (error) logger.error(error);
8-
logger.info(`The server is running on prot ${PORT}...`);
9-
});
8+
const httpServer = createServer(app);
9+
10+
io.attach(httpServer);
11+
12+
httpServer.on('error', (error) => logger.error(error));
13+
14+
httpServer.listen(PORT, () => logger.info(`The server is running on prot ${PORT}...`));
15+
16+
export default httpServer;

0 commit comments

Comments
 (0)