Files
crm.clientright.ru/search_in_trash.php
Fedor 840acca51a feat(documents): дедупликация documents_meta и исправление field_label
- Исправлен N8N_CODE_PROCESS_UPLOADED_FILES_FIXED.js: использовать uploads_field_labels[0] вместо [grp]
- Создан SQL_CLAIMSAVE_FIXED_NEW_FLOW_DEDUP.sql с дедупликацией documents_meta
- Создан SQL_CLEANUP_DOCUMENTS_META_DUPLICATES.sql для очистки существующих дубликатов
- Создан полный уникальный индекс idx_document_texts_hash_unique на document_texts(file_hash)
- Добавлен SESSION_LOG_2025-11-28_documents_dedup.md с описанием всех изменений

Fixes:
- field_label теперь корректно отображает 'Переписка' вместо 'group-2'
- documents_meta не накапливает дубликаты при повторных сохранениях
- ON CONFLICT (file_hash) теперь работает для document_texts
2025-11-28 18:16:53 +03:00

260 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
$config = require '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/config.php';
$docIds = [386869, 394973];
$s3Bucket = $config['s3']['bucket'];
echo "Поиск файлов в корзине/удаленных версиях S3\n";
echo str_repeat("=", 80) . "\n\n";
try {
// Получаем информацию о документах из БД
$pdo = new PDO(
"mysql:host={$dbconfig['db_server']};port=3306;dbname={$dbconfig['db_name']};charset=utf8",
$dbconfig['db_username'],
$dbconfig['db_password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// Инициализация S3 клиента
$s3Client = new \Aws\S3\S3Client([
'version' => '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
]);
foreach ($docIds as $docId) {
echo "Поиск файла для документа $docId:\n";
echo str_repeat("-", 80) . "\n";
$stmt = $pdo->prepare('SELECT notesid, title, s3_key, s3_etag, filesize FROM vtiger_notes WHERE notesid = ?');
$stmt->execute([$docId]);
$doc = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$doc) {
echo " Документ не найден в БД\n\n";
continue;
}
echo " Название: {$doc['title']}\n";
echo " Ожидаемый путь: {$doc['s3_key']}\n";
echo " ETag: {$doc['s3_etag']}\n";
echo " Размер: " . number_format($doc['filesize'] / 1024, 2) . " KB\n\n";
$s3Key = $doc['s3_key'];
$targetEtag = trim($doc['s3_etag'], '"');
$targetSize = $doc['filesize'];
// 1. Проверяем версии объекта (если включено versioning)
echo " 1. Проверка версий объекта...\n";
try {
$versions = $s3Client->listObjectVersions([
'Bucket' => $s3Bucket,
'Prefix' => $s3Key,
'MaxKeys' => 100
]);
if (isset($versions['Versions']) && !empty($versions['Versions'])) {
echo " ✅ Найдено версий: " . count($versions['Versions']) . "\n";
foreach ($versions['Versions'] as $version) {
$versionKey = $version['Key'];
$versionId = $version['VersionId'];
$isDeleteMarker = isset($version['IsDeleteMarker']) && $version['IsDeleteMarker'];
$versionEtag = isset($version['ETag']) ? trim($version['ETag'], '"') : null;
$versionSize = isset($version['Size']) ? $version['Size'] : null;
if ($isDeleteMarker) {
echo " ⚠️ Delete Marker: VersionId=$versionId, Дата: " . ($version['LastModified'] ?? 'не указана') . "\n";
} else {
$matchInfo = [];
if ($versionEtag === $targetEtag) {
$matchInfo[] = "ETag совпадает";
}
if ($versionSize && abs($versionSize - $targetSize) <= 100) {
$matchInfo[] = "размер совпадает (" . number_format($versionSize / 1024, 2) . " KB)";
}
echo " ✅ Версия: VersionId=$versionId\n";
echo " Размер: " . ($versionSize ? number_format($versionSize / 1024, 2) . " KB" : 'не указан') . "\n";
echo " ETag: " . ($versionEtag ?: 'не указан') . "\n";
echo " Дата: " . ($version['LastModified'] ?? 'не указана') . "\n";
if (!empty($matchInfo)) {
echo " " . implode(', ', $matchInfo) . "\n";
}
echo "\n";
}
}
} else {
echo " ❌ Версии не найдены (возможно, versioning не включен)\n";
}
} catch (\Aws\Exception\AwsException $e) {
if ($e->getAwsErrorCode() == 'AccessDenied' || strpos($e->getMessage(), 'versioning') !== false) {
echo " ⚠️ Версионирование не включено или нет доступа: " . $e->getAwsErrorCode() . "\n";
} else {
echo " ❌ Ошибка: " . $e->getMessage() . "\n";
}
}
echo "\n";
// 2. Проверяем delete markers
echo " 2. Проверка delete markers...\n";
try {
$deleteMarkers = $s3Client->listObjectVersions([
'Bucket' => $s3Bucket,
'Prefix' => $s3Key,
'MaxKeys' => 100
]);
if (isset($deleteMarkers['DeleteMarkers']) && !empty($deleteMarkers['DeleteMarkers'])) {
echo " ✅ Найдено delete markers: " . count($deleteMarkers['DeleteMarkers']) . "\n";
foreach ($deleteMarkers['DeleteMarkers'] as $marker) {
echo " ⚠️ Delete Marker найден:\n";
echo " VersionId: " . ($marker['VersionId'] ?? 'не указан') . "\n";
echo " Дата удаления: " . ($marker['LastModified'] ?? 'не указана') . "\n";
echo " Ключ: " . ($marker['Key'] ?? 'не указан') . "\n";
echo "\n";
echo " 💡 Файл был удален, но можно восстановить, удалив delete marker\n";
}
} else {
echo " ❌ Delete markers не найдены\n";
}
} catch (\Aws\Exception\AwsException $e) {
echo " ⚠️ Ошибка при проверке delete markers: " . $e->getAwsErrorCode() . "\n";
}
echo "\n";
// 3. Проверяем папки с названиями типа trash, deleted, recycle и т.д.
echo " 3. Поиск в папках корзины...\n";
$trashPrefixes = [
'trash/',
'deleted/',
'recycle/',
'.trash/',
'deleted_files/',
'removed/',
];
$foundInTrash = false;
foreach ($trashPrefixes as $trashPrefix) {
try {
$objects = $s3Client->listObjectsV2([
'Bucket' => $s3Bucket,
'Prefix' => $trashPrefix,
'MaxKeys' => 1000
]);
if (isset($objects['Contents'])) {
foreach ($objects['Contents'] as $object) {
$key = $object['Key'];
// Ищем файлы с ID документа или похожим размером
if (strpos($key, (string)$docId) !== false ||
(isset($object['Size']) && abs($object['Size'] - $targetSize) <= 100)) {
try {
$headResult = $s3Client->headObject([
'Bucket' => $s3Bucket,
'Key' => $key
]);
$fileEtag = isset($headResult['ETag']) ? trim($headResult['ETag'], '"') : null;
echo " ✅ НАЙДЕН в $trashPrefix:\n";
echo " Путь: $key\n";
echo " Размер: " . number_format($headResult['ContentLength'] / 1024, 2) . " KB\n";
echo " ETag: " . ($fileEtag ?: 'не указан') . "\n";
if ($fileEtag === $targetEtag) {
echo " ✅ ТОЧНОЕ СОВПАДЕНИЕ ПО ETAG!\n";
}
$foundInTrash = true;
echo "\n";
} catch (\Aws\Exception\AwsException $e) {
// Пропускаем
}
}
}
}
} catch (\Aws\Exception\AwsException $e) {
// Пропускаем ошибки
}
}
if (!$foundInTrash) {
echo " ❌ Файлы не найдены в папках корзины\n";
}
echo "\n";
// 4. Проверяем, может быть файл был перемещен в другую папку проекта
echo " 4. Поиск по ETag во всех папках проекта...\n";
$projectId = 384256; // Из предыдущих запросов
$projectPrefixes = [
"crm2/CRM_Active_Files/Documents/Project/Гафиев_ООО_ЭДЭКС_$projectId/",
"temp/$projectId/",
];
$foundByEtag = false;
foreach ($projectPrefixes as $prefix) {
try {
$objects = $s3Client->listObjectsV2([
'Bucket' => $s3Bucket,
'Prefix' => $prefix,
'MaxKeys' => 1000
]);
if (isset($objects['Contents'])) {
foreach ($objects['Contents'] as $object) {
$key = $object['Key'];
try {
$headResult = $s3Client->headObject([
'Bucket' => $s3Bucket,
'Key' => $key
]);
$fileEtag = isset($headResult['ETag']) ? trim($headResult['ETag'], '"') : null;
if ($fileEtag === $targetEtag) {
echo " ✅ НАЙДЕН ПО ETAG в $prefix:\n";
echo " Путь: $key\n";
echo " Размер: " . number_format($headResult['ContentLength'] / 1024, 2) . " KB\n";
echo " ETag: $fileEtag\n";
echo " 💡 Это точно нужный файл!\n";
$foundByEtag = true;
echo "\n";
}
} catch (\Aws\Exception\AwsException $e) {
// Пропускаем
}
}
}
} catch (\Aws\Exception\AwsException $e) {
// Пропускаем ошибки
}
}
if (!$foundByEtag) {
echo " ❌ Файл с таким ETag не найден в папках проекта\n";
}
echo "\n";
}
} catch (Exception $e) {
echo "ОШИБКА: " . $e->getMessage() . "\n";
echo "Trace: " . $e->getTraceAsString() . "\n";
}