2025-11-02 19:25:04 +03:00
< ? php
/**
* API для привязки документов к проекту / заявке
*
* Использование из n8n :
* POST https :// crm . clientright . ru / api_attach_documents . php
*
* Входные данные ( JSON массив ) :
* [
* {
* " contact_id " : " 320096 " ,
* " project_id " : " 396868 " ,
* " ticket_id " : " 396936 " ,
* " filename " : " boarding_pass.pdf " ,
* " file_type " : " flight_delay_boarding_or_ticket " ,
* " file " : " /bucket/path/to/file.pdf "
* }
* ]
*/
error_reporting ( E_ALL );
ini_set ( 'display_errors' , '0' );
// Функция для логирования
function log_message ( $message ) {
$timestamp = date ( 'Y-m-d H:i:s' );
$line = " [ $timestamp ] $message\n " ;
@ file_put_contents ( __DIR__ . '/logs/api_attach_documents.log' , $line , FILE_APPEND | LOCK_EX );
error_log ( '[api_attach_documents] ' . $message );
}
// Функция для JSON ответа
function json_response ( $data , $code = 200 ) {
if ( ! headers_sent ()) {
http_response_code ( $code );
header ( 'Content-Type: application/json; charset=utf-8' );
header ( 'Access-Control-Allow-Origin: *' );
header ( 'Access-Control-Allow-Methods: POST, OPTIONS' );
header ( 'Access-Control-Allow-Headers: Content-Type' );
}
echo json_encode ( $data , JSON_UNESCAPED_UNICODE );
exit ;
}
// CORS preflight
if ( $_SERVER [ 'REQUEST_METHOD' ] === 'OPTIONS' ) {
json_response ([ 'status' => 'ok' ]);
}
// Только POST
if ( $_SERVER [ 'REQUEST_METHOD' ] !== 'POST' ) {
json_response ([ 'success' => false , 'error' => 'Method not allowed' ], 405 );
}
log_message ( '=== START API REQUEST ===' );
2026-03-26 14:19:01 +03:00
log_message ( 'Host: ' . ( $_SERVER [ 'HTTP_HOST' ] ? ? 'not set' ));
log_message ( 'SERVER_NAME: ' . ( $_SERVER [ 'SERVER_NAME' ] ? ? 'not set' ));
log_message ( 'REQUEST_URI: ' . ( $_SERVER [ 'REQUEST_URI' ] ? ? 'not set' ));
// Проверяем, что запрос идёт на правильный домен
$host = $_SERVER [ 'HTTP_HOST' ] ? ? $_SERVER [ 'SERVER_NAME' ] ? ? '' ;
if ( ! empty ( $host ) && strpos ( $host , 'crm.clientright.ru' ) === false && strpos ( $host , 'aeroflot.clientright.ru' ) !== false ) {
log_message ( '⚠️ WARNING: Request came to wrong virtual host: ' . $host );
// Н е блокируем, но логируем для диагностики
}
2025-11-02 19:25:04 +03:00
// Получаем входные данные
$input = file_get_contents ( 'php://input' );
log_message ( 'Raw input: ' . substr ( $input , 0 , 500 ));
$input = ltrim ( $input , " \xEF \xBB \xBF \x00 \x09 \x0A \x0D \x20 " );
$data = json_decode ( $input , true );
if ( json_last_error () !== JSON_ERROR_NONE ) {
log_message ( '❌ JSON Error: ' . json_last_error_msg ());
json_response ([
'success' => false ,
'error' => 'Invalid JSON: ' . json_last_error_msg ()
], 400 );
}
// Поддерживаем как массив, так и одиночный объект
$documents_array = is_array ( $data ) && isset ( $data [ 0 ]) ? $data : [ $data ];
log_message ( 'Processing ' . count ( $documents_array ) . ' document(s)' );
// Обрабатываем каждый документ
$processed_documents = [];
$S3_HOST = 'https://s3.twcstorage.ru' ;
foreach ( $documents_array as $idx => $doc ) {
$contact_id = $doc [ 'contact_id' ] ? ? null ;
$project_id = $doc [ 'project_id' ] ? ? null ;
$ticket_id = $doc [ 'ticket_id' ] ? ? null ;
// Поддерживаем о б а формата: file и file_url
$file_path = $doc [ 'file' ] ? ? $doc [ 'file_url' ] ? ? null ;
if ( ! $file_path ) {
log_message ( " ❌ Document # { $idx } : missing 'file' or 'file_url' " );
continue ;
}
// Строим полный S3 URL
if ( strpos ( $file_path , 'http' ) === 0 ) {
$file_url = $file_path ;
} elseif ( strpos ( $file_path , '/' ) === 0 ) {
$file_url = $S3_HOST . $file_path ;
} else {
$file_url = $S3_HOST . '/' . $file_path ;
}
// Поддерживаем о б а формата: filename и file_name
$file_name = $doc [ 'filename' ] ? ? $doc [ 'file_name' ] ? ? null ;
if ( ! $file_name ) {
log_message ( " ❌ Document # { $idx } : missing 'filename' or 'file_name' " );
continue ;
}
$file_type = $doc [ 'file_type' ] ? ? 'Документ' ;
// Валидация обязательных полей
if ( ! $contact_id || ! $project_id ) {
log_message ( " ❌ Document # { $idx } : missing contact_id or project_id " );
continue ;
}
log_message ( " [ { $idx } ] { $file_name } (type: { $file_type } ) " );
log_message ( " Contact: { $contact_id } , Project: { $project_id } , Ticket: " . ( $ticket_id ? : 'N/A' ));
log_message ( " File URL: { $file_url } " );
$processed_documents [] = [
'url' => $file_url ,
'file_name' => $file_name ,
'description' => $file_type ,
'projectid' => ( int ) $project_id ,
'ticket_id' => $ticket_id ? ( int ) $ticket_id : null ,
'contactid' => ( int ) $contact_id ,
'pages' => 1
];
}
if ( empty ( $processed_documents )) {
log_message ( '❌ No valid documents to process' );
json_response ([
'success' => false ,
'error' => 'No valid documents to process'
], 400 );
}
2026-03-26 14:19:01 +03:00
log_message ( '📤 Processing ' . count ( $processed_documents ) . ' documents via upload_documents_to_crm.php' );
2025-11-02 19:25:04 +03:00
2026-03-26 14:19:01 +03:00
// ✅ Вызываем upload_documents_to_crm.php напрямую через include (избегаем проблем с HTTP/Nginx)
2025-11-02 19:25:04 +03:00
// Берем общие параметры из первого документа
$first_doc = $processed_documents [ 0 ];
2026-03-26 14:19:01 +03:00
// Формируем данные в формате, который ожидает upload_documents_to_crm.php
$upload_data = [
2025-11-02 19:25:04 +03:00
'documents' => $processed_documents ,
'projectid' => $first_doc [ 'projectid' ],
'ticket_id' => $first_doc [ 'ticket_id' ],
'user_id' => 1
2026-03-26 14:19:01 +03:00
];
log_message ( 'Payload: ' . substr ( json_encode ( $upload_data , JSON_UNESCAPED_UNICODE ), 0 , 500 ));
// ✅ Вызываем upload_documents_to_crm.php напрямую через include (избегаем проблем с HTTP)
// Загружаем функции из upload_documents_to_crm.php
$upload_script_path = __DIR__ . '/upload_documents_to_crm.php' ;
// ✅ Перехватываем вывод (на случай, если скрипт что-то выводит)
ob_start ();
// ✅ Помечаем, что скрипт вызван из API — чтобы upload_documents_to_crm не выполнял тестовый GET-блок (который создавал лишний документ "Иск" по проекту 354918 при каждом запросе)
define ( 'UPLOAD_DOCUMENTS_CALLED_FROM_API' , true );
// ✅ Сохраняем оригинальный REQUEST_METHOD и устанавливаем GET, чтобы предотвратить выполнение основного POST-кода в upload_documents_to_crm
$original_request_method = $_SERVER [ 'REQUEST_METHOD' ] ? ? null ;
$_SERVER [ 'REQUEST_METHOD' ] = 'GET' ;
// ✅ Загружаем upload_documents_to_crm.php (только функции; тестовый GET-блок не выполнится из-за UPLOAD_DOCUMENTS_CALLED_FROM_API)
try {
require_once $upload_script_path ;
// ✅ Восстанавливаем оригинальный REQUEST_METHOD
if ( $original_request_method !== null ) {
$_SERVER [ 'REQUEST_METHOD' ] = $original_request_method ;
}
} catch ( Throwable $e ) {
ob_end_clean ();
// ✅ Восстанавливаем оригинальный REQUEST_METHOD даже при ошибке
if ( $original_request_method !== null ) {
$_SERVER [ 'REQUEST_METHOD' ] = $original_request_method ;
}
log_message ( '❌ Fatal error loading upload_documents_to_crm.php: ' . $e -> getMessage () . ' in ' . $e -> getFile () . ':' . $e -> getLine ());
2025-11-02 19:25:04 +03:00
json_response ([
'success' => false ,
2026-03-26 14:19:01 +03:00
'error' => 'Failed to load upload script: ' . $e -> getMessage ()
2025-11-02 19:25:04 +03:00
], 500 );
}
2026-03-26 14:19:01 +03:00
// ✅ Вызываем функцию normalizeInputData для преобразования данных
$filesArray = normalizeInputData ( $upload_data );
2025-11-02 19:25:04 +03:00
2026-03-26 14:19:01 +03:00
if ( ! is_array ( $filesArray ) || empty ( $filesArray )) {
ob_end_clean ();
log_message ( '❌ Error: Failed to normalize input data. Type: ' . gettype ( $filesArray ) . ', Empty: ' . ( empty ( $filesArray ) ? 'yes' : 'no' ));
json_response ([
'success' => false ,
'error' => 'Failed to normalize input data'
], 400 );
}
2025-11-02 19:25:04 +03:00
2026-03-26 14:19:01 +03:00
// ✅ Вызываем функцию createDocumentsInCRM напрямую
log_message ( '🚀 Calling createDocumentsInCRM with ' . count ( $filesArray ) . ' files' );
$results = createDocumentsInCRM ( $filesArray );
log_message ( '🔍 createDocumentsInCRM returned. Type: ' . gettype ( $results ) . ', Is array: ' . ( is_array ( $results ) ? 'yes, count: ' . count ( $results ) : 'no' ));
// ✅ Очищаем output buffer
ob_end_clean ();
// ✅ Проверяем результат
if ( isset ( $results [ 'error' ])) {
log_message ( '❌ Error: ' . $results [ 'error' ]);
2025-11-02 19:25:04 +03:00
json_response ([
'success' => false ,
2026-03-26 14:19:01 +03:00
'error' => 'Upload failed: ' . $results [ 'error' ]
2025-11-02 19:25:04 +03:00
], 500 );
}
2026-03-26 14:19:01 +03:00
// ✅ Формируем ответ в формате, который ожидает api_attach_documents.php
// ✅ Проверяем успешность
if ( is_array ( $results ) && ! empty ( $results )) {
$results_array = $results ;
2025-11-02 19:25:04 +03:00
// Формируем ответ
$processed_results = [];
$errors = [];
foreach ( $results_array as $idx => $res ) {
2026-03-26 14:19:01 +03:00
$status = $res [ 'status' ] ? ? 'unknown' ;
if ( $status === 'success' ) {
2025-11-02 19:25:04 +03:00
$crm_result = $res [ 'crm_result' ] ? ? [];
$processed_results [] = [
'document_id' => $crm_result [ 'document_id' ] ? ? null ,
'document_numeric_id' => $crm_result [ 'document_numeric_id' ] ? ? null ,
'attached_to' => isset ( $res [ 'ticket_id' ]) && $res [ 'ticket_id' ] ? 'ticket' : 'project' ,
'attached_to_id' => $res [ 'ticket_id' ] ? ? $res [ 'projectid' ] ? ? null ,
'file_name' => $res [ 'file_name' ] ? ? null ,
'file_type' => $res [ 'description' ] ? ? null ,
's3_bucket' => $crm_result [ 's3_bucket' ] ? ? null ,
's3_key' => $crm_result [ 's3_key' ] ? ? null ,
'file_size' => $crm_result [ 'file_size' ] ? ? null ,
'message' => $crm_result [ 'message' ] ? ? null
];
} else {
2026-03-26 14:19:01 +03:00
$error_msg = $res [ 'crm_result' ][ 'message' ] ? ? ( $res [ 'error' ] ? ? 'Unknown error' );
2025-11-02 19:25:04 +03:00
$errors [] = [
'file_name' => $res [ 'file_name' ] ? ? 'Unknown' ,
'error' => $error_msg
];
}
}
json_response ([
'success' => true ,
'total_processed' => count ( $results_array ),
'successful' => count ( $processed_results ),
'failed' => count ( $errors ),
'results' => $processed_results ,
'errors' => ! empty ( $errors ) ? $errors : null
]);
} else {
2026-03-26 14:19:01 +03:00
$error_msg = is_array ( $results ) && isset ( $results [ 'error' ]) ? $results [ 'error' ] : 'Upload failed' ;
log_message ( '❌ Upload failed: ' . ( is_string ( $error_msg ) ? $error_msg : json_encode ( $error_msg )));
2025-11-02 19:25:04 +03:00
json_response ([
'success' => false ,
2026-03-26 14:19:01 +03:00
'error' => is_string ( $error_msg ) ? $error_msg : 'Upload failed'
2025-11-02 19:25:04 +03:00
], 500 );
}