aethex.live/server.js

128 lines
3.5 KiB
JavaScript

/**
* Custom server for Socket.io integration with Next.js
* Run this with `node server.js` for real-time chat support
*/
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const { Server } = require('socket.io')
const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = parseInt(process.env.PORT, 10) || 3000
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const httpServer = createServer((req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
})
// Setup Socket.io on the HTTP server
const io = new Server(httpServer, {
cors: {
origin: process.env.NEXT_PUBLIC_APP_URL || `http://${hostname}:${port}`,
methods: ['GET', 'POST'],
credentials: true,
},
})
// Import Prisma Client
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
io.on('connection', (socket) => {
console.log('Client connected:', socket.id)
// Join a stream room
socket.on('join:stream', ({ streamId, userId }) => {
console.log(`User ${userId} joined stream ${streamId}`)
socket.join(`stream:${streamId}`)
// Broadcast viewer count
const roomSize = io.sockets.adapter.rooms.get(`stream:${streamId}`)?.size || 0
io.to(`stream:${streamId}`).emit('viewers:update', {
streamId,
viewerCount: roomSize,
})
// Update DB
prisma.stream
.update({
where: { id: streamId },
data: { viewerCount: roomSize },
})
.catch(console.error)
})
// Leave stream
socket.on('leave:stream', ({ streamId, userId }) => {
socket.leave(`stream:${streamId}`)
const roomSize = io.sockets.adapter.rooms.get(`stream:${streamId}`)?.size || 0
io.to(`stream:${streamId}`).emit('viewers:update', {
streamId,
viewerCount: roomSize,
})
prisma.stream
.update({
where: { id: streamId },
data: { viewerCount: roomSize },
})
.catch(console.error)
})
// Chat message
socket.on('chat:message', async ({ streamId, userId, message }) => {
try {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
displayName: true,
avatarUrl: true,
verified: true,
isCreator: true,
},
})
if (!user) return socket.emit('chat:error', { message: 'User not found' })
const chatMessage = await prisma.chatMessage.create({
data: {
streamId,
userId,
message: message.trim(),
badges: user.isCreator ? ['creator'] : [],
},
})
io.to(`stream:${streamId}`).emit('chat:message', {
id: chatMessage.id,
message: chatMessage.message,
badges: chatMessage.badges,
user: {
id: user.id,
displayName: user.displayName,
avatarUrl: user.avatarUrl,
verified: user.verified,
},
createdAt: chatMessage.createdAt,
})
} catch (error) {
console.error('Chat error:', error)
}
})
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id)
})
})
httpServer.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`)
console.log(`> Socket.io server running`)
})
})