feat: add realtime with socket.io
This commit is contained in:
		| @@ -12,7 +12,10 @@ | ||||
|     "prettier/prettier": "error", | ||||
|     "import/order": [ | ||||
|       "error", | ||||
|       { "groups": ["builtin", "external", "internal"] } | ||||
|       { | ||||
|         "groups": ["builtin", "external", "internal"], | ||||
|         "newlines-between": "always" | ||||
|       } | ||||
|     ], | ||||
|     "import/extensions": ["error", "always"], | ||||
|     "unicorn/prefer-node-protocol": "error", | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ node_modules | ||||
|  | ||||
| # production | ||||
| build | ||||
| .swc | ||||
|  | ||||
| # testing | ||||
| coverage | ||||
|   | ||||
							
								
								
									
										721
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										721
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										25
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								package.json
									
									
									
									
									
								
							| @@ -18,7 +18,7 @@ | ||||
|     "generate": "plop", | ||||
|     "lint:commit": "commitlint", | ||||
|     "lint:editorconfig": "editorconfig-checker", | ||||
|     "lint:markdown": "markdownlint '**/*.md' --dot --ignore 'node_modules'", | ||||
|     "lint:markdown": "markdownlint '**/*.md' --dot --ignore-path '.gitignore'", | ||||
|     "lint:typescript": "eslint '**/*.{js,ts,jsx,tsx}'", | ||||
|     "lint:prettier": "prettier '.' --check", | ||||
|     "lint:staged": "lint-staged", | ||||
| @@ -31,21 +31,22 @@ | ||||
|     "postinstall": "husky install" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@prisma/client": "3.7.0", | ||||
|     "@prisma/client": "3.8.0", | ||||
|     "@sinclair/typebox": "0.23.2", | ||||
|     "@thream/socketio-jwt": "2.1.1", | ||||
|     "axios": "0.24.0", | ||||
|     "bcryptjs": "2.4.3", | ||||
|     "dotenv": "10.0.0", | ||||
|     "dotenv": "11.0.0", | ||||
|     "ejs": "3.1.6", | ||||
|     "fastify": "3.25.3", | ||||
|     "fastify-cors": "6.0.2", | ||||
|     "fastify-helmet": "5.3.2", | ||||
|     "fastify-helmet": "6.0.0", | ||||
|     "fastify-multipart": "5.2.1", | ||||
|     "fastify-plugin": "3.0.0", | ||||
|     "fastify-rate-limit": "5.7.0", | ||||
|     "fastify-sensible": "3.1.2", | ||||
|     "fastify-static": "4.5.0", | ||||
|     "fastify-swagger": "4.13.0", | ||||
|     "fastify-swagger": "4.13.1", | ||||
|     "fastify-url-data": "3.0.3", | ||||
|     "http-errors": "2.0.0", | ||||
|     "jsonwebtoken": "8.5.1", | ||||
| @@ -55,18 +56,18 @@ | ||||
|     "socket.io": "4.4.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@commitlint/cli": "16.0.1", | ||||
|     "@commitlint/cli": "16.0.2", | ||||
|     "@commitlint/config-conventional": "16.0.0", | ||||
|     "@saithodev/semantic-release-backmerge": "2.1.0", | ||||
|     "@swc/cli": "0.1.55", | ||||
|     "@swc/core": "1.2.127", | ||||
|     "@swc/jest": "0.2.15", | ||||
|     "@swc/core": "1.2.129", | ||||
|     "@swc/jest": "0.2.17", | ||||
|     "@types/bcryptjs": "2.4.2", | ||||
|     "@types/busboy": "0.3.1", | ||||
|     "@types/busboy": "1.3.0", | ||||
|     "@types/ejs": "3.1.0", | ||||
|     "@types/http-errors": "1.8.1", | ||||
|     "@types/jest": "27.4.0", | ||||
|     "@types/jsonwebtoken": "8.5.6", | ||||
|     "@types/jsonwebtoken": "8.5.7", | ||||
|     "@types/ms": "0.7.31", | ||||
|     "@types/node": "17.0.8", | ||||
|     "@types/nodemailer": "6.4.4", | ||||
| @@ -86,12 +87,12 @@ | ||||
|     "jest": "27.4.7", | ||||
|     "jest-mock-extended": "2.0.4", | ||||
|     "jest-ts-webcompat-resolver": "1.0.0", | ||||
|     "lint-staged": "12.1.5", | ||||
|     "lint-staged": "12.1.7", | ||||
|     "markdownlint-cli": "0.30.0", | ||||
|     "nodemon": "2.0.15", | ||||
|     "plop": "3.0.5", | ||||
|     "prettier": "2.5.1", | ||||
|     "prisma": "3.7.0", | ||||
|     "prisma": "3.8.0", | ||||
|     "rimraf": "3.0.2", | ||||
|     "semantic-release": "18.0.1", | ||||
|     "typescript": "4.5.4" | ||||
|   | ||||
| @@ -102,7 +102,7 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|         } | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       return { | ||||
|       const item = { | ||||
|         ...message, | ||||
|         member: { | ||||
|           ...memberCheck, | ||||
| @@ -112,6 +112,12 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'create', item } | ||||
|       }) | ||||
|       return item | ||||
|     } | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -107,7 +107,7 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|         } | ||||
|       }) | ||||
|       reply.statusCode = 201 | ||||
|       return { | ||||
|       const item = { | ||||
|         ...message, | ||||
|         member: { | ||||
|           ...memberCheck, | ||||
| @@ -117,6 +117,12 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       await fastify.io.emitToMembers({ | ||||
|         event: 'messages', | ||||
|         guildId: item.member.guildId, | ||||
|         payload: { action: 'create', item } | ||||
|       }) | ||||
|       return item | ||||
|     } | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,18 +1,88 @@ | ||||
| import fastifyPlugin from 'fastify-plugin' | ||||
| import { Server as SocketIoServer, ServerOptions } from 'socket.io' | ||||
| import { authorize } from '@thream/socketio-jwt' | ||||
|  | ||||
| import prisma from '../database/prisma.js' | ||||
| import { JWT_ACCESS_SECRET } from '../configurations/index.js' | ||||
|  | ||||
| interface EmitEventOptions { | ||||
|   event: string | ||||
|   payload: { | ||||
|     action: 'create' | 'delete' | 'update' | ||||
|     item: object | ||||
|   } | ||||
| } | ||||
|  | ||||
| interface EmitToAuthorizedUsersOptions extends EmitEventOptions { | ||||
|   /** tests whether the current connected userId is authorized to get the event, if the callback returns true, the server will emit the event to that user */ | ||||
|   isAuthorizedCallback: (userId: number) => Promise<boolean> | ||||
| } | ||||
|  | ||||
| type EmitToAuthorizedUsers = ( | ||||
|   options: EmitToAuthorizedUsersOptions | ||||
| ) => Promise<void> | ||||
|  | ||||
| interface EmitToMembersOptions extends EmitEventOptions { | ||||
|   guildId: number | ||||
| } | ||||
|  | ||||
| type EmitToMembers = (options: EmitToMembersOptions) => Promise<void> | ||||
|  | ||||
| interface FastifyIo { | ||||
|   instance: SocketIoServer | ||||
|   emitToAuthorizedUsers: EmitToAuthorizedUsers | ||||
|   emitToMembers: EmitToMembers | ||||
| } | ||||
|  | ||||
| declare module 'fastify' { | ||||
|   export interface FastifyInstance { | ||||
|     io: SocketIoServer | ||||
|     io: FastifyIo | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default fastifyPlugin( | ||||
|   async (fastify, options: Partial<ServerOptions>) => { | ||||
|     const socket = new SocketIoServer(fastify.server, options) | ||||
|     fastify.decorate('io', socket) | ||||
|     const instance = new SocketIoServer(fastify.server, options) | ||||
|     instance.use( | ||||
|       authorize({ | ||||
|         secret: JWT_ACCESS_SECRET | ||||
|       }) | ||||
|     ) | ||||
|     const emitToAuthorizedUsers: EmitToAuthorizedUsers = async (options) => { | ||||
|       const { event, payload, isAuthorizedCallback } = options | ||||
|       const clients = await instance.sockets.allSockets() | ||||
|       for (const clientId of clients) { | ||||
|         const client = instance.sockets.sockets.get(clientId) | ||||
|         if (client != null) { | ||||
|           const userId = client.decodedToken.id | ||||
|           const isAuthorized = await isAuthorizedCallback(userId) | ||||
|           if (isAuthorized) { | ||||
|             client.emit(event, payload) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     const emitToMembers: EmitToMembers = async (options) => { | ||||
|       const { event, payload, guildId } = options | ||||
|       await emitToAuthorizedUsers({ | ||||
|         event, | ||||
|         payload, | ||||
|         isAuthorizedCallback: async (userId) => { | ||||
|           const memberCount = await prisma.member.count({ | ||||
|             where: { userId, guildId } | ||||
|           }) | ||||
|           return memberCount > 0 | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|     const io: FastifyIo = { | ||||
|       instance, | ||||
|       emitToAuthorizedUsers, | ||||
|       emitToMembers | ||||
|     } | ||||
|     fastify.decorate('io', io) | ||||
|     fastify.addHook('onClose', async (fastify) => { | ||||
|       fastify.io.close() | ||||
|       fastify.io.instance.close() | ||||
|     }) | ||||
|   }, | ||||
|   { fastify: '3.x' } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user