526 lines
22 KiB
PHP
526 lines
22 KiB
PHP
|
|
<?php
|
|||
|
|
error_reporting(E_ALL);
|
|||
|
|
ini_set('display_errors', '1');
|
|||
|
|
|
|||
|
|
// Для POST запросов отключаем вывод ошибок в HTML
|
|||
|
|
$IS_POST = ($_SERVER['REQUEST_METHOD'] === 'POST');
|
|||
|
|
if ($IS_POST) {
|
|||
|
|
ini_set('display_errors', '0');
|
|||
|
|
ini_set('display_startup_errors', '0');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для записи в лог с детализацией
|
|||
|
|
function writeLog($message, $level = 'INFO') {
|
|||
|
|
$timestamp = date('Y-m-d H:i:s');
|
|||
|
|
$line = "[$timestamp] [$level] $message\n";
|
|||
|
|
// Пробуем писать в основной лог
|
|||
|
|
@file_put_contents(__DIR__ . '/logs/upload_documents_v2_detailed.log', $line, FILE_APPEND | LOCK_EX);
|
|||
|
|
// И в /tmp как запасной вариант
|
|||
|
|
@file_put_contents('/tmp/upload_documents_v2_detailed.log', $line, FILE_APPEND | LOCK_EX);
|
|||
|
|
// И в системный лог
|
|||
|
|
error_log('[upload_documents_v2_detailed] ' . $message);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для JSON ответа
|
|||
|
|
function json_response($data, $code = 200) {
|
|||
|
|
if (!headers_sent()) {
|
|||
|
|
http_response_code($code);
|
|||
|
|
header('Content-Type: application/json; charset=utf-8');
|
|||
|
|
}
|
|||
|
|
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Быстрый ping
|
|||
|
|
if (isset($_GET['ping'])) {
|
|||
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|||
|
|
echo 'pong v2 detailed';
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Для POST запросов регистрируем обработчик фатальных ошибок
|
|||
|
|
if ($IS_POST) {
|
|||
|
|
register_shutdown_function(function() {
|
|||
|
|
$error = error_get_last();
|
|||
|
|
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
|
|||
|
|
writeLog('FATAL: ' . $error['message'] . ' in ' . $error['file'] . ':' . $error['line'], 'FATAL');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => [
|
|||
|
|
'type' => 'fatal',
|
|||
|
|
'message' => 'Internal error'
|
|||
|
|
]
|
|||
|
|
], 500);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Инициализация CRM
|
|||
|
|
require_once 'config.inc.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';
|
|||
|
|
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 'data/CRMEntity.php';
|
|||
|
|
require_once 'modules/Vtiger/CRMEntity.php';
|
|||
|
|
$adb = PearDatabase::getInstance();
|
|||
|
|
|
|||
|
|
// Вспомогательные функции
|
|||
|
|
function getUserWsPrefix() {
|
|||
|
|
global $adb;
|
|||
|
|
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['Users']);
|
|||
|
|
return ($rs && $adb->num_rows($rs) > 0) ? $adb->query_result($rs, 0, 'id') : 19;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getProjectWsIdFromDB($projectId) {
|
|||
|
|
global $adb;
|
|||
|
|
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['Project']);
|
|||
|
|
return ($rs && $adb->num_rows($rs) > 0) ? $adb->query_result($rs, 0, 'id') . 'x' . (int)$projectId : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getDocumentFoldersWsPrefix() {
|
|||
|
|
global $adb;
|
|||
|
|
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['DocumentFolders']);
|
|||
|
|
return ($rs && $adb->num_rows($rs) > 0) ? (int)$adb->query_result($rs, 0, 'id') : 22;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Извлекает S3 метаданные из URL
|
|||
|
|
*/
|
|||
|
|
function parseS3Url($fileUrl) {
|
|||
|
|
$parsed = parse_url($fileUrl);
|
|||
|
|
if (!$parsed || !isset($parsed['host']) || !isset($parsed['path'])) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$path = ltrim($parsed['path'], '/');
|
|||
|
|
$pathParts = explode('/', $path, 2);
|
|||
|
|
|
|||
|
|
if (count($pathParts) < 2) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'bucket' => $pathParts[0],
|
|||
|
|
'key' => $pathParts[1],
|
|||
|
|
'host' => $parsed['host']
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получает размер файла из S3
|
|||
|
|
*/
|
|||
|
|
function getS3FileSize($fileUrl) {
|
|||
|
|
try {
|
|||
|
|
$ch = curl_init();
|
|||
|
|
curl_setopt($ch, CURLOPT_URL, $fileUrl);
|
|||
|
|
curl_setopt($ch, CURLOPT_NOBODY, true);
|
|||
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|||
|
|
curl_setopt($ch, CURLOPT_HEADER, true);
|
|||
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|||
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|||
|
|
|
|||
|
|
$response = curl_exec($ch);
|
|||
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|||
|
|
curl_close($ch);
|
|||
|
|
|
|||
|
|
if ($httpCode === 200 && $response) {
|
|||
|
|
if (preg_match('/Content-Length:\s*(\d+)/i', $response, $matches)) {
|
|||
|
|
return (int)$matches[1];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
writeLog("Ошибка получения размера файла: " . $e->getMessage(), 'ERROR');
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Обновляет документ в базе данных напрямую с S3 метаданными
|
|||
|
|
*/
|
|||
|
|
function updateDocumentS3Metadata($documentId, $s3Info, $fileSize, $originalFileName) {
|
|||
|
|
global $adb;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
writeLog("Обновляем S3 метаданные для документа $documentId", 'DEBUG');
|
|||
|
|
writeLog("S3 Bucket: {$s3Info['bucket']}, Key: {$s3Info['key']}, Size: $fileSize", 'DEBUG');
|
|||
|
|
|
|||
|
|
$updateQuery = "
|
|||
|
|
UPDATE vtiger_notes
|
|||
|
|
SET
|
|||
|
|
filename = ?,
|
|||
|
|
filelocationtype = 'E',
|
|||
|
|
filesize = ?,
|
|||
|
|
s3_bucket = ?,
|
|||
|
|
s3_key = ?,
|
|||
|
|
s3_etag = ''
|
|||
|
|
WHERE notesid = ?
|
|||
|
|
";
|
|||
|
|
|
|||
|
|
// Создаем полный S3 URL для отображения в CRM
|
|||
|
|
$s3Url = "https://s3.twcstorage.ru/{$s3Info['bucket']}/{$s3Info['key']}";
|
|||
|
|
|
|||
|
|
$params = [
|
|||
|
|
$s3Url, // Используем полный S3 URL вместо оригинального имени файла
|
|||
|
|
$fileSize,
|
|||
|
|
$s3Info['bucket'],
|
|||
|
|
$s3Info['key'],
|
|||
|
|
$documentId
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
writeLog("SQL: $updateQuery", 'DEBUG');
|
|||
|
|
writeLog("Params: " . json_encode($params), 'DEBUG');
|
|||
|
|
|
|||
|
|
$result = $adb->pquery($updateQuery, $params);
|
|||
|
|
|
|||
|
|
if ($result) {
|
|||
|
|
writeLog("✅ S3 метаданные успешно обновлены для документа $documentId", 'SUCCESS');
|
|||
|
|
return true;
|
|||
|
|
} else {
|
|||
|
|
writeLog("❌ Ошибка обновления S3 метаданных: " . $adb->database->errorMsg(), 'ERROR');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
writeLog("❌ Исключение при обновлении S3 метаданных: " . $e->getMessage(), 'ERROR');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🚀 УЛУЧШЕННАЯ ФУНКЦИЯ: Создание документов с правильными S3 метаданными
|
|||
|
|
function createDocumentsWithSession($data) {
|
|||
|
|
global $adb, $current_user;
|
|||
|
|
|
|||
|
|
$sessionName = $data['sessionName'];
|
|||
|
|
$projectId = (int)$data['projectid'];
|
|||
|
|
$contactId = (int)$data['contactid'];
|
|||
|
|
$userId = (int)($data['user_id'] ?? 1);
|
|||
|
|
$filesArray = $data['files'] ?? [];
|
|||
|
|
|
|||
|
|
writeLog('🚀 Начинаем создание документов с правильными S3 метаданными', 'INFO');
|
|||
|
|
writeLog("📋 Проект: $projectId, Контакт: $contactId, Пользователь: $userId", 'INFO');
|
|||
|
|
writeLog('📋 Файлов к обработке: ' . count($filesArray), 'INFO');
|
|||
|
|
|
|||
|
|
// Упрощенный подход: используем только webservice API без обращений к БД
|
|||
|
|
$projectWsId = '20x' . $projectId; // Предполагаем стандартный префикс для Project
|
|||
|
|
$assignedUserWsId = '19x' . $userId; // Предполагаем стандартный префикс для Users
|
|||
|
|
$folderWsId = '22x1'; // Default папка
|
|||
|
|
|
|||
|
|
writeLog("✅ Project WS ID: $projectWsId", 'DEBUG');
|
|||
|
|
writeLog("👤 User WS ID: $assignedUserWsId", 'DEBUG');
|
|||
|
|
writeLog("📁 Папка: $folderWsId", 'DEBUG');
|
|||
|
|
|
|||
|
|
// Инициализируем CURL
|
|||
|
|
$ch = curl_init();
|
|||
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|||
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
|||
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|||
|
|
curl_setopt($ch, CURLOPT_URL, 'https://crm.clientright.ru/webservice.php');
|
|||
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|||
|
|
|
|||
|
|
$results = [];
|
|||
|
|
|
|||
|
|
foreach ($filesArray as $i => $file) {
|
|||
|
|
$fileName = $file['original_file_name'] ?? $file['file_name'] ?? 'Unknown';
|
|||
|
|
writeLog("📄 Обрабатываем файл #{$i}: $fileName", 'INFO');
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Проверяем обязательные поля
|
|||
|
|
if (empty($file['file_url'])) {
|
|||
|
|
throw new Exception("Отсутствует file_url");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Парсим S3 URL для получения метаданных
|
|||
|
|
$s3Info = parseS3Url($file['file_url']);
|
|||
|
|
if (!$s3Info) {
|
|||
|
|
throw new Exception("Не удалось распарсить S3 URL: " . $file['file_url']);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
writeLog("🔍 S3 Bucket: {$s3Info['bucket']}, Key: {$s3Info['key']}", 'DEBUG');
|
|||
|
|
|
|||
|
|
// Получаем размер файла из S3
|
|||
|
|
$fileSize = getS3FileSize($file['file_url']);
|
|||
|
|
writeLog("📏 Размер файла: " . number_format($fileSize) . " байт", 'DEBUG');
|
|||
|
|
|
|||
|
|
// Определяем название и описание документа
|
|||
|
|
$documentTitle = $file['upload_description'] ?? $file['original_file_name'] ?? $file['file_name'] ?? 'Документ';
|
|||
|
|
$documentContent = sprintf(
|
|||
|
|
'Загружено через n8n. ID: %s, Контакт: %d, Проект: %d, Загружено: %s',
|
|||
|
|
$file['id'] ?? 'N/A',
|
|||
|
|
$contactId,
|
|||
|
|
$projectId,
|
|||
|
|
$file['uploaded_at'] ?? date('Y-m-d H:i:s')
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Создаём документ БЕЗ S3 метаданных (webservice не поддерживает их)
|
|||
|
|
$docElement = [
|
|||
|
|
'notes_title' => $documentTitle,
|
|||
|
|
'filename' => $file['original_file_name'] ?? $file['file_name'], // Оригинальное имя файла
|
|||
|
|
'assigned_user_id' => $assignedUserWsId,
|
|||
|
|
'notecontent' => $documentContent,
|
|||
|
|
'filetype' => 'application/pdf',
|
|||
|
|
'filesize' => (string)$fileSize,
|
|||
|
|
'filelocationtype' => 'E', // External URL
|
|||
|
|
'fileversion' => '1.0',
|
|||
|
|
'filestatus' => '1', // Active
|
|||
|
|
'folderid' => $folderWsId,
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
writeLog("📤 Создаём документ '$documentTitle' через webservice", 'INFO');
|
|||
|
|
writeLog("📤 Элемент документа: " . json_encode($docElement, JSON_UNESCAPED_UNICODE), 'DEBUG');
|
|||
|
|
|
|||
|
|
// Отправляем запрос на создание документа
|
|||
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
|||
|
|
'operation' => 'create',
|
|||
|
|
'sessionName' => $sessionName,
|
|||
|
|
'elementType' => 'Documents',
|
|||
|
|
'element' => json_encode($docElement, JSON_UNESCAPED_UNICODE),
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$resp = curl_exec($ch);
|
|||
|
|
if ($resp === false) {
|
|||
|
|
throw new Exception('CURL error: ' . curl_error($ch));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
|
|||
|
|
writeLog("📥 Ответ создания документа: " . substr($resp, 0, 500), 'DEBUG');
|
|||
|
|
|
|||
|
|
$doc = json_decode($resp, true);
|
|||
|
|
if (!$doc || !$doc['success'] || empty($doc['result']['id'])) {
|
|||
|
|
throw new Exception('Failed to create document: ' . substr($resp, 0, 200));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$documentWsId = $doc['result']['id'];
|
|||
|
|
list(, $docNumericId) = explode('x', $documentWsId, 2);
|
|||
|
|
|
|||
|
|
writeLog("✅ Документ создан: $documentWsId (numeric: $docNumericId)", 'SUCCESS');
|
|||
|
|
|
|||
|
|
// ВАЖНО: Обновляем S3 метаданные напрямую в базе данных
|
|||
|
|
$s3UpdateSuccess = updateDocumentS3Metadata($docNumericId, $s3Info, $fileSize, $file['original_file_name'] ?? $file['file_name']);
|
|||
|
|
|
|||
|
|
// Привязываем к проекту
|
|||
|
|
writeLog("🔗 Привязываем документ $documentWsId к проекту $projectWsId", 'INFO');
|
|||
|
|
|
|||
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
|||
|
|
'operation' => 'AddRelated',
|
|||
|
|
'sessionName' => $sessionName,
|
|||
|
|
'sourceRecordId' => $projectWsId,
|
|||
|
|
'relatedRecordId' => $documentWsId,
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$resp = curl_exec($ch);
|
|||
|
|
$relationOk = false;
|
|||
|
|
|
|||
|
|
if ($resp !== false) {
|
|||
|
|
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
|
|||
|
|
writeLog("📥 Ответ AddRelated: " . substr($resp, 0, 200), 'DEBUG');
|
|||
|
|
|
|||
|
|
$rel = json_decode($resp, true);
|
|||
|
|
$relationOk = isset($rel['result']['message']) && $rel['result']['message'] === 'successfull';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если webservice не сработал - используем прямую привязку
|
|||
|
|
if (!$relationOk) {
|
|||
|
|
writeLog("⚠️ AddRelated не сработал, используем прямую привязку", 'WARNING');
|
|||
|
|
try {
|
|||
|
|
// Устанавливаем current_user для CRMEntity
|
|||
|
|
if (!isset($current_user) || !$current_user) {
|
|||
|
|
$current_user = new Users();
|
|||
|
|
$current_user->retrieveCurrentUserInfoFromFile($userId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$focus = CRMEntity::getInstance('Project');
|
|||
|
|
relateEntities($focus, 'Project', $projectId, 'Documents', (int)$docNumericId);
|
|||
|
|
writeLog("✅ Прямая привязка успешна", 'SUCCESS');
|
|||
|
|
} catch (Exception $directRelationError) {
|
|||
|
|
writeLog("❌ Прямая привязка не удалась: " . $directRelationError->getMessage(), 'ERROR');
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
writeLog("✅ AddRelated успешен", 'SUCCESS');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Возвращаем результат с сохранением всех исходных данных
|
|||
|
|
$result = array_merge($file, [
|
|||
|
|
'status' => 'success',
|
|||
|
|
'projectid' => $projectId,
|
|||
|
|
'contactid' => $contactId,
|
|||
|
|
'crm_result' => [
|
|||
|
|
'document_id' => $documentWsId,
|
|||
|
|
'document_numeric_id' => $docNumericId,
|
|||
|
|
'project_id' => $projectId,
|
|||
|
|
'contact_id' => $contactId,
|
|||
|
|
'folder_id' => $folderWsId,
|
|||
|
|
's3_bucket' => $s3Info['bucket'],
|
|||
|
|
's3_key' => $s3Info['key'],
|
|||
|
|
'file_size' => $fileSize,
|
|||
|
|
's3_metadata_updated' => $s3UpdateSuccess,
|
|||
|
|
'message' => 'Документ создан с правильными S3 метаданными и привязан к проекту' . (!$relationOk ? ' (прямая привязка)' : '')
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$results[] = $result;
|
|||
|
|
writeLog("✅ Файл '$fileName' успешно обработан с S3 метаданными", 'SUCCESS');
|
|||
|
|
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
writeLog("❌ Ошибка для файла '$fileName': " . $e->getMessage(), 'ERROR');
|
|||
|
|
|
|||
|
|
$results[] = array_merge($file, [
|
|||
|
|
'status' => 'error',
|
|||
|
|
'projectid' => $projectId,
|
|||
|
|
'contactid' => $contactId,
|
|||
|
|
'crm_result' => [
|
|||
|
|
'message' => $e->getMessage()
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
curl_close($ch);
|
|||
|
|
$successCount = count(array_filter($results, function($r) { return $r['status'] === 'success'; }));
|
|||
|
|
writeLog("🏁 Обработка завершена. Успешно: $successCount/" . count($results), 'INFO');
|
|||
|
|
|
|||
|
|
return $results;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обработка запроса
|
|||
|
|
if ($IS_POST) {
|
|||
|
|
writeLog('=== START POST REQUEST V2 (DETAILED LOGGING) ===', 'INFO');
|
|||
|
|
writeLog('Headers: ' . json_encode(getallheaders(), JSON_UNESCAPED_UNICODE), 'DEBUG');
|
|||
|
|
|
|||
|
|
// Получаем и проверяем входные данные
|
|||
|
|
$input = file_get_contents('php://input');
|
|||
|
|
writeLog('Raw input: ' . substr($input, 0, 1000) . (strlen($input) > 1000 ? '...(truncated)' : ''), 'DEBUG');
|
|||
|
|
|
|||
|
|
$input = ltrim($input, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
|
|||
|
|
$data = json_decode($input, true);
|
|||
|
|
|
|||
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|||
|
|
writeLog('❌ JSON Error: ' . json_last_error_msg(), 'ERROR');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => ['message' => 'Invalid JSON: ' . json_last_error_msg()]
|
|||
|
|
], 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
writeLog('Parsed data keys: ' . implode(', ', array_keys($data)), 'DEBUG');
|
|||
|
|
|
|||
|
|
// Проверяем обязательные поля
|
|||
|
|
if (empty($data['sessionName'])) {
|
|||
|
|
writeLog('❌ Error: sessionName is required', 'ERROR');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => ['message' => 'sessionName is required in request data']
|
|||
|
|
], 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (empty($data['projectid'])) {
|
|||
|
|
writeLog('❌ Error: projectid is required', 'ERROR');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => ['message' => 'projectid is required in request data']
|
|||
|
|
], 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (empty($data['contactid'])) {
|
|||
|
|
writeLog('❌ Error: contactid is required', 'ERROR');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => ['message' => 'contactid is required in request data']
|
|||
|
|
], 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Поддерживаем оба формата: files и documents
|
|||
|
|
$filesArray = null;
|
|||
|
|
if (!empty($data['files']) && is_array($data['files'])) {
|
|||
|
|
$filesArray = $data['files'];
|
|||
|
|
} elseif (!empty($data['documents']) && is_array($data['documents'])) {
|
|||
|
|
$filesArray = $data['documents'];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (empty($filesArray)) {
|
|||
|
|
writeLog('❌ Error: files or documents array is required', 'ERROR');
|
|||
|
|
json_response([
|
|||
|
|
'success' => false,
|
|||
|
|
'error' => ['message' => 'files or documents array is required in request data']
|
|||
|
|
], 400);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
writeLog("🔑 Сессия: {$data['sessionName']}", 'INFO');
|
|||
|
|
writeLog("📋 Проект: {$data['projectid']}, Контакт: {$data['contactid']}", 'INFO');
|
|||
|
|
writeLog('📄 Файлов: ' . count($filesArray), 'INFO');
|
|||
|
|
|
|||
|
|
// Нормализуем данные: всегда используем 'files' внутри функции
|
|||
|
|
$normalizedData = $data;
|
|||
|
|
$normalizedData['files'] = $filesArray;
|
|||
|
|
|
|||
|
|
// Создаём документы с правильными S3 метаданными
|
|||
|
|
$results = createDocumentsWithSession($normalizedData);
|
|||
|
|
|
|||
|
|
// Успешный ответ
|
|||
|
|
writeLog('✅ Success: processed ' . count($results) . ' files with S3 metadata', 'SUCCESS');
|
|||
|
|
json_response([
|
|||
|
|
'success' => true,
|
|||
|
|
'total_processed' => count($filesArray),
|
|||
|
|
'results' => $results,
|
|||
|
|
'session_used' => $sessionName
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
} else {
|
|||
|
|
// GET запрос - документация API
|
|||
|
|
header('Content-Type: application/json; charset=utf-8');
|
|||
|
|
echo json_encode([
|
|||
|
|
'success' => true,
|
|||
|
|
'message' => 'Upload Documents API v2 - Detailed Logging Version',
|
|||
|
|
'endpoint' => 'POST to this URL with complete data object',
|
|||
|
|
'logging' => 'Detailed logs available in logs/upload_documents_v2_detailed.log',
|
|||
|
|
'format' => [
|
|||
|
|
'sessionName' => 'string (required - from n8n CRM login)',
|
|||
|
|
'projectid' => 'string (required - project ID)',
|
|||
|
|
'contactid' => 'string (required - contact ID)',
|
|||
|
|
'user_id' => 'string (optional - default "1")',
|
|||
|
|
'files' => [ // или 'documents' - поддерживаются оба формата
|
|||
|
|
[
|
|||
|
|
'id' => 'string (file UUID)',
|
|||
|
|
'file_id' => 'string (S3 path)',
|
|||
|
|
'file_url' => 'string (required - full S3 URL)',
|
|||
|
|
'file_name' => 'string (S3 filename)',
|
|||
|
|
'field_name' => 'string (form field)',
|
|||
|
|
'uploaded_at' => 'string (ISO datetime)',
|
|||
|
|
'original_file_name' => 'string (user filename)',
|
|||
|
|
'upload_description' => 'string (document description)',
|
|||
|
|
'filename_for_upload' => 'string (display filename)'
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'changes' => [
|
|||
|
|
'Added detailed logging with levels (INFO, DEBUG, SUCCESS, WARNING, ERROR)',
|
|||
|
|
'Fixed S3 metadata handling via direct database update',
|
|||
|
|
'Proper filelocationtype=E setting',
|
|||
|
|
'Correct filename field usage',
|
|||
|
|
'Added S3 bucket and key extraction',
|
|||
|
|
'Added file size detection from S3',
|
|||
|
|
'Added s3_metadata_updated flag in response'
|
|||
|
|
]
|
|||
|
|
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|||
|
|
}
|
|||
|
|
?>
|