Files
crm.clientright.ru/crm_extensions/file_storage/n8n_comment_migration_clean.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']);
?>