feat: add realtime with socket.io

This commit is contained in:
Divlo 2022-01-13 16:33:16 +00:00
parent 97b1d04261
commit 5643ab420f
No known key found for this signature in database
GPG Key ID: 8F9478F220CE65E9
7 changed files with 483 additions and 363 deletions

View File

@ -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
View File

@ -4,6 +4,7 @@ node_modules
# production
build
.swc
# testing
coverage

721
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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
}
})
}

View File

@ -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
}
})
}

View File

@ -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' }