✨ Features: - Migrated ALL files to new S3 structure (Projects, Contacts, Accounts, HelpDesk, Invoice, etc.) - Added Nextcloud folder buttons to ALL modules - Fixed Nextcloud editor integration - WebSocket server for real-time updates - Redis Pub/Sub integration - File path manager for organized storage - Redis caching for performance (Functions.php) 📁 New Structure: Documents/Project/ProjectName_ID/file_docID.ext Documents/Contacts/FirstName_LastName_ID/file_docID.ext Documents/Accounts/AccountName_ID/file_docID.ext 🔧 Technical: - FilePathManager for standardized paths - S3StorageService integration - WebSocket server (Node.js + Docker) - Redis cache for getBasicModuleInfo() - Predis library for Redis connectivity 📝 Scripts: - Migration scripts for all modules - Test pages for WebSocket/SSE/Polling - Documentation (MIGRATION_*.md, REDIS_*.md) 🎯 Result: 15,000+ files migrated successfully!
272 lines
10 KiB
PHP
272 lines
10 KiB
PHP
<?php
|
||
/**
|
||
* Миграция файлов КОНТАКТОВ
|
||
* Переносит файлы из старой структуры в новую: Contacts/имя_ID/файл_docID.pdf
|
||
*/
|
||
|
||
// Включаем отображение ошибок
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', 1);
|
||
|
||
echo "🚀 МИГРАЦИЯ ФАЙЛОВ КОНТАКТОВ\n";
|
||
echo "============================\n\n";
|
||
|
||
// Подключаем конфигурацию
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/FilePathManager.php';
|
||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/shared/EnvLoader.php';
|
||
|
||
// Загружаем переменные окружения
|
||
EnvLoader::load('/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/.env');
|
||
|
||
// Создаем PDO подключение
|
||
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]
|
||
);
|
||
echo "✅ PDO подключен\n";
|
||
} catch (Exception $e) {
|
||
die("❌ Ошибка PDO: " . $e->getMessage() . "\n");
|
||
}
|
||
|
||
// 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' => EnvLoader::getRequired('S3_ACCESS_KEY'),
|
||
'secret' => EnvLoader::getRequired('S3_SECRET_KEY')
|
||
];
|
||
|
||
try {
|
||
echo "🔧 Создаем S3 клиент...\n";
|
||
$s3 = new Aws\S3\S3Client($s3Config);
|
||
echo "✅ S3 подключен\n";
|
||
} catch (Exception $e) {
|
||
die("❌ Ошибка S3: " . $e->getMessage() . "\n");
|
||
}
|
||
|
||
echo "🔧 Создаем FilePathManager...\n";
|
||
$pathMgr = new FilePathManager();
|
||
echo "✅ FilePathManager создан\n";
|
||
|
||
// Получаем контакты с файлами в старой структуре
|
||
echo "\n📁 ПОИСК КОНТАКТОВ С ФАЙЛАМИ:\n";
|
||
echo "=============================\n";
|
||
|
||
$sql = "SELECT DISTINCT sr.crmid as contactid,
|
||
CONCAT(c.firstname, ' ', c.lastname) as contact_name,
|
||
COUNT(n.notesid) as file_count
|
||
FROM vtiger_senotesrel sr
|
||
INNER JOIN vtiger_notes n ON sr.notesid = n.notesid
|
||
INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid
|
||
INNER JOIN vtiger_contactdetails c ON sr.crmid = c.contactid
|
||
WHERE ce.setype = 'Contacts'
|
||
AND n.filelocationtype = 'E'
|
||
AND n.s3_key IS NOT NULL
|
||
AND n.s3_key NOT LIKE '%/Contacts/%'
|
||
GROUP BY sr.crmid, c.firstname, c.lastname
|
||
ORDER BY file_count DESC, contact_name
|
||
LIMIT 50";
|
||
|
||
$result = $pdo->query($sql);
|
||
$contacts = [];
|
||
|
||
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
|
||
$contacts[] = $row;
|
||
echo "• {$row['contact_name']} (ID: {$row['contactid']}): {$row['file_count']} файлов\n";
|
||
}
|
||
|
||
echo "\n📈 ПОКАЗАНО: " . count($contacts) . " контактов (топ 50 по количеству файлов)\n";
|
||
|
||
// Подсчитываем общее количество файлов для миграции
|
||
$sql = "SELECT COUNT(*) as total_files,
|
||
COUNT(DISTINCT sr.crmid) as total_contacts
|
||
FROM vtiger_senotesrel sr
|
||
INNER JOIN vtiger_notes n ON sr.notesid = n.notesid
|
||
INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid
|
||
WHERE ce.setype = 'Contacts'
|
||
AND n.filelocationtype = 'E'
|
||
AND n.s3_key IS NOT NULL
|
||
AND n.s3_key NOT LIKE '%/Contacts/%'";
|
||
|
||
$stmt = $pdo->prepare($sql);
|
||
$stmt->execute();
|
||
$stats = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
echo "📁 ВСЕГО КОНТАКТОВ: {$stats['total_contacts']}\n";
|
||
echo "📄 ВСЕГО ФАЙЛОВ: {$stats['total_files']}\n";
|
||
|
||
// Спрашиваем пользователя
|
||
echo "\n❓ ВОПРОС:\n";
|
||
echo "===========\n";
|
||
echo "Мигрировать файлы контактов ({$stats['total_files']} файлов от {$stats['total_contacts']} контактов)? (y/n): ";
|
||
$handle = fopen("php://stdin", "r");
|
||
$line = fgets($handle);
|
||
fclose($handle);
|
||
|
||
if (trim(strtolower($line)) !== 'y') {
|
||
echo "❌ Миграция отменена\n";
|
||
exit;
|
||
}
|
||
|
||
// Получаем ВСЕ контакты с файлами
|
||
echo "\n🔄 Загружаем полный список контактов...\n";
|
||
|
||
$sql = "SELECT DISTINCT sr.crmid as contactid,
|
||
CONCAT(c.firstname, ' ', c.lastname) as contact_name
|
||
FROM vtiger_senotesrel sr
|
||
INNER JOIN vtiger_notes n ON sr.notesid = n.notesid
|
||
INNER JOIN vtiger_crmentity ce ON sr.crmid = ce.crmid
|
||
INNER JOIN vtiger_contactdetails c ON sr.crmid = c.contactid
|
||
WHERE ce.setype = 'Contacts'
|
||
AND n.filelocationtype = 'E'
|
||
AND n.s3_key IS NOT NULL
|
||
AND n.s3_key NOT LIKE '%/Contacts/%'
|
||
ORDER BY contact_name";
|
||
|
||
$result = $pdo->query($sql);
|
||
$allContacts = [];
|
||
|
||
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
|
||
$allContacts[] = $row;
|
||
}
|
||
|
||
echo "✅ Загружено: " . count($allContacts) . " контактов\n";
|
||
|
||
// Начинаем миграцию
|
||
echo "\n🚀 НАЧИНАЕМ МИГРАЦИЮ КОНТАКТОВ:\n";
|
||
echo "===============================\n";
|
||
|
||
$migratedContacts = 0;
|
||
$migratedFiles = 0;
|
||
$errors = 0;
|
||
|
||
foreach ($allContacts as $contact) {
|
||
$contactId = $contact['contactid'];
|
||
$contactName = $contact['contact_name'];
|
||
|
||
echo "\n👤 Контакт: $contactName (ID: $contactId)\n";
|
||
|
||
// Получаем все файлы контакта которые еще не мигрированы
|
||
$sql = "SELECT n.notesid, n.title, n.filename, n.s3_key, n.s3_bucket
|
||
FROM vtiger_notes n
|
||
INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid
|
||
WHERE sr.crmid = ?
|
||
AND n.filelocationtype = 'E'
|
||
AND n.s3_key IS NOT NULL
|
||
AND n.s3_key NOT LIKE '%/Contacts/%'";
|
||
|
||
$stmt = $pdo->prepare($sql);
|
||
$stmt->execute([$contactId]);
|
||
$files = [];
|
||
|
||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||
$files[] = $row;
|
||
}
|
||
|
||
echo " 📄 Файлов для миграции: " . count($files) . "\n";
|
||
|
||
$contactMigratedFiles = 0;
|
||
$contactErrors = 0;
|
||
|
||
foreach ($files as $file) {
|
||
$documentId = $file['notesid'];
|
||
$fileName = $file['filename'];
|
||
$oldS3Key = $file['s3_key'];
|
||
$title = $file['title'];
|
||
|
||
// Генерируем новый путь для Contacts
|
||
$newFilePath = $pathMgr->getFilePath('Contacts', $contactId, $documentId, $fileName, $title, $contactName);
|
||
$newS3Key = $newFilePath;
|
||
|
||
// Проверяем, нужно ли мигрировать
|
||
if ($oldS3Key === $newS3Key) {
|
||
$contactMigratedFiles++;
|
||
continue;
|
||
}
|
||
|
||
echo " 🔄 Мигрируем: $title\n";
|
||
|
||
try {
|
||
// Проверяем существование старого файла
|
||
$oldUrl = "https://s3.twcstorage.ru/{$s3Config['bucket']}/{$oldS3Key}";
|
||
$headers = @get_headers($oldUrl);
|
||
|
||
if (!$headers || strpos($headers[0], '200') === false) {
|
||
echo " ⚠️ Файл не найден в S3\n";
|
||
$contactErrors++;
|
||
continue;
|
||
}
|
||
|
||
// Скачиваем файл
|
||
$fileContent = file_get_contents($oldUrl);
|
||
if ($fileContent === false) {
|
||
echo " ❌ Не удалось скачать файл\n";
|
||
$contactErrors++;
|
||
continue;
|
||
}
|
||
|
||
// Загружаем в новое место
|
||
$uploadResult = $s3->putObject([
|
||
'Bucket' => $s3Config['bucket'],
|
||
'Key' => $newS3Key,
|
||
'Body' => $fileContent,
|
||
'ContentType' => mime_content_type('data://text/plain;base64,' . base64_encode($fileContent))
|
||
]);
|
||
|
||
// Обновляем БД (и s3_key и filename с полным URL)
|
||
$newFileUrl = "https://s3.twcstorage.ru/{$s3Config['bucket']}/{$newS3Key}";
|
||
$updateSql = "UPDATE vtiger_notes SET s3_key = ?, filename = ? WHERE notesid = ?";
|
||
$updateStmt = $pdo->prepare($updateSql);
|
||
$updateStmt->execute([$newS3Key, $newFileUrl, $documentId]);
|
||
|
||
// Удаляем старый файл
|
||
try {
|
||
$s3->deleteObject([
|
||
'Bucket' => $s3Config['bucket'],
|
||
'Key' => $oldS3Key
|
||
]);
|
||
} catch (Exception $e) {
|
||
// Не критичная ошибка
|
||
}
|
||
|
||
echo " ✅ Файл мигрирован успешно\n";
|
||
$contactMigratedFiles++;
|
||
|
||
} catch (Exception $e) {
|
||
echo " ❌ Ошибка миграции: " . $e->getMessage() . "\n";
|
||
$contactErrors++;
|
||
}
|
||
}
|
||
|
||
echo " 📊 Результат контакта: $contactMigratedFiles файлов мигрировано, $contactErrors ошибок\n";
|
||
|
||
$migratedContacts++;
|
||
$migratedFiles += $contactMigratedFiles;
|
||
$errors += $contactErrors;
|
||
}
|
||
|
||
// Итоговая статистика
|
||
echo "\n🎉 МИГРАЦИЯ КОНТАКТОВ ЗАВЕРШЕНА!\n";
|
||
echo "================================\n";
|
||
echo "👤 Контактов обработано: $migratedContacts\n";
|
||
echo "📄 Файлов мигрировано: $migratedFiles\n";
|
||
echo "❌ Ошибок: $errors\n";
|
||
|
||
if ($migratedFiles + $errors > 0) {
|
||
echo "✅ Успешность: " . round(($migratedFiles / ($migratedFiles + $errors)) * 100, 2) . "%\n";
|
||
}
|
||
|
||
echo "\n🚀 Все файлы контактов мигрированы в новую структуру Contacts/имя_ID/файл_docID!\n";
|
||
?>
|
||
|
||
|
||
|