'latest', 'region' => $config['s3']['region'], 'endpoint' => $config['s3']['endpoint'], 'use_path_style_endpoint' => true, 'credentials' => [ 'key' => $config['s3']['key'], 'secret' => $config['s3']['secret'], ], 'suppress_php_deprecation_warning' => true ]); // Получаем документы проекта из БД echo "Подключение к БД...\n"; $db = PearDatabase::getInstance(); echo "✅ БД подключена\n"; echo "Выполнение запроса...\n"; $result = $db->pquery(" SELECT n.notesid, n.filename, n.filelocationtype, n.s3_bucket, n.s3_key, n.filesize FROM vtiger_notes n INNER JOIN vtiger_senotesrel sn ON sn.notesid = n.notesid WHERE sn.crmid = ? ORDER BY n.notesid ", array($projectId)); echo "✅ Запрос выполнен\n"; $totalDocs = $db->num_rows($result); echo "Найдено документов в БД: {$totalDocs}\n\n"; if ($totalDocs == 0) { echo "Документы не найдены!\n"; exit(0); } $stats = [ 'total_docs' => $totalDocs, 'existing' => 0, 'deleted' => 0, 'missing' => 0, 'restored' => 0, 'failed' => 0, 'errors' => [] ]; // Проверяем каждый документ while ($row = $db->fetch_array($result)) { $s3Key = $row['s3_key']; $docId = $row['notesid']; $filename = $row['filename']; echo "Документ ID: {$docId} | Файл: {$filename}\n"; if (empty($s3Key)) { echo " ⚠️ Нет S3 ключа\n\n"; $stats['missing']++; continue; } echo " S3 ключ: {$s3Key}\n"; // Проверяем существование файла $exists = $s3Client->doesObjectExist($s3Bucket, $s3Key); if ($exists) { echo " ✅ Файл существует\n\n"; $stats['existing']++; continue; } // Проверяем версии и delete markers try { $versions = $s3Client->listObjectVersions([ 'Bucket' => $s3Bucket, 'Prefix' => $s3Key, ]); $deleteMarker = null; $fileVersion = null; foreach ($versions['Versions'] ?? [] as $version) { if (isset($version['IsDeleteMarker']) && $version['IsDeleteMarker']) { $deleteMarker = $version; } else { $fileVersion = $version; } } if ($deleteMarker) { echo " ❌ Файл удален (delete marker от " . $deleteMarker['LastModified']->format('Y-m-d H:i:s') . ")\n"; if (!$dryRun) { // Удаляем delete marker try { $s3Client->deleteObject([ 'Bucket' => $s3Bucket, 'Key' => $s3Key, 'VersionId' => $deleteMarker['VersionId'], ]); // Если есть версия файла, копируем её if ($fileVersion) { $s3Client->copyObject([ 'Bucket' => $s3Bucket, 'Key' => $s3Key, 'CopySource' => "{$s3Bucket}/{$s3Key}?versionId={$fileVersion['VersionId']}", ]); echo " ✅ Файл восстановлен из версии\n"; } else { echo " ⚠️ Delete marker удален, но версия файла не найдена\n"; } $stats['restored']++; sleep(1); // Пауза между запросами } catch (Exception $e) { echo " ❌ Ошибка восстановления: " . $e->getMessage() . "\n"; $stats['failed']++; $stats['errors'][] = "{$s3Key}: " . $e->getMessage(); } } else { echo " ⏸️ Будет восстановлен (dry-run)\n"; $stats['restored']++; } $stats['deleted']++; } else { echo " ⚠️ Файл отсутствует, но delete marker не найден\n"; $stats['missing']++; } } catch (Exception $e) { echo " ❌ Ошибка проверки версий: " . $e->getMessage() . "\n"; $stats['failed']++; $stats['errors'][] = "{$s3Key}: " . $e->getMessage(); } echo "\n"; } // Итоговая статистика echo str_repeat("=", 80) . "\n"; echo "ИТОГОВАЯ СТАТИСТИКА:\n\n"; echo "Всего документов: {$stats['total_docs']}\n"; echo "Существующих файлов: {$stats['existing']}\n"; echo "Удаленных файлов (delete marker): {$stats['deleted']}\n"; echo "Отсутствующих файлов: {$stats['missing']}\n"; if (!$dryRun) { echo "Восстановлено: {$stats['restored']}\n"; echo "Ошибок: {$stats['failed']}\n"; } else { echo "Будет восстановлено: {$stats['restored']}\n"; } if (!empty($stats['errors'])) { echo "\nОшибки:\n"; foreach ($stats['errors'] as $error) { echo " - {$error}\n"; } } echo "\n=== ГОТОВО ===\n"; } catch (Exception $e) { echo "❌ Критическая ошибка: " . $e->getMessage() . "\n"; echo "Stack trace:\n" . $e->getTraceAsString() . "\n"; exit(1); }