Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
283 lines
11 KiB
PHP
283 lines
11 KiB
PHP
<?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 ===');
|
||
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);
|
||
// Не блокируем, но логируем для диагностики
|
||
}
|
||
|
||
// Получаем входные данные
|
||
$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);
|
||
}
|
||
|
||
log_message('📤 Processing ' . count($processed_documents) . ' documents via upload_documents_to_crm.php');
|
||
|
||
// ✅ Вызываем upload_documents_to_crm.php напрямую через include (избегаем проблем с HTTP/Nginx)
|
||
// Берем общие параметры из первого документа
|
||
$first_doc = $processed_documents[0];
|
||
|
||
// Формируем данные в формате, который ожидает upload_documents_to_crm.php
|
||
$upload_data = [
|
||
'documents' => $processed_documents,
|
||
'projectid' => $first_doc['projectid'],
|
||
'ticket_id' => $first_doc['ticket_id'],
|
||
'user_id' => 1
|
||
];
|
||
|
||
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());
|
||
json_response([
|
||
'success' => false,
|
||
'error' => 'Failed to load upload script: ' . $e->getMessage()
|
||
], 500);
|
||
}
|
||
|
||
// ✅ Вызываем функцию normalizeInputData для преобразования данных
|
||
$filesArray = normalizeInputData($upload_data);
|
||
|
||
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);
|
||
}
|
||
|
||
// ✅ Вызываем функцию 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']);
|
||
json_response([
|
||
'success' => false,
|
||
'error' => 'Upload failed: ' . $results['error']
|
||
], 500);
|
||
}
|
||
|
||
// ✅ Формируем ответ в формате, который ожидает api_attach_documents.php
|
||
// ✅ Проверяем успешность
|
||
if (is_array($results) && !empty($results)) {
|
||
$results_array = $results;
|
||
|
||
// Формируем ответ
|
||
$processed_results = [];
|
||
$errors = [];
|
||
|
||
foreach ($results_array as $idx => $res) {
|
||
$status = $res['status'] ?? 'unknown';
|
||
|
||
if ($status === 'success') {
|
||
$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 {
|
||
$error_msg = $res['crm_result']['message'] ?? ($res['error'] ?? 'Unknown error');
|
||
$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 {
|
||
$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)));
|
||
json_response([
|
||
'success' => false,
|
||
'error' => is_string($error_msg) ? $error_msg : 'Upload failed'
|
||
], 500);
|
||
}
|