Files
chad/server/plugins/socket.ts
2026-04-12 22:35:47 +06:00

116 lines
3.2 KiB
TypeScript

import type { FastifyInstance } from 'fastify'
import type { ServerOptions } from 'socket.io'
import type { ChadClient } from '../types/socket.ts'
import { consola } from 'consola'
import fp from 'fastify-plugin'
import { Server } from 'socket.io'
import prisma from '../prisma/client.ts'
import registerChannelHandlers from '../socket/channel.ts'
import registerWebrtcHandlers from '../socket/webrtc.ts'
declare module 'fastify' {
interface FastifyInstance {
io: Server
}
}
export default fp<Partial<ServerOptions>>(
async (fastify, opts) => {
fastify.decorate('io', new Server(fastify.server, opts))
fastify.addHook('preClose', () => {
fastify.io.disconnectSockets(true)
})
fastify.addHook('onClose', async (fastify: FastifyInstance) => {
await fastify.io.close()
})
fastify.ready(() => {
const audioLevelObserver = await fastify.mediasoupRouter.createAudioLevelObserver({
maxEntries: 10,
threshold: -80,
interval: 800,
})
const activeSpeakerObserver = await fastify.mediasoupRouter.createActiveSpeakerObserver()
audioLevelObserver.on('volumes', async (volumes: types.AudioLevelObserverVolume[]) => {
fastify.io.emit('webrtc:speaking-peers', volumes.map(({ producer, volume }) => {
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
return {
clientId: socketId,
volume,
}
}))
})
audioLevelObserver.on('silence', () => {
fastify.io.emit('webrtc:speaking-peers', [])
fastify.io.emit('webrtc:active-speaker', undefined)
})
activeSpeakerObserver.on('dominantspeaker', ({ producer }) => {
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
fastify.io.emit('webrtc:active-speaker', socketId)
})
fastify.io.on('connection', async (socket) => {
consola.info('New connection', socket.id)
const user = await prisma.user.findUnique({
where: {
id: socket.handshake.auth.userId,
},
select: {
id: true,
username: true,
displayName: true,
},
})
if (!user) {
socket.disconnect()
return
}
const { id, username, displayName } = user
socket.data.userId = id
socket.data.username = username
socket.data.displayName = displayName
consola.info('User authorized', ...Object.values(user))
const channel = await registerChannelHandlers(fastify.io, socket)
const webrtc = await registerWebrtcHandlers(
fastify.io,
socket,
fastify.mediasoupRouter,
audioLevelObserver,
activeSpeakerObserver,
)
socket.emit('webrtc:authenticated', {
...channel,
...webrtc,
rtpCapabilities: fastify.mediasoupRouter.rtpCapabilities,
})
})
})
},
{ name: 'socket-io', dependencies: ['mediasoup-worker', 'mediasoup-router'] },
)
export const autoConfig: Partial<ServerOptions> = {
path: '/chad/ws',
cors: {
origin: process.env.CORS_ORIGIN || '*',
methods: ['GET', 'POST'],
credentials: true,
},
}