diff --git a/DYNAMIC_MEDIA_URLS.md b/DYNAMIC_MEDIA_URLS.md new file mode 100644 index 00000000..2f671d5f --- /dev/null +++ b/DYNAMIC_MEDIA_URLS.md @@ -0,0 +1,130 @@ +# Динамическая генерация ссылок на медиа + +## Проблема +При работе с несколькими доменами (локальный и глобальный) статические ссылки на медиа-файлы работали только для одного домена. + +## Решение +Реализовали динамическую генерацию ссылок на медиа на основе текущего домена запроса. + +## Что изменилось + +### 1. Context Processor +**Файл:** `backend/core/context_processors.py` + +Добавлен context processor `media_domain()`, который: +- Определяет текущий домен из запроса (`request.get_host()`) +- Формирует динамический `MEDIA_URL_DYNAMIC` с учётом протокола и домена +- Добавляет переменные в контекст всех шаблонов: + - `MEDIA_URL_DYNAMIC` — полный URL для медиа (например: `http://localhost:9000/media/` или `https://example.com/media/`) + - `CURRENT_DOMAIN` — текущий домен + - `CURRENT_SCHEME` — протокол (http/https) + +### 2. Настройки Django +**Файл:** `backend/eusrr_backend/settings.py` + +Добавлен context processor в `TEMPLATES`: +```python +"context_processors": [ + # ... + "core.context_processors.media_domain", +], +``` + +### 3. Обновлённые шаблоны +Все шаблоны обновлены для использования динамических ссылок: + +#### `backend/templates/feed/_feed_cards.html` +- Изображения постов +- Аватары авторов +- Добавлен `loading="lazy"` и `decoding="async"` для оптимизации + +#### `backend/templates/feed/feed_list.html` +- Аватары новых сотрудников + +#### `backend/templates/feed/post_detail.html` +- Изображения постов в детальном виде +- Аватары авторов + +## Логика обработки URL + +Шаблоны проверяют формат URL и обрабатывают его соответственно: + +```django +{% if img_url|slice:":7" == "/media/" %} + {# Относительный путь /media/... → добавляем домен #} + ... +{% elif img_url|slice:":1" == "/" %} + {# Абсолютный путь /... → добавляем схему и домен #} + ... +{% else %} + {# Уже полный URL или внешняя ссылка → используем как есть #} + ... +{% endif %} +``` + +## Примеры работы + +### Локальный домен (localhost:9000) +``` +Запрос: http://localhost:9000/feed/ +MEDIA_URL_DYNAMIC: http://localhost:9000/media/ + +Файл: /media/feed/images/photo.jpg +Результат: http://localhost:9000/media/feed/images/photo.jpg +``` + +### Глобальный домен (example.com) +``` +Запрос: https://example.com/feed/ +MEDIA_URL_DYNAMIC: https://example.com/media/ + +Файл: /media/feed/images/photo.jpg +Результат: https://example.com/media/feed/images/photo.jpg +``` + +## Преимущества + +✅ **Автоматическая адаптация** — один код работает на всех доменах +✅ **Без перезапуска** — не нужно менять настройки при переключении доменов +✅ **SEO-дружественные URL** — правильные абсолютные ссылки +✅ **Поддержка HTTPS/HTTP** — автоматически определяется из запроса +✅ **Оптимизация загрузки** — добавлены атрибуты `loading="lazy"` и `decoding="async"` + +## Дальнейшие улучшения + +### Возможные оптимизации: +1. **CDN интеграция** — добавить отдельные CDN домены для статики +2. **Оптимизация изображений** — генерация thumbnails разных размеров +3. **WebP/AVIF форматы** — современные форматы для уменьшения размера +4. **Кэширование** — добавить Cache-Control заголовки +5. **Версионирование** — добавить версии к URL для инвалидации кэша + +### Пример с CDN: +```python +def media_domain(request): + host = request.get_host().split(':')[0] + + # Определяем CDN на основе домена + if host in ['localhost', '127.0.0.1']: + cdn_url = f"{request.scheme}://{request.get_host()}/media/" + elif 'dev' in host: + cdn_url = "https://cdn-dev.example.com/media/" + else: + cdn_url = "https://cdn.example.com/media/" + + return {'MEDIA_URL_DYNAMIC': cdn_url} +``` + +## Тестирование + +Проверьте работу на обоих доменах: +1. Откройте ленту на `http://localhost:9000/feed/` +2. Откройте ленту на `https://your-domain.com/feed/` +3. Убедитесь, что изображения загружаются корректно в обоих случаях +4. Проверьте в DevTools, что URLs формируются правильно + +## Откат изменений + +Если нужно вернуться к старому поведению: +1. Удалить `core.context_processors.media_domain` из `settings.py` +2. В шаблонах заменить `{{ MEDIA_URL_DYNAMIC }}{{ img_url|slice:"7:" }}` на `{{ img_url }}` diff --git a/backend/core/context_processors.py b/backend/core/context_processors.py new file mode 100644 index 00000000..a940542d --- /dev/null +++ b/backend/core/context_processors.py @@ -0,0 +1,26 @@ +""" +Context processors для глобальных переменных в шаблонах +""" + + +def media_domain(request): + """ + Динамически определяет MEDIA_URL на основе текущего домена запроса. + + Это позволяет правильно формировать ссылки на медиа как для локального, + так и для глобального домена без перезапуска сервера. + """ + host = request.get_host() + scheme = request.scheme + + # Формируем базовый URL для текущего домена + base_url = f"{scheme}://{host}" + + # Динамический MEDIA_URL + dynamic_media_url = f"{base_url}/media/" + + return { + 'MEDIA_URL_DYNAMIC': dynamic_media_url, + 'CURRENT_DOMAIN': host, + 'CURRENT_SCHEME': scheme, + } diff --git a/backend/eusrr_backend/settings.py b/backend/eusrr_backend/settings.py index fa587172..18559dff 100644 --- a/backend/eusrr_backend/settings.py +++ b/backend/eusrr_backend/settings.py @@ -80,6 +80,7 @@ TEMPLATES = [ "django.contrib.messages.context_processors.messages", "communications.context_processors.chat_unread_total", "eusrr_backend.context_processors.branding", + "core.context_processors.media_domain", ], }, }, diff --git a/backend/templates/feed/_feed_cards.html b/backend/templates/feed/_feed_cards.html index 0d26ca80..33abc1f1 100644 --- a/backend/templates/feed/_feed_cards.html +++ b/backend/templates/feed/_feed_cards.html @@ -13,7 +13,13 @@ {% if a %} {% firstof a.avatar as ava_url %} {% if ava_url %} - + {% if ava_url|slice:":7" == "/media/" %} + + {% elif ava_url|slice:":1" == "/" %} + + {% else %} + + {% endif %} {% else %} {% endif %} @@ -67,7 +73,13 @@ {% if img_url %}
{% if post_id %}{% endif %} - {{ post.title|default:'Публикация' }} + {% if img_url|slice:":7" == "/media/" %} + {{ post.title|default:'Публикация' }} + {% elif img_url|slice:":1" == "/" %} + {{ post.title|default:'Публикация' }} + {% else %} + {{ post.title|default:'Публикация' }} + {% endif %}
{% endif %} {% endif %} diff --git a/backend/templates/feed/feed_list.html b/backend/templates/feed/feed_list.html index 2f60cbd6..093b2a68 100644 --- a/backend/templates/feed/feed_list.html +++ b/backend/templates/feed/feed_list.html @@ -42,9 +42,17 @@ title="{{ emp_name }}"> {% if ava %} {% if ava.url %} - + {% if ava.url|slice:":7" == "/media/" %} + + {% else %} + + {% endif %} {% else %} - + {% if ava|slice:":7" == "/media/" %} + + {% else %} + + {% endif %} {% endif %} {% else %} diff --git a/backend/templates/feed/post_detail.html b/backend/templates/feed/post_detail.html index b6571db9..5b297372 100644 --- a/backend/templates/feed/post_detail.html +++ b/backend/templates/feed/post_detail.html @@ -64,7 +64,17 @@ {% with a=post.author %} {% if a %} {% firstof a.avatar.url a.avatar as ava_url %} - {% if ava_url %}{% else %}{% endif %} + {% if ava_url %} + {% if ava_url|slice:":7" == "/media/" %} + + {% elif ava_url|slice:":1" == "/" %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} {% else %} {% endif %} @@ -108,7 +118,13 @@ {% firstof post.image.url post.image as img_url %} {% if img_url %}
- {% firstof post.title 'Публикация' %} + {% if img_url|slice:":7" == "/media/" %} + {% firstof post.title 'Публикация' %} + {% elif img_url|slice:":1" == "/" %} + {% firstof post.title 'Публикация' %} + {% else %} + {% firstof post.title 'Публикация' %} + {% endif %}
{% endif %} {% endif %}