✨ 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!
265 lines
7.9 KiB
PHP
265 lines
7.9 KiB
PHP
<?php
|
|
/**
|
|
* Webhook endpoint для получения событий от Nextcloud
|
|
*
|
|
* Настройка в Nextcloud:
|
|
* - Webhook URL: https://crm.clientright.ru/crm_extensions/file_storage/api/nextcloud_webhook.php
|
|
* - События: file_created, file_updated, file_deleted, folder_renamed, folder_deleted
|
|
*/
|
|
|
|
// Подключаем CRM
|
|
require_once('../../../../config.inc.php');
|
|
require_once('../../../../include/utils/utils.php');
|
|
require_once('../../../../include/utils/CommonUtils.php');
|
|
require_once('../FilePathManager.php');
|
|
|
|
// Логирование
|
|
$logFile = '/var/log/crm_nextcloud_webhook.log';
|
|
|
|
function logWebhook($message) {
|
|
global $logFile;
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
file_put_contents($logFile, "[$timestamp] $message\n", FILE_APPEND | LOCK_EX);
|
|
}
|
|
|
|
// Проверяем метод запроса
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['error' => 'Method not allowed']);
|
|
exit;
|
|
}
|
|
|
|
// Получаем данные webhook
|
|
$input = file_get_contents('php://input');
|
|
$data = json_decode($input, true);
|
|
|
|
logWebhook("Webhook received: " . $input);
|
|
|
|
if (!$data) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Invalid JSON']);
|
|
exit;
|
|
}
|
|
|
|
// Проверяем обязательные поля
|
|
if (!isset($data['action']) || !isset($data['file_path'])) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Missing required fields']);
|
|
exit;
|
|
}
|
|
|
|
$action = $data['action'];
|
|
$filePath = $data['file_path'];
|
|
$projectId = isset($data['project_id']) ? $data['project_id'] : null;
|
|
|
|
logWebhook("Processing action: $action, path: $filePath, project: $projectId");
|
|
|
|
// Парсим путь файла
|
|
$pathManager = new FilePathManager();
|
|
$parsedPath = $pathManager->parseFilePath($filePath);
|
|
|
|
if (!$parsedPath) {
|
|
logWebhook("Failed to parse file path: $filePath");
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Invalid file path']);
|
|
exit;
|
|
}
|
|
|
|
$module = $parsedPath['module'];
|
|
$recordId = $parsedPath['recordId'];
|
|
$documentId = $parsedPath['documentId'];
|
|
$fileName = $parsedPath['fileName'];
|
|
|
|
logWebhook("Parsed: module=$module, recordId=$recordId, documentId=$documentId, fileName=$fileName");
|
|
|
|
// Обрабатываем разные типы событий
|
|
switch ($action) {
|
|
case 'file_created':
|
|
handleFileCreated($module, $recordId, $documentId, $fileName, $data);
|
|
break;
|
|
|
|
case 'file_updated':
|
|
handleFileUpdated($module, $recordId, $documentId, $fileName, $data);
|
|
break;
|
|
|
|
case 'file_deleted':
|
|
handleFileDeleted($module, $recordId, $documentId, $fileName, $data);
|
|
break;
|
|
|
|
case 'folder_renamed':
|
|
handleFolderRenamed($module, $recordId, $data);
|
|
break;
|
|
|
|
case 'folder_deleted':
|
|
handleFolderDeleted($module, $recordId, $data);
|
|
break;
|
|
|
|
default:
|
|
logWebhook("Unknown action: $action");
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Unknown action']);
|
|
exit;
|
|
}
|
|
|
|
// Функция обработки создания файла
|
|
function handleFileCreated($module, $recordId, $documentId, $fileName, $data) {
|
|
global $adb;
|
|
|
|
// Проверяем, есть ли уже запись в БД
|
|
$query = "SELECT notesid FROM vtiger_notes WHERE notesid = ?";
|
|
$result = $adb->pquery($query, [$documentId]);
|
|
|
|
if ($adb->num_rows($result) > 0) {
|
|
logWebhook("File already exists in DB: $documentId");
|
|
return;
|
|
}
|
|
|
|
// Создаем новую запись в БД
|
|
$query = "INSERT INTO vtiger_notes (notesid, title, filename, filetype, filesize, filelocationtype, fileversion, createdtime, modifiedtime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
|
|
$title = pathinfo($fileName, PATHINFO_FILENAME);
|
|
$fileType = pathinfo($fileName, PATHINFO_EXTENSION);
|
|
$fileSize = isset($data['file_size']) ? $data['file_size'] : 0;
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
$adb->pquery($query, [
|
|
$documentId,
|
|
$title,
|
|
$fileName,
|
|
$fileType,
|
|
$fileSize,
|
|
'I', // Internal
|
|
'1',
|
|
$now,
|
|
$now
|
|
]);
|
|
|
|
// Отправляем SSE событие
|
|
sendSSEEvent('file_created', [
|
|
'module' => $module,
|
|
'recordId' => $recordId,
|
|
'documentId' => $documentId,
|
|
'fileName' => $fileName
|
|
]);
|
|
|
|
logWebhook("File created in DB: $documentId");
|
|
}
|
|
|
|
// Функция обработки обновления файла
|
|
function handleFileUpdated($module, $recordId, $documentId, $fileName, $data) {
|
|
global $adb;
|
|
|
|
// Обновляем запись в БД
|
|
$query = "UPDATE vtiger_notes SET filename = ?, filesize = ?, modifiedtime = ? WHERE notesid = ?";
|
|
|
|
$fileSize = isset($data['file_size']) ? $data['file_size'] : 0;
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
$adb->pquery($query, [
|
|
$fileName,
|
|
$fileSize,
|
|
$now,
|
|
$documentId
|
|
]);
|
|
|
|
// Отправляем SSE событие
|
|
sendSSEEvent('file_updated', [
|
|
'module' => $module,
|
|
'recordId' => $recordId,
|
|
'documentId' => $documentId,
|
|
'fileName' => $fileName
|
|
]);
|
|
|
|
logWebhook("File updated in DB: $documentId");
|
|
}
|
|
|
|
// Функция обработки удаления файла
|
|
function handleFileDeleted($module, $recordId, $documentId, $fileName, $data) {
|
|
global $adb;
|
|
|
|
// Помечаем файл как удаленный
|
|
$query = "UPDATE vtiger_notes SET deleted = 1 WHERE notesid = ?";
|
|
$adb->pquery($query, [$documentId]);
|
|
|
|
// Отправляем SSE событие
|
|
sendSSEEvent('file_deleted', [
|
|
'module' => $module,
|
|
'recordId' => $recordId,
|
|
'documentId' => $documentId,
|
|
'fileName' => $fileName
|
|
]);
|
|
|
|
logWebhook("File deleted in DB: $documentId");
|
|
}
|
|
|
|
// Функция обработки переименования папки
|
|
function handleFolderRenamed($module, $recordId, $data) {
|
|
global $adb;
|
|
|
|
$oldPath = $data['old_path'];
|
|
$newPath = $data['new_path'];
|
|
|
|
// Обновляем пути файлов в БД
|
|
$query = "UPDATE vtiger_notes SET filename = REPLACE(filename, ?, ?) WHERE filename LIKE ?";
|
|
$adb->pquery($query, [$oldPath, $newPath, "%$oldPath%"]);
|
|
|
|
// Отправляем SSE событие
|
|
sendSSEEvent('folder_renamed', [
|
|
'module' => $module,
|
|
'recordId' => $recordId,
|
|
'oldPath' => $oldPath,
|
|
'newPath' => $newPath
|
|
]);
|
|
|
|
logWebhook("Folder renamed: $oldPath -> $newPath");
|
|
}
|
|
|
|
// Функция обработки удаления папки
|
|
function handleFolderDeleted($module, $recordId, $data) {
|
|
global $adb;
|
|
|
|
$folderPath = $data['folder_path'];
|
|
|
|
// Помечаем все файлы папки как удаленные
|
|
$query = "UPDATE vtiger_notes SET deleted = 1 WHERE filename LIKE ?";
|
|
$adb->pquery($query, ["%$folderPath%"]);
|
|
|
|
// Отправляем SSE событие
|
|
sendSSEEvent('folder_deleted', [
|
|
'module' => $module,
|
|
'recordId' => $recordId,
|
|
'folderPath' => $folderPath
|
|
]);
|
|
|
|
logWebhook("Folder deleted: $folderPath");
|
|
}
|
|
|
|
// Функция для отправки SSE события
|
|
function sendSSEEvent($type, $data) {
|
|
$event = [
|
|
'type' => $type,
|
|
'data' => $data,
|
|
'timestamp' => time()
|
|
];
|
|
|
|
// Сохраняем событие в файл для SSE endpoint
|
|
$eventsFile = '/tmp/crm_sse_events.json';
|
|
$events = [];
|
|
|
|
if (file_exists($eventsFile)) {
|
|
$events = json_decode(file_get_contents($eventsFile), true) ?: [];
|
|
}
|
|
|
|
$events[] = $event;
|
|
file_put_contents($eventsFile, json_encode($events));
|
|
}
|
|
|
|
// Отправляем успешный ответ
|
|
http_response_code(200);
|
|
echo json_encode(['status' => 'success', 'message' => 'Event processed']);
|
|
?>
|
|
|
|
|
|
|
|
|