import { Router } from 'express'; import crypto from 'crypto'; import { querySensors as sensors } from '../data/db.js'; import { redis } from '../data/redis.js'; import { verify } from '../core/securitycore.js'; const router = Router(); const rateLimiter = 10; const rateLimitWindow = 60; router.post('/connect', async (req, res) => { const { sensorID, code } = req.body; const ip = (req.headers['x-forwarded-for']?.split(',')[0]?.trim()) || req.socket.remoteAddress || 'unknown'; const tryKey = `streamconnect:fail:${ip}`; const fails = Number(await redis.get(tryKey).catch(() => 0)); if (fails >= rateLimiter) { return res.status(429).json({ error: 'Too many failed attempts' }); } if (!sensorID || !code) { await redis.multi().incr(tryKey).expire(tryKey, rateLimitWindow).exec().catch(() => { }); return res.status(400).json({ error: 'sensor and code are required' }); } const { rows } = await sensors('select id, name, code_hash from sensors where id = $1', [sensorID]); if (rows.length === 0) { return res.status(404).json({ error: 'sensor not found' }); } if (!rows[0] || !verify(code, rows[0].code_hash)) { await redis.multi().incr(tryKey).expire(tryKey, rateLimitWindow).exec().catch(() => { }); return res.status(401).json({ error: 'invalid code' }); } const token = crypto.randomUUID(); await redis.set(`sensor:pending:${token}`, rows[0].id, 'EX', 5); res.json({ token, expiresIn: 5 }) }) export { router as connectsAPI }