Добавлен retry механизм для webservice аутентификации (race condition fix)

This commit is contained in:
Fedor
2025-11-25 23:02:52 +03:00
parent 2b1dca9e92
commit be1ac2ed49

View File

@@ -293,13 +293,11 @@ function normalizeInputData($data) {
return [];
}
// Основная функция для создания документов
function createDocumentsInCRM($filesArray, $userName = 'api') {
global $adb, $current_user;
// Функция для получения сессии webservice с retry
function getWebserviceSession($userName, $maxRetries = 3) {
global $adb;
writeLog('🚀 Начинаем создание документов...');
// 1. Получаем access key пользователя
// Получаем access key пользователя
$rs = $adb->pquery('SELECT id, accesskey FROM vtiger_users WHERE user_name=?', [$userName]);
if (!$rs || $adb->num_rows($rs) == 0) {
writeLog("❌ Пользователь $userName не найден");
@@ -308,53 +306,93 @@ function createDocumentsInCRM($filesArray, $userName = 'api') {
$userId = $adb->query_result($rs, 0, 'id');
$accessKey = $adb->query_result($rs, 0, 'accesskey');
// 2. Получаем challenge token
$endpointUrl = 'https://crm.clientright.ru/webservice.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
writeLog("🔐 Попытка аутентификации #{$attempt}...");
// Добавляем случайную задержку для избежания race condition
if ($attempt > 1) {
$delay = rand(100, 500) * 1000; // 100-500ms в микросекундах
usleep($delay);
writeLog("⏱️ Задержка " . ($delay / 1000) . "ms перед попыткой #{$attempt}");
}
// getchallenge
curl_setopt($ch, CURLOPT_URL, $endpointUrl . '?operation=getchallenge&username=' . urlencode($userName));
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_HTTPGET, 1);
$resp = curl_exec($ch);
if ($resp === false) {
writeLog('❌ CURL error (challenge): ' . curl_error($ch));
continue;
}
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$challenge = json_decode($resp, true);
if (!$challenge || empty($challenge['result']['token'])) {
writeLog("❌ Invalid challenge response (attempt #{$attempt}): " . substr($resp, 0, 100));
continue;
}
$token = $challenge['result']['token'];
// login
$key = md5($token . $accessKey);
curl_setopt($ch, CURLOPT_URL, $endpointUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'operation' => 'login',
'username' => $userName,
'accessKey' => $key,
]);
$resp = curl_exec($ch);
if ($resp === false) {
writeLog('❌ CURL error (login): ' . curl_error($ch));
continue;
}
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$login = json_decode($resp, true);
if (!$login || empty($login['result']['sessionName'])) {
writeLog("❌ Login failed (attempt #{$attempt}): " . substr($resp, 0, 100));
continue;
}
writeLog("✅ Аутентификация успешна с попытки #{$attempt}");
return [
'sessionId' => $login['result']['sessionName'],
'userId' => $userId,
'curl' => $ch
];
}
curl_close($ch);
return ['error' => 'Authentication failed after ' . $maxRetries . ' attempts'];
}
// Основная функция для создания документов
function createDocumentsInCRM($filesArray, $userName = 'api') {
global $adb, $current_user;
writeLog('🚀 Начинаем создание документов...');
// Получаем сессию с retry логикой
$session = getWebserviceSession($userName);
if (isset($session['error'])) {
return $session;
}
$sessionId = $session['sessionId'];
$userId = $session['userId'];
$ch = $session['curl'];
$endpointUrl = 'https://crm.clientright.ru/webservice.php';
// getchallenge
curl_setopt($ch, CURLOPT_URL, $endpointUrl . '?operation=getchallenge&username=' . urlencode($userName));
$resp = curl_exec($ch);
if ($resp === false) {
writeLog('❌ CURL error: ' . curl_error($ch));
return ['error' => 'Network error'];
}
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$challenge = json_decode($resp, true);
if (!$challenge || empty($challenge['result']['token'])) {
writeLog('❌ Invalid challenge response: ' . substr($resp, 0, 100));
return ['error' => 'Authentication failed'];
}
$token = $challenge['result']['token'];
// 3. Логинимся
$key = md5($token . $accessKey);
curl_setopt($ch, CURLOPT_URL, $endpointUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'operation' => 'login',
'username' => $userName,
'accessKey' => $key,
]);
$resp = curl_exec($ch);
if ($resp === false) {
writeLog('❌ CURL error: ' . curl_error($ch));
return ['error' => 'Network error'];
}
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$login = json_decode($resp, true);
if (!$login || empty($login['result']['sessionName'])) {
writeLog('❌ Login failed: ' . substr($resp, 0, 100));
return ['error' => 'Authentication failed'];
}
$sessionId = $login['result']['sessionName'];
// 4. Устанавливаем current_user для CRMEntity
$current_user = new Users();
$current_user->retrieveCurrentUserInfoFromFile((int)$userId);