Files
crm.clientright.ru/upload_file_n8n.php

403 lines
17 KiB
PHP
Raw Normal View History

<?php
// upload_documents_to_crm.php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Быстрый ping для проверки доступности скрипта из браузера/HTTP-клиента
if (isset($_GET['ping'])) {
header('Content-Type: text/plain; charset=utf-8');
echo 'pong';
exit;
}
// Подключение к CRM
require_once('config.inc.php');
require_once('include/Webservices/Utils.php');
require_once('include/Webservices/Create.php');
require_once('include/Webservices/Login.php');
require_once('include/Webservices/AuthToken.php');
require_once('include/Webservices/AddRelated.php');
require_once('include/utils/utils.php');
require_once('includes/Loader.php');
vimport('includes.runtime.Globals');
require_once('include/database/PearDatabase.php');
require_once('modules/Users/Users.php');
// Инициализируем подключение к БД явно
$adb = PearDatabase::getInstance();
// Управление вербозностью: для POST по умолчанию тихий режим (чистый JSON)
$__IS_POST__ = (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST');
$__VERBOSE__ = (!$__IS_POST__) || (isset($_GET['verbose']) && $_GET['verbose'] == '1');
if ($__IS_POST__) {
// В API-режиме скрываем любые варнинги/эхо и возвращаем только JSON
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
}
// Глобальный логгер для этого эндпоинта
if (!function_exists('writeLog')) {
function writeLog($message) {
$logFile = '/logs/upload_documents.log';
$timestamp = date('Y-m-d H:i:s');
file_put_contents($logFile, "[$timestamp] $message\n", FILE_APPEND | LOCK_EX);
}
}
// Унифицированный JSON-ответ и обработка фатальных ошибок для POST
function json_response($payload, $code = 200) {
if (!headers_sent()) {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
}
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
}
if ($__IS_POST__) {
register_shutdown_function(function () {
$e = error_get_last();
if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
writeLog('FATAL: '.$e['message'].' in '.$e['file'].':'.$e['line']);
json_response([
'success' => false,
'error' => [
'type' => 'fatal',
'message' => 'Internal error',
]
], 500);
}
});
}
/**
* Функция для получения entity ID по имени модуля
*/
function vtws_getEntityId($entityName) {
global $adb;
$wsrs = $adb->pquery('select id from vtiger_ws_entity where name=?', array($entityName));
if ($wsrs && $adb->num_rows($wsrs) == 1) {
$wsid = $adb->query_result($wsrs, 0, 0);
} else {
$wsid = 0;
}
return $wsid;
}
/**
* Функция для получения webservice ID пользователя
*/
function getWebserviceUserId($userId) {
global $adb;
$entityId = vtws_getEntityId('Users');
return $entityId . 'x' . $userId;
}
/**
* Функция для получения webservice ID проекта
*/
function getWebserviceProjectId($projectId) {
global $adb;
$entityId = vtws_getEntityId('Project');
return $entityId . 'x' . $projectId;
}
/**
* Префикс WS для папок документов
*/
function getDocumentFoldersWsPrefix() {
global $adb;
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['DocumentFolders']);
if ($rs && $adb->num_rows($rs) > 0) {
return (int)$adb->query_result($rs, 0, 'id');
}
return 22;
}
/**
* Получить WS ID папки по имени, иначе null
*/
function getFolderWsIdByName($folderName) {
global $adb;
$rs = $adb->pquery('SELECT folderid FROM vtiger_attachmentsfolder WHERE foldername = ? LIMIT 1', [$folderName]);
if ($rs && $adb->num_rows($rs) > 0) {
$folderId = (int)$adb->query_result($rs, 0, 'folderid');
$prefix = getDocumentFoldersWsPrefix();
return $prefix . 'x' . $folderId;
}
return null;
}
/**
* Безопасная аутентификация через challenge/response
*/
function authenticateWithCRM($username) {
// 1) Получаем challenge токен
$challenge = vtws_getchallenge($username);
if (empty($challenge['token'])) {
throw new Exception('Не удалось получить challenge token');
}
// 2) Берем accesskey пользователя
$user = new Users();
$userId = $user->retrieve_user_id($username);
if (!$userId) {
throw new Exception("Пользователь {$username} не найден");
}
$accessKey = vtws_getUserAccessKey($userId);
if (!$accessKey) {
throw new Exception('Access key пользователя не найден');
}
// 3) Логинимся
$generatedKey = md5($challenge['token'] . $accessKey);
$loggedUser = vtws_login($username, $generatedKey);
// Для отладки можно писать в лог
// error_log("WS login ok for $username, token={$challenge['token']} md5=$generatedKey");
// Установим $current_user для операций CRMEntity
global $current_user;
$current_user = new Users();
$current_user->retrieveCurrentUserInfoFromFile((int)$userId);
return $loggedUser;
}
/**
* Функция для получения ID папки по умолчанию
*/
function getDefaultFolderId() {
global $adb;
$result = $adb->pquery('SELECT folderid FROM vtiger_attachmentsfolder WHERE foldername = ? LIMIT 1', array('Default'));
if ($result && $adb->num_rows($result) > 0) {
$folderId = $adb->query_result($result, 0, 'folderid');
$entityId = vtws_getEntityId('DocumentFolders');
return $entityId . 'x' . $folderId;
}
return '22x1'; // Fallback ID
}
/**
* Основная функция для создания документов в CRM
*/
function createDocumentsInCRM($filesArray, $adminUsername = 'api') {
global $adb;
writeLog("🚀 Начинаем создание документов для пользователя: $adminUsername");
try {
writeLog("🔐 Попытка аутентификации...");
// Проверяем существование пользователя
$user = new Users();
$userId = $user->retrieve_user_id($adminUsername);
if (!$userId) {
throw new Exception("Пользователь '$adminUsername' не найден");
}
writeLog("✅ Пользователь найден, ID: $userId");
// Безопасная аутентификация
$authenticatedUser = authenticateWithCRM($adminUsername);
writeLog("✅ Аутентификация успешна");
$results = [];
foreach ($filesArray as $index => $fileData) {
writeLog("📄 Обрабатываем файл #$index: " . ($fileData['file_name'] ?? 'Unknown'));
try {
// Получаем webservice ID пользователя и проекта
$assignedUserId = getWebserviceUserId($fileData['user_id']);
$projectId = getWebserviceProjectId($fileData['projectid']);
// Папка: сначала пробуем 'Суд', иначе Default
$folderId = getFolderWsIdByName('Суд');
if (!$folderId) {
$folderId = getDefaultFolderId();
writeLog("⚠️ Папка 'Суд' не найдена, используем по умолчанию: $folderId");
} else {
writeLog("📁 Используем папку 'Суд': $folderId");
}
writeLog("🔗 WebService IDs - User: $assignedUserId, Project: $projectId, Folder: $folderId");
// Формируем данные документа
$documentData = [
'notes_title' => $fileData['description'] ?: $fileData['file_name'],
'filename' => $fileData['url'],
'assigned_user_id' => $assignedUserId,
'notecontent' => 'Автоматически загружен из S3. Контакт: ' . $fileData['contactid'],
'filetype' => 'application/pdf',
'filesize' => '0',
'filelocationtype' => 'E',
'fileversion' => '1.0',
'filestatus' => '1',
'filedownloadcount' => '0',
'folderid' => $folderId,
'created_user_id' => $assignedUserId,
'starred' => '0',
'tags' => 'S3,автозагрузка'
];
writeLog("📋 Данные документа подготовлены: " . json_encode($documentData, JSON_UNESCAPED_UNICODE));
// Создаем документ
$document = vtws_create('Documents', $documentData, $authenticatedUser);
if ($document && isset($document['id'])) {
writeLog("✅ Документ создан с ID: " . $document['id']);
// Привязываем документ к проекту
try {
vtws_add_related($projectId, $document['id'], false, $authenticatedUser);
writeLog("✅ Документ привязан к проекту");
$results[] = [
'status' => 'success',
'file_name' => $fileData['file_name'],
'document_id' => $document['id'],
'project_id' => $fileData['projectid'],
'url' => $fileData['url'],
'message' => 'Документ успешно создан и привязан к проекту'
];
} catch (Exception $e) {
// Fallback: привязываем напрямую через CRMEntity
writeLog("⚠️ Webservice AddRelated не сработал: " . $e->getMessage() . ". Пытаемся привязать напрямую...");
require_once 'data/CRMEntity.php';
require_once 'modules/Vtiger/CRMEntity.php';
$docIds = vtws_getIdComponents($document['id']);
$docNumericId = (int)$docIds[1];
$focus = CRMEntity::getInstance('Project');
relateEntities($focus, 'Project', (int)$fileData['projectid'], 'Documents', $docNumericId);
writeLog("✅ Привязка выполнена напрямую (Project {$fileData['projectid']} -> Document {$docNumericId})");
$results[] = [
'status' => 'success',
'file_name' => $fileData['file_name'],
'document_id' => $document['id'],
'project_id' => $fileData['projectid'],
'url' => $fileData['url'],
'message' => 'Документ создан и привязан к проекту (fallback)'
];
}
} else {
writeLog("❌ Ошибка создания документа");
$results[] = [
'status' => 'error',
'file_name' => $fileData['file_name'],
'project_id' => $fileData['projectid'],
'url' => $fileData['url'],
'message' => 'Ошибка создания документа'
];
}
} catch (Exception $e) {
writeLog("❌ Критическая ошибка для файла: " . $e->getMessage());
$results[] = [
'status' => 'error',
'file_name' => $fileData['file_name'] ?? 'Unknown',
'project_id' => $fileData['projectid'] ?? 'Unknown',
'url' => $fileData['url'] ?? 'Unknown',
'message' => 'Ошибка: ' . $e->getMessage()
];
}
}
writeLog("🏁 Обработка завершена. Всего файлов: " . count($filesArray));
return $results;
} catch (Exception $e) {
writeLog("💥 Критическая ошибка: " . $e->getMessage());
return [
'status' => 'critical_error',
'message' => 'Ошибка аутентификации в CRM: ' . $e->getMessage()
];
}
}
/**
* Получение access key пользователя
*/
function vtws_getUserAccessKey($userId) {
global $adb;
$sql = "SELECT accesskey FROM vtiger_users WHERE id = ?";
$result = $adb->pquery($sql, array($userId));
if ($result && $adb->num_rows($result) > 0) {
return $adb->query_result($result, 0, 'accesskey');
}
return null;
}
// Пример использования:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Получаем JSON данные (без BOM) и пробуем распознать формат
writeLog('POST '.$_SERVER['REQUEST_URI'].' CT='.(isset($_SERVER['CONTENT_TYPE'])?$_SERVER['CONTENT_TYPE']:'').', CL='.(isset($_SERVER['CONTENT_LENGTH'])?$_SERVER['CONTENT_LENGTH']:'').' UA='.(isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'').', IP='.(isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'') );
$input = file_get_contents('php://input');
writeLog('RAW BODY: '.substr($input,0,2048).(strlen($input)>2048?'...<truncated>':''));
$input = ltrim($input, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$filesArray = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
writeLog('JSON ERROR: '.json_last_error_msg());
json_response(['success' => false, 'error' => ['message' => 'Invalid JSON: '.json_last_error_msg()]], 400);
exit;
}
// Авто-распаковка n8n-форматов:
// 1) [{"data":[...]}]
if (is_array($filesArray) && count($filesArray) === 1 && isset($filesArray[0]['data']) && is_array($filesArray[0]['data'])) {
$filesArray = $filesArray[0]['data'];
}
// 2) {"data":[...]}
if (is_array($filesArray) && isset($filesArray['data']) && is_array($filesArray['data'])) {
$filesArray = $filesArray['data'];
}
if (!is_array($filesArray)) {
writeLog('BAD FORMAT: not an array after normalization');
json_response(['success' => false, 'error' => ['message' => 'JSON must be an array of file items']], 400);
exit;
}
writeLog('ITEMS: '.count($filesArray));
// Создаем документы
$results = createDocumentsInCRM($filesArray);
writeLog('DONE: processed='.count($filesArray));
// Возвращаем результат
json_response([
'success' => true,
'results' => $results,
'total_processed' => count($filesArray)
]);
} else {
// Тестовый запуск с вашими данными
$testData = [
[
"session_token" => "sess_dedc540e-f60d-491b-b6bb-ef7f7d04a366",
"user_id" => 1,
"contactid" => 120374,
"description" => "Иск",
"projectid" => 354918,
"pages" => 2,
"url" => "https://s3.twcstorage.ru/f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c/clientright/120374/1757346451126.pdf",
"file_name" => "1757346451126.pdf"
],
[
"session_token" => "sess_dedc540e-f60d-491b-b6bb-ef7f7d04a366",
"user_id" => 1,
"contactid" => 120374,
"description" => "доказательство направления претензии",
"projectid" => 354918,
"pages" => 4,
"url" => "https://s3.twcstorage.ru/f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c/clientright/120374/1757346454717.pdf",
"file_name" => "1757346454717.pdf"
]
];
$results = createDocumentsInCRM($testData);
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'results' => $results,
'total_processed' => count($testData)
], JSON_UNESCAPED_UNICODE);
}
?>