import { Router } from 'express'; import { z } from 'zod'; import { db } from '../db/index.js'; import { connections } from '../db/schema.js'; import { eq, and } from 'drizzle-orm'; import { authenticateHttp, AuthenticatedRequest } from '../auth/index.js'; import { connectionManager } from '../irc/connection-manager.js'; const router = Router(); router.use(authenticateHttp); const createConnectionSchema = z.object({ name: z.string().min(1).max(128), hostname: z.string().min(1).max(255), port: z.number().int().min(1).max(65535).default(6667), tls: z.boolean().default(false), nick: z.string().min(1).max(64), username: z.string().max(64).nullable().optional(), realname: z.string().max(255).nullable().optional(), password: z.string().nullable().optional(), saslUsername: z.string().max(128).nullable().optional(), saslPassword: z.string().nullable().optional(), autoConnect: z.boolean().default(false), }); const updateConnectionSchema = createConnectionSchema.partial(); router.get('/', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const userConnections = await db.query.connections.findMany({ where: eq(connections.userId, userId), with: { channels: true }, }); res.json({ connections: userConnections }); } catch (err) { console.error('List connections error:', err); res.status(500).json({ error: 'Internal server error' }); } }); router.post('/', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const body = createConnectionSchema.parse(req.body); const [conn] = await db .insert(connections) .values({ userId, name: body.name, hostname: body.hostname, port: body.port, tls: body.tls, nick: body.nick, username: body.username || null, realname: body.realname || null, password: body.password || null, saslUsername: body.saslUsername || null, saslPassword: body.saslPassword || null, autoConnect: body.autoConnect, }) .returning(); res.status(201).json({ connection: conn }); } catch (err) { if (err instanceof z.ZodError) { res.status(400).json({ error: 'Validation failed', details: err.errors }); return; } console.error('Create connection error:', err); res.status(500).json({ error: 'Internal server error' }); } }); router.put('/:id', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const connectionId = parseInt(req.params.id!, 10); const body = updateConnectionSchema.parse(req.body); const existing = await db.query.connections.findFirst({ where: and( eq(connections.id, connectionId), eq(connections.userId, userId) ), }); if (!existing) { res.status(404).json({ error: 'Connection not found' }); return; } const [updated] = await db .update(connections) .set({ ...body, updatedAt: new Date(), }) .where(eq(connections.id, connectionId)) .returning(); res.json({ connection: updated }); } catch (err) { if (err instanceof z.ZodError) { res.status(400).json({ error: 'Validation failed', details: err.errors }); return; } console.error('Update connection error:', err); res.status(500).json({ error: 'Internal server error' }); } }); router.delete('/:id', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const connectionId = parseInt(req.params.id!, 10); const existing = await db.query.connections.findFirst({ where: and( eq(connections.id, connectionId), eq(connections.userId, userId) ), }); if (!existing) { res.status(404).json({ error: 'Connection not found' }); return; } await connectionManager.disconnect(userId, connectionId); await db.delete(connections).where(eq(connections.id, connectionId)); res.status(204).send(); } catch (err) { console.error('Delete connection error:', err); res.status(500).json({ error: 'Internal server error' }); } }); router.post('/:id/connect', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const connectionId = parseInt(req.params.id!, 10); const conn = await db.query.connections.findFirst({ where: and( eq(connections.id, connectionId), eq(connections.userId, userId) ), }); if (!conn) { res.status(404).json({ error: 'Connection not found' }); return; } await connectionManager.connect(userId, { id: conn.id, hostname: conn.hostname, port: conn.port, tls: conn.tls, nick: conn.nick, username: conn.username, realname: conn.realname, password: conn.password, saslUsername: conn.saslUsername, saslPassword: conn.saslPassword, }); res.json({ status: 'connecting', connectionId }); } catch (err) { console.error('Connect error:', err); res.status(500).json({ error: 'Internal server error' }); } }); router.post('/:id/disconnect', async (req, res) => { try { const authReq = req as AuthenticatedRequest; const userId = authReq.user!.userId; const connectionId = parseInt(req.params.id!, 10); const conn = await db.query.connections.findFirst({ where: and( eq(connections.id, connectionId), eq(connections.userId, userId) ), }); if (!conn) { res.status(404).json({ error: 'Connection not found' }); return; } await connectionManager.disconnect(userId, connectionId); res.json({ status: 'disconnected', connectionId }); } catch (err) { console.error('Disconnect error:', err); res.status(500).json({ error: 'Internal server error' }); } }); export default router;