118 lines
3.1 KiB
TypeScript
118 lines
3.1 KiB
TypeScript
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;
|