feat: OnlyOffice Standalone integration with S3 direct URLs
✅ ЧТО СДЕЛАНО: - Поднят новый standalone OnlyOffice Document Server (порт 8083) - Настроен Nginx для доступа через office.clientright.ru:9443 - Создан open_file_v3_standalone.php для работы с новым OnlyOffice - Реализована поддержка прямых S3 URL (bucket публичный) - Добавлен s3_proxy.php с поддержкой Range requests - Создан onlyoffice_callback.php для сохранения (базовая версия) - Файлы успешно открываются и загружаются! ⚠️ TODO (на завтра): - Доработать onlyoffice_callback.php для сохранения обратно в ОРИГИНАЛЬНЫЙ путь в S3 - Добавить Redis маппинг documentKey → S3 path - Обновить CRM JS для использования open_file_v3_standalone.php - Протестировать сохранение файлов - Удалить тестовые файлы 📊 РЕЗУЛЬТАТ: - OnlyOffice Standalone РАБОТАЕТ! ✅ - Файлы открываются напрямую из S3 ✅ - Редактор загружается БЫСТРО ✅ - Автосохранение настроено ✅ (но нужна доработка callback)
This commit is contained in:
@@ -1,110 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* Простой редирект на файл в Nextcloud БЕЗ CSRF проверок
|
||||
* Использует FilePathManager для новой структуры файлов
|
||||
* ФИНАЛ: OnlyOffice + Pre-signed S3 URL
|
||||
* Теперь с CORS и правильными настройками!
|
||||
*/
|
||||
|
||||
// Включаем отображение ошибок
|
||||
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');
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Подключаем конфигурацию и FilePathManager
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/config.inc.php';
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/file_storage/FilePathManager.php';
|
||||
|
||||
// Получаем параметры
|
||||
$fileName = isset($_GET['fileName']) ? $_GET['fileName'] : '';
|
||||
$recordId = isset($_GET['recordId']) ? $_GET['recordId'] : '';
|
||||
|
||||
// Если fileName содержит полный URL S3, извлекаем путь к файлу
|
||||
$ncPath = '';
|
||||
if (empty($fileName)) {
|
||||
die("❌ fileName не указан");
|
||||
}
|
||||
|
||||
// Извлекаем S3 путь
|
||||
$s3Path = '';
|
||||
if (strpos($fileName, 'http') === 0) {
|
||||
// Декодируем URL
|
||||
$fileName = urldecode($fileName);
|
||||
|
||||
// Извлекаем путь после bucket ID
|
||||
// Формат: https://s3.twcstorage.ru/BUCKET_ID/crm2/CRM_Active_Files/...
|
||||
$bucketId = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
||||
$pos = strpos($fileName, $bucketId . '/');
|
||||
if ($pos !== false) {
|
||||
$s3Path = substr($fileName, $pos + strlen($bucketId) + 1);
|
||||
|
||||
// Nextcloud путь = /crm/ + s3_path
|
||||
$ncPath = '/crm/' . $s3Path;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ncPath)) {
|
||||
die("❌ Ошибка: Не удалось извлечь путь из URL: $fileName");
|
||||
if (empty($s3Path)) {
|
||||
die("❌ Не удалось извлечь путь из URL");
|
||||
}
|
||||
|
||||
// Настройки Nextcloud
|
||||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||||
$username = 'admin';
|
||||
$password = 'office';
|
||||
// Извлекаем расширение файла
|
||||
$ext = strtolower(pathinfo($s3Path, PATHINFO_EXTENSION));
|
||||
|
||||
// Вспомогательная функция: кодирование пути по сегментам (WebDAV)
|
||||
$encodePath = function(array $segments) {
|
||||
return implode('/', array_map('rawurlencode', $segments));
|
||||
};
|
||||
// ПРЯМОЙ S3 URL (bucket публичный, CORS настроен!)
|
||||
$bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
||||
$s3Url = 'https://s3.twcstorage.ru/' . $bucket . '/' . $s3Path;
|
||||
|
||||
// Получаем fileId через WebDAV PROPFIND
|
||||
$fileId = null;
|
||||
$propfindUrl = $nextcloudUrl . '/remote.php/dav/files/' . $username . $ncPath;
|
||||
// Генерируем версию и ключ документа
|
||||
$version = time();
|
||||
// СЛУЧАЙНЫЙ ключ при каждом запросе, чтобы OnlyOffice не использовал кеш!
|
||||
$documentKey = md5($s3Path . '_' . $version);
|
||||
|
||||
error_log("Nextcloud Editor: PROPFIND -> {$propfindUrl}");
|
||||
// ПРЯМОЙ S3 URL (bucket публичный, поэтому pre-signed URL не нужен!)
|
||||
// Bucket поддерживает Range requests и CORS из коробки
|
||||
$fileUrl = $s3Url;
|
||||
|
||||
// XML запрос для получения fileid
|
||||
$xmlRequest = '<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:fileid/>
|
||||
</d:prop>
|
||||
</d:propfind>';
|
||||
// ОТЛАДКА: Логируем все параметры
|
||||
error_log("=== OPEN FILE DEBUG ===");
|
||||
error_log("S3 Path: " . $s3Path);
|
||||
error_log("File URL: " . $fileUrl);
|
||||
error_log("File extension: " . $ext);
|
||||
error_log("Document Key (unique): " . $documentKey);
|
||||
error_log("Version: " . $version);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $propfindUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . $password);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PROPFIND');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Depth: 0',
|
||||
'Content-Type: application/xml'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
$fileBasename = basename($s3Path);
|
||||
$fileType = getFileType($ext);
|
||||
$officeFormats = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
||||
|
||||
if ($response === false) {
|
||||
error_log("Nextcloud Editor: Ошибка cURL: " . $curlError);
|
||||
} else {
|
||||
error_log("Nextcloud Editor: HTTP код: {$httpCode}");
|
||||
|
||||
if ($httpCode === 207 && preg_match('/<oc:fileid>(\d+)<\/oc:fileid>/', $response, $matches)) {
|
||||
$fileId = (int)$matches[1];
|
||||
error_log("Nextcloud Editor: Получен fileId: {$fileId}");
|
||||
} else {
|
||||
error_log("Nextcloud Editor: Файл не найден по пути: {$ncPath} (HTTP {$httpCode})");
|
||||
}
|
||||
if (!in_array($ext, $officeFormats)) {
|
||||
header('Location: ' . $s3Url);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$fileId) {
|
||||
$errorMsg = "❌ Ошибка: Не удалось получить fileId для файла {$fileName}";
|
||||
error_log("Nextcloud Editor ERROR: " . $errorMsg);
|
||||
die($errorMsg);
|
||||
}
|
||||
|
||||
// Формируем URL для Nextcloud
|
||||
// РАБОЧИЙ ФОРМАТ - редирект на файл с автооткрытием редактора!
|
||||
$redirectUrl = $nextcloudUrl . '/apps/files/files/' . $fileId . '?dir=/&editing=true&openfile=true';
|
||||
|
||||
// Логирование
|
||||
error_log("Nextcloud Editor: Redirect to $redirectUrl for file (ID: $fileId)");
|
||||
|
||||
// Делаем редирект
|
||||
header('Location: ' . $redirectUrl);
|
||||
exit;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title><?php echo htmlspecialchars($fileBasename); ?></title>
|
||||
<script src="https://office.clientright.ru:9443/web-apps/apps/api/documents/api.js?v=<?php echo time(); ?>"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#editor {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor"></div>
|
||||
|
||||
<script>
|
||||
// Отладка в консоль
|
||||
console.log('📁 Файл:', <?php echo json_encode($fileBasename); ?>);
|
||||
console.log('🔗 S3 URL:', <?php echo json_encode($fileUrl); ?>);
|
||||
console.log('🔑 Document Key (unique):', <?php echo json_encode($documentKey); ?>);
|
||||
console.log('✅ Standalone OnlyOffice (9443) + Direct S3 URL!');
|
||||
|
||||
new DocsAPI.DocEditor("editor", {
|
||||
"documentType": "<?php echo $fileType; ?>",
|
||||
"document": {
|
||||
"fileType": "<?php echo $ext; ?>",
|
||||
"key": "<?php echo $documentKey; ?>",
|
||||
"title": <?php echo json_encode($fileBasename); ?>,
|
||||
"url": <?php echo json_encode($fileUrl); ?>,
|
||||
"permissions": {
|
||||
"comment": true,
|
||||
"download": true,
|
||||
"edit": true,
|
||||
"print": true,
|
||||
"review": true
|
||||
}
|
||||
},
|
||||
"editorConfig": {
|
||||
"mode": "edit",
|
||||
"lang": "ru",
|
||||
"callbackUrl": "https://crm.clientright.ru/crm_extensions/file_storage/api/onlyoffice_callback.php",
|
||||
"user": {
|
||||
"id": "user_<?php echo $recordId ?? 'guest'; ?>",
|
||||
"name": "CRM User"
|
||||
},
|
||||
"customization": {
|
||||
"autosave": true,
|
||||
"chat": false,
|
||||
"comments": true,
|
||||
"compactHeader": false,
|
||||
"compactToolbar": false,
|
||||
"help": true,
|
||||
"hideRightMenu": false,
|
||||
"logo": {
|
||||
"image": "https://crm.clientright.ru/layouts/v7/skins/images/logo.png",
|
||||
"imageEmbedded": "https://crm.clientright.ru/layouts/v7/skins/images/logo.png"
|
||||
},
|
||||
"zoom": 100
|
||||
}
|
||||
},
|
||||
"height": "100%",
|
||||
"width": "100%",
|
||||
"type": "desktop",
|
||||
"events": {
|
||||
"onReady": function() {
|
||||
console.log('✅ Editor ready!');
|
||||
},
|
||||
"onDocumentReady": function() {
|
||||
console.log('✅ Document loaded!');
|
||||
},
|
||||
"onError": function(event) {
|
||||
console.error('❌ OnlyOffice Error FULL:', JSON.stringify(event, null, 2));
|
||||
console.error('Event data:', event.data);
|
||||
console.error('Error code:', event.data.errorCode);
|
||||
console.error('Error description:', event.data.errorDescription);
|
||||
|
||||
// Тестируем доступность URL из браузера
|
||||
console.log('🧪 Testing S3 URL from browser...');
|
||||
fetch(<?php echo json_encode($fileUrl); ?>, { method: 'HEAD' })
|
||||
.then(response => {
|
||||
console.log('✅ Browser can access S3:', response.status);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('❌ Browser CANNOT access S3:', error);
|
||||
});
|
||||
|
||||
alert('Ошибка загрузки документа:\n\n' +
|
||||
'Code: ' + event.data.errorCode + '\n' +
|
||||
'Description: ' + event.data.errorDescription + '\n\n' +
|
||||
'Используется Pre-signed URL из S3\n\n' +
|
||||
'Смотри консоль браузера (F12) для деталей!');
|
||||
},
|
||||
"onWarning": function(event) {
|
||||
console.warn('⚠️ OnlyOffice Warning:', event);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
|
||||
function getFileType($ext) {
|
||||
if (in_array($ext, ['doc', 'docx'])) return 'word';
|
||||
if (in_array($ext, ['xls', 'xlsx'])) return 'cell';
|
||||
if (in_array($ext, ['ppt', 'pptx'])) return 'slide';
|
||||
return 'word';
|
||||
}
|
||||
|
||||
function generatePresignedUrl($s3Key, $expirationSeconds) {
|
||||
try {
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
|
||||
|
||||
$s3Client = new Aws\S3\S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => 'ru-1',
|
||||
'endpoint' => 'https://s3.twcstorage.ru',
|
||||
'use_path_style_endpoint' => true,
|
||||
'credentials' => [
|
||||
'key' => EnvLoader::getRequired('S3_ACCESS_KEY'),
|
||||
'secret' => EnvLoader::getRequired('S3_SECRET_KEY')
|
||||
],
|
||||
'suppress_php_deprecation_warning' => true
|
||||
]);
|
||||
|
||||
$bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
||||
|
||||
// КЛЮЧ: Минимальные параметры = правильная подпись!
|
||||
$cmd = $s3Client->getCommand('GetObject', [
|
||||
'Bucket' => $bucket,
|
||||
'Key' => $s3Key
|
||||
]);
|
||||
|
||||
$request = $s3Client->createPresignedRequest($cmd, "+{$expirationSeconds} seconds");
|
||||
return (string)$request->getUri();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Pre-signed URL error: " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getContentType($filename) {
|
||||
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
$types = [
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
||||
];
|
||||
return $types[$ext] ?? 'application/octet-stream';
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user