'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 ]); $stats = [ 'total_markers' => 0, 'restored' => 0, 'failed' => 0, 'skipped' => 0, 'errors' => [] ]; $processedKeys = []; // Для отслеживания уже обработанных ключей echo "Поиск delete markers в префиксе: $prefix\n"; echo "Ограничение: " . ($limit ? "$limit файлов" : "нет") . "\n\n"; $isTruncated = true; $continuationToken = null; $pageCount = 0; $maxPages = isset($argv[4]) ? (int)$argv[4] : 10; // БЕЗОПАСНО: максимум 10 страниц по умолчанию (можно увеличить через параметр) echo "⚠️ ВНИМАНИЕ: Обработка ограничена {$maxPages} страницами для безопасности\n"; echo " Для обработки большего количества используйте: php restore_all_deleted_files.php [--dry-run] [limit] [prefix] [maxPages]\n\n"; while ($isTruncated && $pageCount < $maxPages && (!$limit || $stats['restored'] + $stats['failed'] < $limit)) { $params = [ 'Bucket' => $s3Bucket, 'Prefix' => $prefix, 'MaxKeys' => 100 // БЕЗОПАСНО: уменьшено с 1000 до 100 для снижения нагрузки ]; if ($continuationToken) { $params['ContinuationToken'] = $continuationToken; } echo "Обработка страницы " . ($pageCount + 1) . "/{$maxPages}...\r"; try { $versions = $s3Client->listObjectVersions($params); $pageCount++; // БЕЗОПАСНОСТЬ: пауза между страницами для снижения нагрузки if ($pageCount < $maxPages && $isTruncated) { usleep(500000); // 0.5 секунды пауза между страницами } if (isset($versions['DeleteMarkers']) && !empty($versions['DeleteMarkers'])) { foreach ($versions['DeleteMarkers'] as $marker) { $key = $marker['Key']; $versionId = $marker['VersionId']; $deleteDate = $marker['LastModified'] ?? 'не указана'; // Пропускаем, если уже обработали этот ключ if (isset($processedKeys[$key])) { continue; } $stats['total_markers']++; // Проверяем лимит if ($limit && ($stats['restored'] + $stats['failed']) >= $limit) { break 2; // Выходим из обоих циклов } // Пропускаем папки (заканчиваются на /) if (substr($key, -1) === '/') { $stats['skipped']++; continue; } try { if (!$dryRun) { // Сначала проверяем, есть ли версии файла try { $versionsList = $s3Client->listObjectVersions([ 'Bucket' => $s3Bucket, 'Prefix' => $key, 'MaxKeys' => 10 ]); $hasVersions = isset($versionsList['Versions']) && !empty($versionsList['Versions']); if ($hasVersions) { // Есть версии - удаляем delete marker и файл восстановится автоматически $s3Client->deleteObject([ 'Bucket' => $s3Bucket, 'Key' => $key, 'VersionId' => $versionId ]); // Проверяем, восстановился ли файл try { $headResult = $s3Client->headObject([ 'Bucket' => $s3Bucket, 'Key' => $key ]); $stats['restored']++; $processedKeys[$key] = true; // БЕЗОПАСНОСТЬ: пауза каждые 10 файлов if ($stats['restored'] % 10 == 0) { echo "Восстановлено: {$stats['restored']} файлов...\r"; usleep(200000); // 0.2 секунды пауза } } catch (\Aws\Exception\AwsException $e) { if ($e->getAwsErrorCode() == 'NotFound') { // Файл все еще не доступен, пробуем восстановить последнюю версию вручную $latestVersion = $versionsList['Versions'][0]; $latestVersionId = $latestVersion['VersionId']; try { // Копируем версию в текущий объект $s3Client->copyObject([ 'Bucket' => $s3Bucket, 'CopySource' => urlencode($s3Bucket . '/' . $key) . '?versionId=' . $latestVersionId, 'Key' => $key ]); $stats['restored']++; $processedKeys[$key] = true; if ($stats['restored'] % 100 == 0) { echo "Восстановлено: {$stats['restored']} файлов...\r"; } } catch (\Aws\Exception\AwsException $e2) { $stats['failed']++; $stats['errors'][] = "Ошибка восстановления версии $key: " . $e2->getMessage(); } } else { $stats['failed']++; $stats['errors'][] = "Ошибка проверки $key: " . $e->getMessage(); } } } else { // Нет версий - файл удален безвозвратно $stats['failed']++; $stats['errors'][] = "Не найдена версия для восстановления: $key (файл удален безвозвратно)"; } } catch (\Aws\Exception\AwsException $e) { $stats['failed']++; $stats['errors'][] = "Ошибка проверки версий для $key: " . $e->getMessage(); } } else { // Dry-run режим - проверяем наличие версий try { $versionsList = $s3Client->listObjectVersions([ 'Bucket' => $s3Bucket, 'Prefix' => $key, 'MaxKeys' => 1 ]); if (isset($versionsList['Versions']) && !empty($versionsList['Versions'])) { $stats['restored']++; } else { $stats['failed']++; } $processedKeys[$key] = true; if (($stats['restored'] + $stats['failed']) % 100 == 0) { echo "Проверено: " . ($stats['restored'] + $stats['failed']) . " файлов...\r"; } } catch (\Aws\Exception\AwsException $e) { $stats['failed']++; $processedKeys[$key] = true; } } } catch (\Aws\Exception\AwsException $e) { $stats['failed']++; $stats['errors'][] = "Ошибка удаления delete marker для $key: " . $e->getMessage(); if (count($stats['errors']) <= 10) { echo "\n Ошибка: {$stats['errors'][count($stats['errors']) - 1]}\n"; } } } } $isTruncated = isset($versions['IsTruncated']) && $versions['IsTruncated']; $continuationToken = isset($versions['NextContinuationToken']) ? $versions['NextContinuationToken'] : null; if (!$isTruncated) { break; } } catch (\Aws\Exception\AwsException $e) { echo "\n❌ Ошибка при обработке страницы: " . $e->getMessage() . "\n"; break; } } echo "\n\n"; echo str_repeat("=", 80) . "\n"; echo "ИТОГОВЫЙ ОТЧЕТ:\n\n"; echo "Всего найдено delete markers: {$stats['total_markers']}\n"; echo "Восстановлено файлов: {$stats['restored']}\n"; echo "Ошибок: {$stats['failed']}\n"; echo "Пропущено (папки): {$stats['skipped']}\n\n"; if (!empty($stats['errors']) && count($stats['errors']) <= 20) { echo "Ошибки (первые " . count($stats['errors']) . "):\n"; foreach ($stats['errors'] as $error) { echo " - $error\n"; } echo "\n"; } elseif (!empty($stats['errors'])) { echo "Всего ошибок: " . count($stats['errors']) . " (показаны первые 20)\n"; foreach (array_slice($stats['errors'], 0, 20) as $error) { echo " - $error\n"; } echo "\n"; } if ($dryRun) { echo "⚠️ Это был режим проверки. Для реального восстановления запустите:\n"; echo " php restore_all_deleted_files.php\n\n"; } else { echo "✅ Восстановление завершено!\n"; echo " Проверьте доступность файлов в интерфейсе CRM\n\n"; } // Сохраняем статистику в файл $logFile = '/var/www/fastuser/data/www/crm.clientright.ru/restore_log_' . date('Y-m-d_H-i-s') . '.json'; file_put_contents($logFile, json_encode($stats, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); echo "📝 Лог сохранен в: $logFile\n"; } catch (Exception $e) { echo "❌ КРИТИЧЕСКАЯ ОШИБКА: " . $e->getMessage() . "\n"; echo "Trace: " . $e->getTraceAsString() . "\n"; }