]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Правки ревью feature_fomichev_erp-488_fix_kik_images_url origin/feature_fomichev_erp-488_fix_kik_images_url
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Sat, 1 Nov 2025 07:46:43 +0000 (10:46 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Sat, 1 Nov 2025 07:46:43 +0000 (10:46 +0300)
erp24/helpers/ImageHelper.php
erp24/services/FileService.php

index 9700196bfa0cf5ae1d8a83c2dfb59ef33aed8937..58392aa5e8f3e1cea49009aa97b5e3d0358cc6b2 100644 (file)
@@ -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');
+        
         ?>
-        <img width=<?= $width ?> src='<?= str_starts_with($url, 'http') ? $url : FileService::padWithSlash($url) ?>' alt="img" onclick="window.open(this.src, '_blank');" />
+        <img 
+            width="<?= $width ?>" 
+            src="<?= str_starts_with($url, 'http') ? $url : FileService::padWithSlash($url) ?>" 
+            alt="img" 
+            style="cursor: pointer;" 
+            onclick="window.open(this.src, '_blank');" 
+        />
         <?php
     }
 
index 5006cf18b65976cb2d454732659f11fc2a8cf341..83f6c3bddbfdb254aa8dc74cc8023f478d93d944 100755 (executable)
@@ -17,6 +17,25 @@ use yii_app\records\Images;
 class FileService
 {
     private const DEFAULT_MAX_BYTES = 8 * 1024 * 1024; // 8 MiB
+    
+    // Конфигурация для корректировки URL изображений KIK Feedback
+    private const API2_URLS = [
+        'erp.erp-flowers.ru' => '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;
         }
 
-        // Ð\95Ñ\81ли Ð½Ð¸Ñ\87его Ð½Ðµ Ð´Ð¾Ñ\81Ñ\82Ñ\83пно, Ð²Ð¾Ð·Ð²Ñ\80аÑ\89аем Ñ\82екÑ\83Ñ\89ий (лÑ\83Ñ\87Ñ\88е Ð¿Ð¾ÐºÐ°Ð·Ð°Ñ\82Ñ\8c Ð¾Ñ\88ибкÑ\83 чем ничего)
+        // Ð\95Ñ\81ли Ð½Ð¸Ñ\87его Ð½Ðµ Ð´Ð¾Ñ\81Ñ\82Ñ\83пно, Ð²Ð¾Ð·Ð²Ñ\80аÑ\89аем Ñ\82екÑ\83Ñ\89ий (показаÑ\82Ñ\8c Ð¾Ñ\88ибкÑ\83 Ð»Ñ\83Ñ\87Ñ\88е, чем ничего)
         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 {
             ?><a href="<?= Url::to(['/files/download', 'url' => $file->url]) ?>" class="btn btn-link" target="_blank" data-pjax="0"><?= basename($file->url)?></a><?php