setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "✅ Подключено к БД\n\n"; } catch (PDOException $e) { die("❌ Ошибка подключения: " . $e->getMessage() . "\n"); } // Параметры $projectId = isset($argv[1]) ? (int)$argv[1] : null; $dryRun = in_array('--dry-run', $argv); if (!$projectId) { echo "❌ Укажите ID проекта!\n"; echo "Использование: php migrate_quick.php PROJECT_ID [--dry-run]\n"; echo "\nПример: php migrate_quick.php 3624 --dry-run\n"; exit(1); } echo "🔄 БЫСТРАЯ МИГРАЦИЯ PROJECT → Project/\n"; echo "==========================================\n"; if ($dryRun) { echo "⚠️ РЕЖИМ DRY-RUN - НИЧЕГО НЕ БУДЕТ ИЗМЕНЕНО\n"; } echo "\n"; // Получаем проект $stmt = $pdo->prepare("SELECT projectname FROM vtiger_project WHERE projectid = ?"); $stmt->execute([$projectId]); $project = $stmt->fetch(PDO::FETCH_ASSOC); if (!$project) { echo "❌ Проект не найден!\n"; exit(1); } $projectName = $project['projectname']; echo "📁 Проект: $projectName (ID: $projectId)\n\n"; // Получаем файлы проекта $stmt = $pdo->prepare(" SELECT n.notesid, n.filename, n.title FROM vtiger_notes n INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid WHERE sr.crmid = ? AND n.filelocationtype = 'E' "); $stmt->execute([$projectId]); $files = $stmt->fetchAll(PDO::FETCH_ASSOC); // Подключаем FilePathManager и S3Client заранее require_once(__DIR__ . '/FilePathManager.php'); require_once(__DIR__ . '/S3Client.php'); $pathMgr = new FilePathManager(); // S3 конфигурация $s3Config = [ 'version' => 'latest', 'region' => 'ru-1', 'endpoint' => 'https://s3.twcstorage.ru', 'bucket' => 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c', 'use_path_style_endpoint' => true, 'key' => 'YCAJEfh7Z06ixD_9fFdVa3BUy', 'secret' => 'YCM9xQmPCOa3L1iO_LS08J0cYWiuUpk3s7q3VSmR' ]; $s3 = new S3Client($s3Config); $totalFiles = count($files); echo "📊 Найдено файлов: $totalFiles\n\n"; if ($totalFiles == 0) { echo "✅ Нет файлов для миграции!\n"; exit(0); } // Статистика $stats = ['processed' => 0, 'updated' => 0, 'errors' => 0]; foreach ($files as $file) { $stats['processed']++; $notesId = $file['notesid']; $oldUrl = $file['filename']; echo "[$stats[processed]/$totalFiles] Документ ID: $notesId\n"; echo " Старый URL: " . substr($oldUrl, 0, 100) . "...\n"; // Извлекаем относительный путь из URL if (preg_match('#/Documents/(.+)$#', $oldUrl, $matches)) { $oldS3Path = $matches[1]; // например: "3/file.pdf" $oldS3Key = "crm2/CRM_Active_Files/Documents/" . urldecode($oldS3Path); // Генерируем новый путь через FilePathManager $fileName = basename(urldecode($oldS3Path)); $documentTitle = $file['title'] ?: null; // getFilePath возвращает ПОЛНЫЙ путь с префиксом $newFullPath = $pathMgr->getFilePath('Project', $projectId, $notesId, $fileName, $documentTitle, $projectName); $newS3Key = $newFullPath; // Для БД нужен путь БЕЗ префикса (только Project/...) $newRelativePath = str_replace('crm2/CRM_Active_Files/Documents/', '', $newFullPath); echo " Новый путь: $newRelativePath\n"; echo " S3: $oldS3Key → $newS3Key\n"; if (!$dryRun) { try { // Проверяем существование старого файла if (!$s3->fileExists($oldS3Key)) { echo " ⚠️ Файл не найден в S3: $oldS3Key\n\n"; $stats['errors']++; continue; } // Проверяем, не существует ли новый if ($s3->fileExists($newS3Key)) { echo " ⚠️ Целевой файл уже существует\n\n"; $stats['errors']++; continue; } // Скачиваем во временный файл $tempFile = $s3->downloadToTemp($oldS3Key); if (!$tempFile) { throw new Exception("Не удалось скачать файл"); } echo " ✅ Файл скачан во временный файл\n"; // Загружаем в новое место if ($s3->uploadFile($tempFile, $newS3Key)) { echo " ✅ Файл загружен в новое место\n"; // Удаляем временный файл @unlink($tempFile); // Удаляем старый файл в S3 $s3->deleteObject($oldS3Key); echo " ✅ Старый файл удален\n"; // Обновляем БД $updateStmt = $pdo->prepare("UPDATE vtiger_notes SET filename = ?, filelocationtype = 'S' WHERE notesid = ?"); $updateStmt->execute([$newRelativePath, $notesId]); echo " ✅ БД обновлена\n"; $stats['updated']++; echo " ✅ УСПЕШНО!\n\n"; } else { throw new Exception("Не удалось скопировать файл в S3"); } } catch (Exception $e) { echo " ❌ ОШИБКА: " . $e->getMessage() . "\n\n"; $stats['errors']++; } } else { echo " [DRY-RUN] S3: копирование $oldS3Key → $newS3Key\n"; echo " [DRY-RUN] БД: filename = '$newRelativePath', filelocationtype = 'S'\n\n"; $stats['updated']++; } } else { echo " ⚠️ Не удалось извлечь путь\n\n"; $stats['errors']++; } } // Итоги echo "\n==========================================\n"; echo "📊 СТАТИСТИКА:\n"; echo "==========================================\n"; echo "Обработано: $stats[processed]\n"; echo "Обновлено: $stats[updated]\n"; echo "Ошибок: $stats[errors]\n"; echo "\n"; if ($dryRun) { echo "⚠️ Это был DRY-RUN. Запустите без --dry-run для реальной миграции.\n"; } else if ($stats['errors'] == 0) { echo "✅ МИГРАЦИЯ БД ЗАВЕРШЕНА!\n"; echo "\n⚠️ ВНИМАНИЕ: Файлы в S3 НЕ ПЕРЕМЕЩАЛИСЬ!\n"; echo "Nextcloud автоматически увидит их по новым путям.\n"; } else { echo "⚠️ Миграция завершена с ошибками.\n"; } ?>