249 lines
9.4 KiB
PHP
249 lines
9.4 KiB
PHP
<?php
|
|
/**
|
|
* n8n Comment Files S3 Migration (Clean JSON Output)
|
|
*
|
|
* Этот скрипт предназначен для вызова из n8n через SSH
|
|
* для автоматической миграции файлов комментариев в S3
|
|
* Выводит ТОЛЬКО чистый JSON без логов
|
|
*/
|
|
|
|
ini_set('memory_limit', '512M');
|
|
set_time_limit(0);
|
|
date_default_timezone_set('Europe/Moscow');
|
|
|
|
// Параметры по умолчанию
|
|
$defaults = [
|
|
'limit' => 100,
|
|
'dry_run' => 0
|
|
];
|
|
|
|
// Получение параметров из командной строки или переменных окружения
|
|
$limit = isset($argv[1]) ? (int)$argv[1] : (int)($_ENV['LIMIT'] ?? $defaults['limit']);
|
|
$dryRun = isset($argv[2]) ? (int)$argv[2] : (int)($_ENV['DRY_RUN'] ?? $defaults['dry_run']);
|
|
|
|
// Валидация параметров
|
|
$limit = max(1, $limit); // Минимум 1 файл
|
|
$dryRun = $dryRun ? 1 : 0;
|
|
|
|
// Подключение к базе данных
|
|
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
|
|
$mysqli = new mysqli($dbconfig['db_server'], $dbconfig['db_username'], $dbconfig['db_password'], $dbconfig['db_name']);
|
|
if ($mysqli->connect_error) {
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'error' => 'Database connection failed: ' . $mysqli->connect_error,
|
|
'exit_code' => 3
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit(3);
|
|
}
|
|
$mysqli->set_charset("utf8");
|
|
|
|
// Подключение S3 сервиса
|
|
require_once '/var/www/fastuser/data/www/crm.clientright.ru/include/Storage/S3StorageService.php';
|
|
$s3Service = new S3StorageService();
|
|
$s3Bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
|
|
|
// Поиск файлов комментариев без S3 метаданных
|
|
$query = "SELECT a.attachmentsid, a.name, a.path, a.storedname, a.type,
|
|
m.modcommentsid, m.commentcontent,
|
|
s.crmid
|
|
FROM vtiger_attachments a
|
|
INNER JOIN vtiger_seattachmentsrel s ON s.attachmentsid = a.attachmentsid
|
|
INNER JOIN vtiger_modcomments m ON m.modcommentsid = s.crmid
|
|
WHERE (a.s3_key IS NULL OR a.s3_key = '')
|
|
AND NOT (a.name IS NULL OR a.name = '')
|
|
AND NOT (a.name LIKE 'file_15_%')
|
|
AND NOT (a.name = '7 заявление потребителя')
|
|
ORDER BY a.attachmentsid ASC
|
|
LIMIT ?";
|
|
|
|
$stmt = $mysqli->prepare($query);
|
|
if (!$stmt) {
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'error' => 'Query prepare failed: ' . $mysqli->error,
|
|
'exit_code' => 3
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit(3);
|
|
}
|
|
|
|
$stmt->bind_param('i', $limit);
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
|
|
$totalProcessed = 0;
|
|
$successfullyMigrated = 0;
|
|
$failedMigrations = 0;
|
|
$skippedFiles = 0;
|
|
$migratedFiles = [];
|
|
$problematicFiles = [];
|
|
|
|
if ($result->num_rows > 0) {
|
|
while ($row = $result->fetch_assoc()) {
|
|
$totalProcessed++;
|
|
$attachmentsid = $row['attachmentsid'];
|
|
$filename = $row['name'];
|
|
$path = $row['path'];
|
|
$storedname = $row['storedname'];
|
|
$modcommentsid = $row['modcommentsid'];
|
|
|
|
// Поиск локального файла
|
|
$storageDir = '/var/www/fastuser/data/www/crm.clientright.ru/';
|
|
$localFilePath = null;
|
|
|
|
// Основной путь из vtiger_attachments
|
|
$mainPath = $storageDir . $path . $attachmentsid . '_' . $storedname;
|
|
if (file_exists($mainPath) && is_readable($mainPath)) {
|
|
$localFilePath = $mainPath;
|
|
}
|
|
|
|
// Если не найден по основному пути, используем альтернативные пути
|
|
if ($localFilePath === null) {
|
|
$alternativePaths = [
|
|
$storageDir . 'storage/' . $filename,
|
|
$storageDir . 'storage/' . $attachmentsid . '_' . $filename,
|
|
$storageDir . 'storage/' . $storedname,
|
|
];
|
|
|
|
foreach ($alternativePaths as $altPath) {
|
|
if (file_exists($altPath) && is_readable($altPath)) {
|
|
$localFilePath = $altPath;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Проверка существования файла
|
|
if ($localFilePath === null) {
|
|
// Помечаем файл как проблемный
|
|
$problematic_filename = "[PROBLEM_" . date('Y-m-d_H-i-s') . "] " . $filename;
|
|
|
|
if (!$dryRun) {
|
|
$update_query = "UPDATE vtiger_attachments SET name = ? WHERE attachmentsid = ?";
|
|
$update_stmt = $mysqli->prepare($update_query);
|
|
if ($update_stmt) {
|
|
$update_stmt->bind_param('si', $problematic_filename, $attachmentsid);
|
|
if ($update_stmt->execute()) {
|
|
$problematicFiles[] = [
|
|
'attachmentsid' => $attachmentsid,
|
|
'modcommentsid' => $modcommentsid,
|
|
'original_filename' => $filename,
|
|
'problematic_filename' => $problematic_filename,
|
|
'reason' => 'Physical file not found'
|
|
];
|
|
$skippedFiles++;
|
|
} else {
|
|
$failedMigrations++;
|
|
}
|
|
$update_stmt->close();
|
|
} else {
|
|
$failedMigrations++;
|
|
}
|
|
} else {
|
|
$problematicFiles[] = [
|
|
'attachmentsid' => $attachmentsid,
|
|
'modcommentsid' => $modcommentsid,
|
|
'original_filename' => $filename,
|
|
'reason' => 'Physical file not found (dry run)'
|
|
];
|
|
$skippedFiles++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!$dryRun) {
|
|
try {
|
|
// Загрузка в S3 (используем правильную сигнатуру)
|
|
$s3_result = $s3Service->put($localFilePath, $attachmentsid, $filename);
|
|
|
|
if ($s3_result && isset($s3_result['url'])) {
|
|
$s3_etag = isset($s3_result['etag']) ? trim($s3_result['etag'], '"') : '';
|
|
$s3_url = $s3_result['url'];
|
|
$s3_key = $s3_result['key'];
|
|
|
|
// Обновление базы данных vtiger_attachments
|
|
$update_query = "UPDATE vtiger_attachments SET
|
|
s3_key = ?,
|
|
s3_bucket = ?,
|
|
s3_etag = ?,
|
|
s3_url = ?
|
|
WHERE attachmentsid = ?";
|
|
|
|
$update_stmt = $mysqli->prepare($update_query);
|
|
if (!$update_stmt) {
|
|
$failedMigrations++;
|
|
continue;
|
|
}
|
|
|
|
$update_stmt->bind_param('ssssi', $s3_key, $s3Bucket, $s3_etag, $s3_url, $attachmentsid);
|
|
|
|
if ($update_stmt->execute()) {
|
|
$migratedFiles[] = [
|
|
'attachmentsid' => $attachmentsid,
|
|
'modcommentsid' => $modcommentsid,
|
|
'filename' => $filename,
|
|
's3_url' => $s3_url,
|
|
's3_key' => $s3_key,
|
|
'local_file_deleted' => unlink($localFilePath)
|
|
];
|
|
$successfullyMigrated++;
|
|
} else {
|
|
$failedMigrations++;
|
|
}
|
|
$update_stmt->close();
|
|
} else {
|
|
$failedMigrations++;
|
|
}
|
|
} catch (Exception $e) {
|
|
$failedMigrations++;
|
|
}
|
|
} else {
|
|
$migratedFiles[] = [
|
|
'attachmentsid' => $attachmentsid,
|
|
'modcommentsid' => $modcommentsid,
|
|
'filename' => $filename,
|
|
'local_file_path' => $localFilePath,
|
|
'dry_run' => true
|
|
];
|
|
$successfullyMigrated++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$stmt->close();
|
|
$mysqli->close();
|
|
|
|
// JSON вывод для n8n
|
|
$jsonOutput = [
|
|
'status' => 'success',
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'summary' => [
|
|
'total_processed' => $totalProcessed,
|
|
'successfully_migrated' => $successfullyMigrated,
|
|
'failed' => $failedMigrations,
|
|
'marked_problematic' => $skippedFiles,
|
|
'dry_run' => $dryRun ? true : false
|
|
],
|
|
'migrated_files' => $migratedFiles,
|
|
'problematic_files' => $problematicFiles,
|
|
'exit_code' => 0
|
|
];
|
|
|
|
// Определяем статус и код выхода
|
|
if ($failedMigrations > 0) {
|
|
$jsonOutput['status'] = 'partial_error';
|
|
$jsonOutput['exit_code'] = 2;
|
|
} elseif ($skippedFiles > 0) {
|
|
$jsonOutput['status'] = 'warning';
|
|
$jsonOutput['exit_code'] = 1;
|
|
}
|
|
|
|
// Выводим ТОЛЬКО чистый JSON
|
|
echo json_encode($jsonOutput, JSON_UNESCAPED_UNICODE);
|
|
|
|
// Возвращаем код выхода для n8n
|
|
exit($jsonOutput['exit_code']);
|
|
?>
|