import { and, desc, eq } from 'drizzle-orm'; import { Router } from 'express'; import { z } from 'zod'; import { db } from '../db/client'; import { deviceLinks, devices } from '../db/schema'; import { requireAuth } from '../middleware/auth'; const router = Router(); const createLinkSchema = z.object({ cameraDeviceId: z.string().uuid(), clientDeviceId: z.string().uuid(), }); router.use(requireAuth); router.post('/', async (req, res) => { const parsed = createLinkSchema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ message: 'Invalid request body', errors: parsed.error.flatten() }); return; } const authSession = req.auth; if (!authSession?.user?.id) { res.status(401).json({ message: 'Unauthorized' }); return; } if (parsed.data.cameraDeviceId === parsed.data.clientDeviceId) { res.status(400).json({ message: 'cameraDeviceId and clientDeviceId must be different' }); return; } const [camera, client] = await Promise.all([ db.query.devices.findFirst({ where: and(eq(devices.id, parsed.data.cameraDeviceId), eq(devices.userId, authSession.user.id)), }), db.query.devices.findFirst({ where: and(eq(devices.id, parsed.data.clientDeviceId), eq(devices.userId, authSession.user.id)), }), ]); if (!camera || !client) { res.status(400).json({ message: 'Both devices must exist and belong to the authenticated user' }); return; } if (camera.role !== 'camera') { res.status(400).json({ message: 'cameraDeviceId must belong to a camera role device' }); return; } if (client.role !== 'client') { res.status(400).json({ message: 'clientDeviceId must belong to a client role device' }); return; } try { const [created] = await db .insert(deviceLinks) .values({ ownerUserId: authSession.user.id, cameraDeviceId: parsed.data.cameraDeviceId, clientDeviceId: parsed.data.clientDeviceId, status: 'active', }) .returning(); res.status(201).json({ message: 'Device link created', link: created }); } catch (error) { console.error('Failed to create device link', error); res.status(409).json({ message: 'Device link already exists' }); } }); router.get('/', async (req, res) => { const authSession = req.auth; if (!authSession?.user?.id) { res.status(401).json({ message: 'Unauthorized' }); return; } const links = await db.query.deviceLinks.findMany({ where: eq(deviceLinks.ownerUserId, authSession.user.id), orderBy: [desc(deviceLinks.updatedAt)], }); res.json({ count: links.length, links }); }); router.delete('/:linkId', async (req, res) => { const authSession = req.auth; if (!authSession?.user?.id) { res.status(401).json({ message: 'Unauthorized' }); return; } const [deleted] = await db .delete(deviceLinks) .where(and(eq(deviceLinks.id, req.params.linkId), eq(deviceLinks.ownerUserId, authSession.user.id))) .returning({ id: deviceLinks.id }); if (!deleted) { res.status(404).json({ message: 'Link not found' }); return; } res.json({ message: 'Device link removed', linkId: deleted.id }); }); export default router;