cringe sfx
This commit is contained in:
255
server/routes/channels.ts
Normal file
255
server/routes/channels.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { z } from 'zod'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { channelPublicSelect } from '../dto/channel.dto.ts'
|
||||
|
||||
export default function (fastify: FastifyInstance) {
|
||||
// GET /chad/channels - List all channels with client counts
|
||||
fastify.get('/channels', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
const channels = await prisma.channel.findMany({
|
||||
select: channelPublicSelect,
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
|
||||
// Add client count to each channel using Socket.IO rooms
|
||||
const channelsWithCounts = await Promise.all(
|
||||
channels.map(async channel => {
|
||||
const socketsInChannel = await fastify.io.in(channel.id).fetchSockets()
|
||||
return {
|
||||
...channel,
|
||||
clientCount: socketsInChannel.length,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
return channelsWithCounts
|
||||
})
|
||||
|
||||
// GET /chad/channels/:id - Get specific channel with client list
|
||||
fastify.get('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
const channel = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
if (!channel) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
// Get clients in this channel using Socket.IO rooms
|
||||
const sockets = await fastify.io.in(params.id).fetchSockets()
|
||||
const clients = sockets
|
||||
.filter(s => s.data.joined)
|
||||
.map(s => {
|
||||
const channelId = Array.from(s.rooms).find(room => room !== s.id) || 'default'
|
||||
return {
|
||||
socketId: s.id,
|
||||
userId: s.data.userId,
|
||||
username: s.data.username,
|
||||
displayName: s.data.displayName,
|
||||
inputMuted: s.data.inputMuted,
|
||||
outputMuted: s.data.outputMuted,
|
||||
currentChannelId: channelId,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...channel,
|
||||
clients,
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// POST /chad/channels - Create channel
|
||||
fastify.post('/channels', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const schema = z.object({
|
||||
name: z.string().min(1).max(50),
|
||||
maxClients: z.number().int().positive().optional().nullable(),
|
||||
persistent: z.boolean().default(false),
|
||||
})
|
||||
const input = schema.parse(req.body)
|
||||
|
||||
const channel = await prisma.channel.create({
|
||||
data: {
|
||||
name: input.name,
|
||||
maxClients: input.maxClients,
|
||||
persistent: input.persistent,
|
||||
owner_id: req.user.id,
|
||||
},
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
// Notify all connected clients about new channel
|
||||
fastify.io.emit('channelCreated', channel)
|
||||
|
||||
return channel
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// PATCH /chad/channels/:id - Update channel
|
||||
fastify.patch('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(1).max(50).optional(),
|
||||
maxClients: z.number().int().positive().optional().nullable(),
|
||||
})
|
||||
const input = schema.parse(req.body)
|
||||
|
||||
// Cannot update default channel
|
||||
if (params.id === 'default') {
|
||||
return reply.code(403).send({ error: 'Cannot modify default channel' })
|
||||
}
|
||||
|
||||
const existing = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
if (!existing) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
if (existing.owner_id !== req.user.id) {
|
||||
return reply.code(403).send({ error: 'Not channel owner' })
|
||||
}
|
||||
|
||||
const channel = await prisma.channel.update({
|
||||
where: { id: params.id },
|
||||
data: input,
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
fastify.io.emit('channelUpdated', channel)
|
||||
|
||||
return channel
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// DELETE /chad/channels/:id - Delete channel
|
||||
fastify.delete('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
if (params.id === 'default') {
|
||||
return reply.code(403).send({ error: 'Cannot delete default channel' })
|
||||
}
|
||||
|
||||
const existing = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
if (!existing) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
if (existing.owner_id !== req.user.id) {
|
||||
return reply.code(403).send({ error: 'Not channel owner' })
|
||||
}
|
||||
|
||||
// Move all users in this channel back to default using Socket.IO rooms
|
||||
const sockets = await fastify.io.in(params.id).fetchSockets()
|
||||
|
||||
for (const socket of sockets) {
|
||||
// Close all their producers
|
||||
for (const producer of socket.data.producers.values()) {
|
||||
producer.close()
|
||||
}
|
||||
socket.data.producers.clear()
|
||||
|
||||
// Close all their consumers
|
||||
for (const consumer of socket.data.consumers.values()) {
|
||||
consumer.close()
|
||||
}
|
||||
socket.data.consumers.clear()
|
||||
|
||||
// Move to default room
|
||||
await socket.leave(params.id)
|
||||
await socket.join('default')
|
||||
|
||||
socket.emit('forcedChannelSwitch', { channelId: 'default' })
|
||||
}
|
||||
|
||||
await prisma.channel.delete({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
fastify.io.emit('channelDeleted', { channelId: params.id })
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user