122 lines
4.0 KiB
PHP
122 lines
4.0 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* S3 Proxy для OnlyOffice
|
|||
|
|
* Проксирует запросы к S3, чтобы OnlyOffice мог загружать файлы
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
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', 0);
|
|||
|
|
|
|||
|
|
$path = isset($_GET['path']) ? $_GET['path'] : '';
|
|||
|
|
|
|||
|
|
if (empty($path)) {
|
|||
|
|
http_response_code(400);
|
|||
|
|
die('Path parameter is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CORS preflight
|
|||
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|||
|
|
header('Access-Control-Allow-Origin: *');
|
|||
|
|
header('Access-Control-Allow-Methods: GET, HEAD, OPTIONS');
|
|||
|
|
header('Access-Control-Allow-Headers: *');
|
|||
|
|
header('Access-Control-Max-Age: 3600');
|
|||
|
|
http_response_code(200);
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Для HEAD запросов - только headers, без body
|
|||
|
|
$isHeadRequest = ($_SERVER['REQUEST_METHOD'] === 'HEAD');
|
|||
|
|
|
|||
|
|
// Формируем URL к S3
|
|||
|
|
$bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
|||
|
|
$s3Url = 'https://s3.twcstorage.ru/' . $bucket . '/' . $path;
|
|||
|
|
|
|||
|
|
// Проверяем Range header (для OnlyOffice partial requests)
|
|||
|
|
$rangeHeader = isset($_SERVER['HTTP_RANGE']) ? $_SERVER['HTTP_RANGE'] : '';
|
|||
|
|
|
|||
|
|
error_log("S3 Proxy: Request from: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
|
|||
|
|
error_log("S3 Proxy: Downloading: " . $s3Url);
|
|||
|
|
if ($rangeHeader) {
|
|||
|
|
error_log("S3 Proxy: Range request: " . $rangeHeader);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// СНАЧАЛА скачиваем в буфер
|
|||
|
|
$ch = curl_init($s3Url);
|
|||
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|||
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // ← ВАЖНО: В БУФЕР!
|
|||
|
|
curl_setopt($ch, CURLOPT_HEADER, true); // ← Получаем headers
|
|||
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
|||
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
|
|||
|
|
|
|||
|
|
// Для HEAD запросов - только headers
|
|||
|
|
if ($isHeadRequest) {
|
|||
|
|
curl_setopt($ch, CURLOPT_NOBODY, true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если есть Range header - передаём его в S3!
|
|||
|
|
if ($rangeHeader) {
|
|||
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Range: ' . $rangeHeader]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$response = curl_exec($ch);
|
|||
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|||
|
|
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|||
|
|
$error = curl_error($ch);
|
|||
|
|
curl_close($ch);
|
|||
|
|
|
|||
|
|
// Проверяем ПРЕЖДЕ чем отправлять что-либо
|
|||
|
|
// 200 = полный файл, 206 = частичный (Range request)
|
|||
|
|
if ($response === false || ($httpCode !== 200 && $httpCode !== 206)) {
|
|||
|
|
error_log("S3 Proxy ERROR: HTTP $httpCode, cURL error: $error");
|
|||
|
|
header('Access-Control-Allow-Origin: *');
|
|||
|
|
http_response_code($httpCode ?: 500);
|
|||
|
|
die('Failed to fetch file from S3');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Разделяем headers и body
|
|||
|
|
$headersText = substr($response, 0, $headerSize);
|
|||
|
|
$body = $isHeadRequest ? '' : substr($response, $headerSize); // Для HEAD body пустой
|
|||
|
|
|
|||
|
|
// Парсим headers
|
|||
|
|
$headers = explode("\r\n", $headersText);
|
|||
|
|
foreach ($headers as $header) {
|
|||
|
|
if (strpos($header, ':') !== false) {
|
|||
|
|
list($name, $value) = explode(':', $header, 2);
|
|||
|
|
$name = strtolower(trim($name));
|
|||
|
|
$value = trim($value);
|
|||
|
|
|
|||
|
|
// Пробрасываем нужные headers
|
|||
|
|
if (in_array($name, ['content-type', 'content-length', 'content-range', 'accept-ranges', 'etag', 'last-modified'])) {
|
|||
|
|
header($name . ': ' . $value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CORS headers
|
|||
|
|
header('Access-Control-Allow-Origin: *');
|
|||
|
|
header('Access-Control-Allow-Methods: GET, HEAD, OPTIONS');
|
|||
|
|
header('Access-Control-Allow-Headers: *');
|
|||
|
|
header('Access-Control-Expose-Headers: Content-Range, Accept-Ranges');
|
|||
|
|
|
|||
|
|
// Устанавливаем правильный HTTP код (206 для partial content)
|
|||
|
|
if ($httpCode === 206) {
|
|||
|
|
http_response_code(206);
|
|||
|
|
} else {
|
|||
|
|
http_response_code(200);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отправляем body только для GET запросов (не для HEAD)
|
|||
|
|
if (!$isHeadRequest) {
|
|||
|
|
echo $body;
|
|||
|
|
error_log("S3 Proxy: Success! Sent " . strlen($body) . " bytes");
|
|||
|
|
} else {
|
|||
|
|
error_log("S3 Proxy: HEAD request completed");
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
|
|||
|
|
|