}
}
+ /**
+ * Тестирование подключения к камере Дом.ру
+ *
+ * @param int $id Номер камеры (1-6)
+ */
+ public function actionTestCamera(int $id = 1): Response
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+
+ if ($id < 1 || $id > 6) {
+ return $this->asJson([
+ 'success' => false,
+ 'message' => 'Неверный номер камеры',
+ 'hint' => 'Номер камеры должен быть от 1 до 6',
+ ]);
+ }
+
+ $login = getenv("CAMERA_{$id}_LOGIN") ?: '';
+ $password = getenv("CAMERA_{$id}_PASSWORD") ?: '';
+
+ if (empty($login) || empty($password)) {
+ return $this->asJson([
+ 'success' => false,
+ 'message' => "Камера {$id}: credentials не настроены",
+ 'hint' => "Установите CAMERA_{$id}_LOGIN и CAMERA_{$id}_PASSWORD в .env файле",
+ 'data' => [
+ 'camera_id' => $id,
+ 'login_set' => !empty($login),
+ 'password_set' => !empty($password),
+ ],
+ ]);
+ }
+
+ try {
+ $client = new Client(['timeout' => 15]);
+ $startTime = microtime(true);
+
+ // Авторизация в API Дом.ру
+ $response = $client->post('https://vs.domru.ru/api/Login', [
+ 'form_params' => [
+ 'Login' => $login,
+ 'Password' => $password,
+ ],
+ 'verify' => false,
+ ]);
+
+ $data = json_decode($response->getBody()->getContents(), true);
+ $sessionId = $data['SessionID'] ?? null;
+
+ if (empty($sessionId)) {
+ return $this->asJson([
+ 'success' => false,
+ 'message' => "Камера {$id}: ошибка авторизации",
+ 'error' => $data['Error'] ?? 'Не получен SessionID',
+ 'data' => [
+ 'camera_id' => $id,
+ 'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+ ],
+ ]);
+ }
+
+ // Получение списка камер
+ $camerasResponse = $client->post('https://vs.domru.ru/api/GetCameras', [
+ 'form_params' => [
+ 'SessionID' => $sessionId,
+ ],
+ 'verify' => false,
+ ]);
+
+ $camerasData = json_decode($camerasResponse->getBody()->getContents(), true);
+ $duration = round((microtime(true) - $startTime) * 1000, 2);
+
+ $camerasList = [];
+ if (is_array($camerasData)) {
+ foreach ($camerasData as $camera) {
+ $camerasList[] = [
+ 'name' => $camera['Name'] ?? 'N/A',
+ 'account_object' => $camera['AccountObjectName'] ?? 'N/A',
+ 'camera_id' => $camera['CameraID'] ?? 'N/A',
+ ];
+ }
+ }
+
+ return $this->asJson([
+ 'success' => true,
+ 'message' => "Камера {$id}: подключение успешно",
+ 'data' => [
+ 'camera_id' => $id,
+ 'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+ 'session_id' => substr($sessionId, 0, 8) . '...',
+ 'cameras_count' => count($camerasList),
+ 'cameras' => $camerasList,
+ 'duration_ms' => $duration,
+ ],
+ ]);
+ } catch (GuzzleException $e) {
+ return $this->asJson([
+ 'success' => false,
+ 'message' => "Камера {$id}: ошибка подключения",
+ 'error' => $e->getMessage(),
+ 'data' => [
+ 'camera_id' => $id,
+ 'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+ ],
+ ]);
+ }
+ }
+
+ /**
+ * Тестирование всех настроенных камер Дом.ру
+ */
+ public function actionTestAllCameras(): Response
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+
+ $results = [];
+ $successCount = 0;
+ $failCount = 0;
+ $skippedCount = 0;
+
+ for ($id = 1; $id <= 6; $id++) {
+ $login = getenv("CAMERA_{$id}_LOGIN") ?: '';
+ $password = getenv("CAMERA_{$id}_PASSWORD") ?: '';
+
+ if (empty($login) || empty($password)) {
+ $results["camera_{$id}"] = [
+ 'success' => null,
+ 'message' => 'Не настроена',
+ 'skipped' => true,
+ ];
+ $skippedCount++;
+ continue;
+ }
+
+ // Вызываем тест для каждой камеры
+ $testResult = $this->actionTestCamera($id);
+ $testData = json_decode($testResult->content, true);
+ $results["camera_{$id}"] = $testData;
+
+ if ($testData['success'] ?? false) {
+ $successCount++;
+ } else {
+ $failCount++;
+ }
+
+ // Небольшая пауза между запросами
+ usleep(500000); // 0.5 секунды
+ }
+
+ return $this->asJson([
+ 'success' => $failCount === 0 && $successCount > 0,
+ 'message' => $successCount > 0
+ ? "Камеры: {$successCount} успешно, {$failCount} ошибок, {$skippedCount} пропущено"
+ : 'Нет настроенных камер',
+ 'summary' => [
+ 'total' => 6,
+ 'configured' => $successCount + $failCount,
+ 'passed' => $successCount,
+ 'failed' => $failCount,
+ 'skipped' => $skippedCount,
+ ],
+ 'results' => $results,
+ ]);
+ }
+
/**
* Полная проверка всех переменных окружения
*/
];
$cameras = [];
- for ($i = 1; $i <= 5; $i++) {
+ for ($i = 1; $i <= 6; $i++) {
$cameras["CAMERA_{$i}_LOGIN"] = "Логин камеры $i";
$cameras["CAMERA_{$i}_PASSWORD"] = "Пароль камеры $i";
}
*/
private function getConnectionsStatus(): array
{
- return [
+ $connections = [
'postgresql' => [
'name' => 'PostgreSQL (основная БД)',
'configured' => Yii::$app->has('db'),
'configured' => Yii::$app->has('queue'),
],
];
+
+ // Добавляем статус камер
+ for ($i = 1; $i <= 6; $i++) {
+ $loginSet = !empty(getenv("CAMERA_{$i}_LOGIN"));
+ $passwordSet = !empty(getenv("CAMERA_{$i}_PASSWORD"));
+ $connections["camera_{$i}"] = [
+ 'name' => "Камера Дом.ру #{$i}",
+ 'configured' => $loginSet && $passwordSet,
+ ];
+ }
+
+ return $connections;
}
/**
</div>
</div>
+ <!-- Cameras Tests -->
+ <div class="section-card">
+ <div class="section-header">
+ <h3>📹 Камеры Дом.ру</h3>
+ <button class="btn-test btn-test-success" onclick="testAllCameras()">
+ 🔄 Тестировать все
+ </button>
+ </div>
+ <div class="section-body" style="padding: 0;">
+ <?php for ($i = 1; $i <= 6; $i++): ?>
+ <?php
+ $loginSet = !empty(getenv("CAMERA_{$i}_LOGIN"));
+ $passwordSet = !empty(getenv("CAMERA_{$i}_PASSWORD"));
+ $configured = $loginSet && $passwordSet;
+ ?>
+ <div class="test-item">
+ <div class="test-icon" style="background: <?= $configured ? '#28a745' : '#6c757d' ?>; color: white;">
+ <?= $i ?>
+ </div>
+ <div class="test-info">
+ <div class="test-name">Камера <?= $i ?></div>
+ <div class="test-status" id="camera-<?= $i ?>-status">
+ <?php if ($configured): ?>
+ <span class="status-badge info">Сконфигурирована</span>
+ <?php else: ?>
+ <span class="status-badge warning">Не настроена</span>
+ <?php endif; ?>
+ </div>
+ </div>
+ <div class="test-actions">
+ <button class="btn-test btn-test-primary" onclick="testCamera(<?= $i ?>)" <?= !$configured ? 'disabled' : '' ?>>
+ Тестировать
+ </button>
+ </div>
+ </div>
+ <div class="result-container" id="camera-<?= $i ?>-result"></div>
+ <?php endfor; ?>
+
+ <!-- Cameras Summary -->
+ <div class="result-container" id="cameras-summary-result" style="margin: 15px;"></div>
+ </div>
+ </div>
+
<!-- Environment Variables -->
<div class="section-card">
<div class="section-header">
'telegram-salebot': '<?= \yii\helpers\Url::to(['/diagnostic/test-telegram-salebot']) ?>',
'queue': '<?= \yii\helpers\Url::to(['/diagnostic/test-queue']) ?>',
'send-message': '<?= \yii\helpers\Url::to(['/diagnostic/send-test-message']) ?>',
- 'run-all': '<?= \yii\helpers\Url::to(['/diagnostic/run-all-tests']) ?>'
+ 'run-all': '<?= \yii\helpers\Url::to(['/diagnostic/run-all-tests']) ?>',
+ 'camera': '<?= \yii\helpers\Url::to(['/diagnostic/test-camera']) ?>',
+ 'all-cameras': '<?= \yii\helpers\Url::to(['/diagnostic/test-all-cameras']) ?>'
};
async function testConnection(type) {
}
}
+ async function testCamera(cameraId) {
+ const resultContainer = document.getElementById('camera-' + cameraId + '-result');
+ const statusContainer = document.getElementById('camera-' + cameraId + '-status');
+
+ resultContainer.innerHTML = '<div class="loading"></div> Тестирование камеры ' + cameraId + '...';
+ resultContainer.classList.add('show');
+
+ try {
+ const response = await fetch(endpoints['camera'] + '?id=' + cameraId);
+ const data = await response.json();
+
+ let statusBadge = data.success
+ ? '<span class="status-badge success">✓ Подключена</span>'
+ : '<span class="status-badge danger">✗ Ошибка</span>';
+
+ if (data.skipped) {
+ statusBadge = '<span class="status-badge warning">Не настроена</span>';
+ }
+
+ statusContainer.innerHTML = statusBadge;
+
+ let resultHtml = `<strong>${data.message}</strong><br>`;
+ if (data.data) {
+ // Форматируем вывод камер
+ if (data.data.cameras && data.data.cameras.length > 0) {
+ resultHtml += '<div style="margin-top: 10px;"><strong>Камеры в аккаунте:</strong><ul style="margin: 5px 0; padding-left: 20px;">';
+ data.data.cameras.forEach(cam => {
+ resultHtml += `<li>${cam.name} (${cam.account_object})</li>`;
+ });
+ resultHtml += '</ul></div>';
+
+ // Показываем остальные данные без массива камер
+ const displayData = {...data.data};
+ delete displayData.cameras;
+ resultHtml += '<pre>' + JSON.stringify(displayData, null, 2) + '</pre>';
+ } else {
+ resultHtml += '<pre>' + JSON.stringify(data.data, null, 2) + '</pre>';
+ }
+ }
+ if (data.error) {
+ resultHtml += '<pre style="color: #dc3545;">' + data.error + '</pre>';
+ }
+ if (data.hint) {
+ resultHtml += '<p style="color: #856404; margin-top: 10px;">💡 ' + data.hint + '</p>';
+ }
+
+ resultContainer.innerHTML = resultHtml;
+ } catch (error) {
+ resultContainer.innerHTML = '<pre style="color: #dc3545;">Ошибка запроса: ' + error.message + '</pre>';
+ statusContainer.innerHTML = '<span class="status-badge danger">✗ Ошибка</span>';
+ }
+ }
+
+ async function testAllCameras() {
+ const summaryContainer = document.getElementById('cameras-summary-result');
+ summaryContainer.innerHTML = '<div class="loading"></div> Тестирование всех камер...';
+ summaryContainer.classList.add('show');
+
+ // Тестируем каждую камеру по очереди
+ for (let i = 1; i <= 6; i++) {
+ const loginSet = document.querySelector(`#camera-${i}-status .status-badge`)?.textContent?.includes('Сконфигурирована');
+ if (loginSet || document.querySelector(`button[onclick="testCamera(${i})"]`)?.disabled === false) {
+ await testCamera(i);
+ await new Promise(resolve => setTimeout(resolve, 300));
+ }
+ }
+
+ // Получаем общую сводку
+ try {
+ const response = await fetch(endpoints['all-cameras']);
+ const data = await response.json();
+
+ let summaryHtml = '<h4 style="margin-top: 0;">📊 Сводка по камерам</h4>';
+
+ if (data.summary) {
+ const { passed, failed, skipped, configured } = data.summary;
+ summaryHtml += `
+ <div style="display: flex; gap: 20px; flex-wrap: wrap;">
+ <div><strong style="color: #28a745;">${passed}</strong> успешно</div>
+ <div><strong style="color: #dc3545;">${failed}</strong> ошибок</div>
+ <div><strong style="color: #6c757d;">${skipped}</strong> пропущено</div>
+ <div><strong style="color: #17a2b8;">${configured}</strong> настроено</div>
+ </div>
+ `;
+ }
+
+ summaryHtml += `<p style="margin-top: 10px;">${data.message}</p>`;
+ summaryContainer.innerHTML = summaryHtml;
+ } catch (error) {
+ summaryContainer.innerHTML = '<pre style="color: #dc3545;">Ошибка: ' + error.message + '</pre>';
+ }
+ }
+
// Загрузка реальных названий ботов при открытии страницы
async function loadBotNames() {
// Загрузка Dev бота