116 lines
3.2 KiB
TypeScript
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,
|
|
},
|
|
}
|