From 0215463099b29796741c9478a8fee2eab93ee5cf Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Sat, 1 Nov 2025 10:46:43 +0300 Subject: [PATCH] =?utf8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D1=80?= =?utf8?q?=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/helpers/ImageHelper.php | 19 +++++- erp24/services/FileService.php | 121 ++++++++++++++++++++++++--------- 2 files changed, 108 insertions(+), 32 deletions(-) diff --git a/erp24/helpers/ImageHelper.php b/erp24/helpers/ImageHelper.php index 9700196b..58392aa5 100644 --- a/erp24/helpers/ImageHelper.php +++ b/erp24/helpers/ImageHelper.php @@ -24,9 +24,26 @@ class ImageHelper } + /** + * Выводит HTML img тег с безопасным экранированием параметров + * + * @param string $url URL изображения (http/https или локальный путь) + * @param int $width Ширина изображения в пиксельях + * @return void + */ public static function drawImage($url, $width = 200) { + // Экранируем параметры для безопасности + $width = (int)$width; + $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8'); + ?> - src='' alt="img" onclick="window.open(this.src, '_blank');" /> + img 'https://api2.erp.erp-flowers.ru', + 'dev.erp-flowers.ru' => 'https://api2.dev.erp-flowers.ru', + 'dev1.erp-flowers.ru' => 'https://api2.dev1.erp-flowers.ru', + 'localhost' => 'http://localhost:5555', + ]; + + private const API2_DEFAULT_URL = 'https://api2.erp.erp-flowers.ru'; + private const PRODUCTION_ECQ_URL = 'https://erp.erp-flowers.ru'; + private const URL_CHECK_CACHE_TTL = 300; // 5 минут + private const ALLOWED_HOSTS = [ + 'erp.erp-flowers.ru', + 'dev.erp-flowers.ru', + 'dev1.erp-flowers.ru', + 'localhost', + ]; + public static function uploadFile($label, $admin_id) { if (isset($_FILES[$label]["name"])) { $isMultiple = is_array($_FILES[$label]["name"]); @@ -195,87 +214,127 @@ class FileService /** * Получает базовый URL для api2 в зависимости от текущего домена + * + * @return string URL api2 для текущего окружения */ private static function getApi2BaseUrl(): string { - $currentHost = $_SERVER['HTTP_HOST'] ?? 'localhost'; + // Используем SERVER_NAME вместо HTTP_HOST для безопасности + $currentHost = $_SERVER['SERVER_NAME'] ?? $_SERVER['HTTP_HOST'] ?? 'localhost'; - // Для продакшена - if ($currentHost === 'erp.erp-flowers.ru') { - return 'https://api2.erp.erp-flowers.ru'; - } - - // Для dev сервера - if ($currentHost === 'dev.erp-flowers.ru') { - return 'https://api2.dev.erp-flowers.ru'; - } - - // Для dev1 сервера - if ($currentHost === 'dev1.erp-flowers.ru') { - return 'https://api2.dev1.erp-flowers.ru'; - } - - // Для локальной разработки (предполагаем localhost:5555 для api2) - if (str_contains($currentHost, 'localhost')) { - return 'http://localhost:5555'; + // Проверяем хост в белом списке для безопасности + if (in_array($currentHost, self::ALLOWED_HOSTS, true)) { + return self::API2_URLS[$currentHost] ?? self::API2_DEFAULT_URL; } // По умолчанию продакшен - return 'https://api2.erp.erp-flowers.ru'; + return self::API2_DEFAULT_URL; } /** - * Проверяет доступность URL + * Проверяет доступность URL с кэшированием результатов + * + * @param string $url URL для проверки + * @return bool true если URL доступен, false если нет */ private static function checkUrlAvailability(string $url): bool { + $cacheKey = 'kik_image_url_' . md5($url); + $cache = Yii::$app->cache; + + // Проверяем кэш + $cachedResult = $cache->get($cacheKey); + if ($cachedResult !== false) { + return (bool)$cachedResult; + } + + // Определяем, нужна ли верификация SSL + $isLocal = str_contains($url, 'localhost') || str_contains($url, 'dev'); + $client = new Client([ - 'timeout' => 5, - 'connect_timeout' => 2, - 'verify' => false, // Для тестовых сред + 'timeout' => 3, + 'connect_timeout' => 1, + 'verify' => !$isLocal, // Отключаем верификацию только для local/dev + 'http_errors' => false, // Не выбрасываем исключение на HTTP ошибок ]); try { $response = $client->head($url); - return $response->getStatusCode() === 200; + $result = $response->getStatusCode() === 200; + } catch (GuzzleException $e) { + // Логируем ошибку для диагностики + Yii::warning( + "KIK Image URL check failed for {$url}: " . $e->getMessage(), + __METHOD__ + ); + $result = false; } catch (\Exception $e) { - return false; + // Ловим другие возможные исключения + Yii::warning( + "Unexpected error checking URL {$url}: " . $e->getMessage(), + __METHOD__ + ); + $result = false; } + + // Кэшируем результат проверки + $cache->set($cacheKey, $result ? 1 : 0, self::URL_CHECK_CACHE_TTL); + + return $result; } /** * Получает корректный URL для изображения с проверкой доступности + * + * На продакшене возвращает текущий URL без дополнительных проверок. + * На dev/local сначала проверяет текущий, затем fallback на продакшен. + * + * @param string $relativePath Относительный путь к файлу (например /uploads/...) + * @return string Полный URL изображения */ private static function getCorrectImageUrl(string $relativePath): string { $currentApi2Url = self::getApi2BaseUrl() . $relativePath; - $prodApi2Url = 'https://api2.erp.erp-flowers.ru' . $relativePath; + $prodApi2Url = self::API2_DEFAULT_URL . $relativePath; - // Если текущий URL доступен, используем его + // На продакшене не проверяем альтернативные URL + if ($currentApi2Url === $prodApi2Url) { + return $currentApi2Url; + } + + // На dev/local сначала проверяем текущий if (self::checkUrlAvailability($currentApi2Url)) { return $currentApi2Url; } - // Если текущий недоступен, пробуем продакшен + // Fallback на продакшен if (self::checkUrlAvailability($prodApi2Url)) { return $prodApi2Url; } - // Если ничего не доступно, возвращаем текущий (лучше показать ошибку чем ничего) + // Если ничего не доступно, возвращаем текущий (показать ошибку лучше, чем ничего) return $currentApi2Url; } public static function drawFile($file) { if ($file->file_type == 'image') { $url = $file->url; + // Корректировка URL для файлов kikfeedbackrequest_file с типом image if ($file->entity == 'kikfeedbackrequest_file') { if (str_starts_with($url, '/uploads/')) { + // Относительный путь - нужно добавить полный URL $url = self::getCorrectImageUrl($url); } else { - $url = str_replace('https://erp.erp-flowers.ru', self::getApi2BaseUrl(), $url); + $newBaseUrl = self::getApi2BaseUrl(); + + // Заменяем старый URL на текущий базовый URL + if (str_starts_with($url, self::PRODUCTION_ECQ_URL)) { + $url = str_replace(self::PRODUCTION_ECQ_URL, $newBaseUrl, $url); + } } } + ImageHelper::drawImage($url); } else { ?>url)?>