Files
god_eye/backend/src/routes/operators.js
2025-09-28 22:00:44 +09:00

337 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;