const express = require('express'); const router = express.Router(); /** * Middleware для проверки аутентификации оператора */ const authenticateOperator = (req, res, next) => { const operatorId = req.headers['x-operator-id']; if (!operatorId) { return res.status(401).json({ error: 'Operator ID required' }); } const operator = req.app.locals.deviceManager.getOperator(operatorId); if (!operator || !operator.isConnected()) { return res.status(401).json({ error: 'Invalid or disconnected operator' }); } req.operator = operator; next(); }; /** * Middleware для проверки разрешений */ const requirePermission = (permission) => { return (req, res, next) => { if (!req.operator.hasPermission(permission)) { return res.status(403).json({ error: `Permission '${permission}' required` }); } next(); }; }; /** * GET /api/operators/devices * Получение списка доступных устройств */ router.get('/devices', authenticateOperator, requirePermission('view_cameras'), (req, res) => { try { const { deviceManager } = req.app.locals; const devices = deviceManager.getAvailableDevicesForOperator(req.operator.operatorId); const devicesData = devices.map(device => device.getSummary()); res.json({ success: true, devices: devicesData, total: devicesData.length }); } catch (error) { res.status(500).json({ error: error.message }); } }); /** * GET /api/operators/devices/:deviceId * Получение информации о конкретном устройстве */ router.get('/devices/:deviceId', authenticateOperator, requirePermission('view_cameras'), (req, res) => { try { const { deviceManager } = req.app.locals; const device = deviceManager.getDevice(req.params.deviceId); if (!device) { return res.status(404).json({ error: 'Device not found' }); } res.json({ success: true, device: device.getSummary() }); } catch (error) { res.status(500).json({ error: error.message }); } }); /** * POST /api/operators/camera/request * Создание запроса на доступ к камере */ router.post('/camera/request', authenticateOperator, requirePermission('request_camera'), (req, res) => { try { const { deviceId, cameraType = 'back' } = req.body; if (!deviceId) { return res.status(400).json({ error: 'Device ID required' }); } const { deviceManager, sessionManager, io } = req.app.locals; const device = deviceManager.getDevice(deviceId); if (!device) { return res.status(404).json({ error: 'Device not found' }); } if (!device.canAcceptNewSession()) { return res.status(409).json({ error: 'Device is busy or unavailable' }); } // Создаем сессию const session = sessionManager.createSession(deviceId, req.operator.operatorId, cameraType); // Отправляем запрос на Android устройство device.socket.emit('camera:request', { sessionId: session.sessionId, operatorId: req.operator.operatorId, cameraType }); // Добавляем сессию к устройству и оператору device.addSession(session.sessionId); req.operator.addSession(session.sessionId); req.app.locals.logger.info(`Camera request created: ${session.sessionId}`, { deviceId, operatorId: req.operator.operatorId, cameraType }); res.json({ success: true, sessionId: session.sessionId, message: 'Camera request sent to device' }); } catch (error) { req.app.locals.logger.error('Error creating camera request', error); res.status(500).json({ error: error.message }); } }); /** * POST /api/operators/camera/:sessionId/switch * Переключение типа камеры в активной сессии */ router.post('/camera/:sessionId/switch', authenticateOperator, requirePermission('request_camera'), (req, res) => { try { const { sessionId } = req.params; const { cameraType } = req.body; if (!cameraType) { return res.status(400).json({ error: 'Camera type required' }); } const { sessionManager, deviceManager, io } = req.app.locals; const session = sessionManager.getSession(sessionId); if (!session) { return res.status(404).json({ error: 'Session not found' }); } if (session.operatorId !== req.operator.operatorId) { return res.status(403).json({ error: 'Access denied to this session' }); } if (session.status !== 'active') { return res.status(409).json({ error: 'Session is not active' }); } const device = deviceManager.getDevice(session.deviceId); if (!device || !device.isConnected()) { return res.status(409).json({ error: 'Device not available' }); } // Отправляем команду переключения камеры device.socket.emit('camera:switch', { sessionId, cameraType }); // Обновляем сессию session.switchCamera(cameraType); req.app.locals.logger.info(`Camera switch requested: ${sessionId} -> ${cameraType}`); res.json({ success: true, message: `Camera switch to ${cameraType} requested` }); } catch (error) { req.app.locals.logger.error('Error switching camera', error); res.status(500).json({ error: error.message }); } }); /** * DELETE /api/operators/camera/:sessionId * Завершение сессии камеры */ router.delete('/camera/:sessionId', authenticateOperator, requirePermission('request_camera'), (req, res) => { try { const { sessionId } = req.params; const { sessionManager, deviceManager } = req.app.locals; const session = sessionManager.getSession(sessionId); if (!session) { return res.status(404).json({ error: 'Session not found' }); } if (session.operatorId !== req.operator.operatorId) { return res.status(403).json({ error: 'Access denied to this session' }); } const device = deviceManager.getDevice(session.deviceId); // Отправляем команду отключения (если устройство подключено) if (device && device.isConnected()) { device.socket.emit('camera:disconnect', { sessionId }); device.removeSession(sessionId); } // Закрываем сессию sessionManager.closeSession(sessionId); req.operator.removeSession(sessionId); req.app.locals.logger.info(`Session terminated: ${sessionId}`); res.json({ success: true, message: 'Session terminated' }); } catch (error) { req.app.locals.logger.error('Error terminating session', error); res.status(500).json({ error: error.message }); } }); /** * GET /api/operators/sessions * Получение активных сессий оператора */ router.get('/sessions', authenticateOperator, (req, res) => { try { const { sessionManager } = req.app.locals; const sessions = sessionManager.getOperatorSessions(req.operator.operatorId); const sessionsData = sessions.map(session => session.getSummary()); res.json({ success: true, sessions: sessionsData, total: sessionsData.length }); } catch (error) { res.status(500).json({ error: error.message }); } }); /** * GET /api/operators/sessions/:sessionId * Получение детальной информации о сессии */ router.get('/sessions/:sessionId', authenticateOperator, (req, res) => { try { const { sessionId } = req.params; const { sessionManager } = req.app.locals; const session = sessionManager.getSession(sessionId); if (!session) { return res.status(404).json({ error: 'Session not found' }); } if (session.operatorId !== req.operator.operatorId && !req.operator.hasPermission('admin')) { return res.status(403).json({ error: 'Access denied to this session' }); } res.json({ success: true, session: session.getFullInfo() }); } catch (error) { res.status(500).json({ error: error.message }); } }); /** * GET /api/operators/profile * Получение профиля текущего оператора */ router.get('/profile', authenticateOperator, (req, res) => { try { res.json({ success: true, operator: req.operator.getSummary() }); } catch (error) { res.status(500).json({ error: error.message }); } }); /** * POST /api/operators/ping/:deviceId * Проверка доступности устройства */ router.post('/ping/:deviceId', authenticateOperator, requirePermission('view_cameras'), (req, res) => { try { const { deviceId } = req.params; const { deviceManager } = req.app.locals; const device = deviceManager.getDevice(deviceId); if (!device || !device.isConnected()) { return res.status(404).json({ error: 'Device not found or disconnected' }); } const pingStart = Date.now(); // Отправляем ping и ждем pong device.socket.emit('ping', { timestamp: pingStart }, (response) => { const responseTime = Date.now() - pingStart; device.updateHealthCheck(responseTime); res.json({ success: true, deviceId, responseTime, timestamp: new Date().toISOString() }); }); // Таймаут для ping setTimeout(() => { if (!res.headersSent) { device.recordError(); res.status(408).json({ error: 'Device ping timeout' }); } }, 5000); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;