Fix hardcoded localhost:8000 URLs
Some checks failed
continuous-integration/drone/push Build is failing

- Add backend/utils.py for URL management
- Update serializers to use normalize_file_url()
- Update views to use URL utils from env vars
- Fix frontend components to use NEXT_PUBLIC_API_URL
- Add new env vars: DJANGO_BACKEND_URL, DJANGO_MEDIA_BASE_URL
- Replace all hardcoded localhost:8000 with configurable URLs
This commit is contained in:
2025-11-08 19:25:35 +09:00
parent fb74a4a25d
commit e82f0f8e6f
17 changed files with 1396 additions and 58 deletions

View File

@@ -3,6 +3,7 @@ from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import Link, LinkGroup
from django.conf import settings
from backend.utils import build_media_url, normalize_file_url
from .models import Link, LinkGroup
User = get_user_model()
@@ -39,12 +40,8 @@ class UserSerializer(serializers.ModelSerializer):
request = self.context.get('request')
if request:
absolute_uri = request.build_absolute_uri(obj.avatar.url)
# Заменяем различные варианты внутренних Docker URL
absolute_uri = absolute_uri.replace('http://web:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://links-web-1:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://backend:8000', 'http://localhost:8000')
return absolute_uri
return f'http://localhost:8000{obj.avatar.url}'
return normalize_file_url(absolute_uri)
return build_media_url(obj.avatar.url)
return None
@@ -71,12 +68,8 @@ class LinkGroupSerializer(serializers.ModelSerializer):
request = self.context.get('request')
if request:
absolute_uri = request.build_absolute_uri(obj.icon.url)
# Заменяем различные варианты внутренних Docker URL
absolute_uri = absolute_uri.replace('http://web:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://links-web-1:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://backend:8000', 'http://localhost:8000')
return absolute_uri
return f'http://localhost:8000{obj.icon.url}'
return normalize_file_url(absolute_uri)
return build_media_url(obj.icon.url)
return None
def get_background_image_url(self, obj):
@@ -85,12 +78,8 @@ class LinkGroupSerializer(serializers.ModelSerializer):
request = self.context.get('request')
if request:
absolute_uri = request.build_absolute_uri(obj.background_image.url)
# Заменяем различные варианты внутренних Docker URL
absolute_uri = absolute_uri.replace('http://web:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://links-web-1:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://backend:8000', 'http://localhost:8000')
return absolute_uri
return f'http://localhost:8000{obj.background_image.url}'
return normalize_file_url(absolute_uri)
return build_media_url(obj.background_image.url)
return None
def validate_header_color(self, value):
@@ -126,10 +115,6 @@ class LinkSerializer(serializers.ModelSerializer):
request = self.context.get('request')
if request:
absolute_uri = request.build_absolute_uri(obj.icon.url)
# Заменяем различные варианты внутренних Docker URL
absolute_uri = absolute_uri.replace('http://web:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://links-web-1:8000', 'http://localhost:8000')
absolute_uri = absolute_uri.replace('http://backend:8000', 'http://localhost:8000')
return absolute_uri
return f'http://localhost:8000{obj.icon.url}'
return normalize_file_url(absolute_uri)
return build_media_url(obj.icon.url)
return None

View File

@@ -9,6 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from drf_spectacular.utils import extend_schema, OpenApiParameter
from django.contrib.auth import get_user_model
from backend.utils import normalize_file_url, build_media_url
from .models import Link, LinkGroup
from .serializers import (
@@ -87,14 +88,11 @@ class PublicUserGroupsView(APIView):
from customization.models import DesignSettings
try:
design_settings = DesignSettings.objects.get(user=user)
# Заменяем Docker URL на localhost для клиента
# Заменяем Docker URL на внешний для клиента
background_image_url = None
if design_settings.background_image:
background_image_url = request.build_absolute_uri(design_settings.background_image.url)
# Заменяем различные варианты внутренних Docker URL
background_image_url = background_image_url.replace('http://web:8000', 'http://localhost:8000')
background_image_url = background_image_url.replace('http://links-web-1:8000', 'http://localhost:8000')
background_image_url = background_image_url.replace('http://backend:8000', 'http://localhost:8000')
background_image_url = normalize_file_url(background_image_url)
design_data = {
'theme_color': design_settings.theme_color,
@@ -135,18 +133,12 @@ class PublicUserGroupsView(APIView):
avatar_url = None
if user.avatar:
avatar_url = request.build_absolute_uri(user.avatar.url)
# Заменяем различные варианты внутренних Docker URL
avatar_url = avatar_url.replace('http://web:8000', 'http://localhost:8000')
avatar_url = avatar_url.replace('http://links-web-1:8000', 'http://localhost:8000')
avatar_url = avatar_url.replace('http://backend:8000', 'http://localhost:8000')
avatar_url = normalize_file_url(avatar_url)
cover_url = None
if user.cover:
cover_url = request.build_absolute_uri(user.cover.url)
# Заменяем различные варианты внутренних Docker URL
cover_url = cover_url.replace('http://web:8000', 'http://localhost:8000')
cover_url = cover_url.replace('http://links-web-1:8000', 'http://localhost:8000')
cover_url = cover_url.replace('http://backend:8000', 'http://localhost:8000')
cover_url = normalize_file_url(cover_url)
result = {
"username": user.username,
@@ -163,19 +155,13 @@ class PublicUserGroupsView(APIView):
grp_icon_url = None
if grp.icon:
grp_icon_url = request.build_absolute_uri(grp.icon.url)
# Заменяем различные варианты внутренних Docker URL
grp_icon_url = grp_icon_url.replace('http://web:8000', 'http://localhost:8000')
grp_icon_url = grp_icon_url.replace('http://links-web-1:8000', 'http://localhost:8000')
grp_icon_url = grp_icon_url.replace('http://backend:8000', 'http://localhost:8000')
grp_icon_url = normalize_file_url(grp_icon_url)
# background_image у группы
grp_bg_url = None
if grp.background_image:
grp_bg_url = request.build_absolute_uri(grp.background_image.url)
# Заменяем различные варианты внутренних Docker URL
grp_bg_url = grp_bg_url.replace('http://web:8000', 'http://localhost:8000')
grp_bg_url = grp_bg_url.replace('http://links-web-1:8000', 'http://localhost:8000')
grp_bg_url = grp_bg_url.replace('http://backend:8000', 'http://localhost:8000')
grp_bg_url = normalize_file_url(grp_bg_url)
grp_data = {
"id": grp.id,
@@ -193,10 +179,7 @@ class PublicUserGroupsView(APIView):
ln_icon_url = None
if ln.icon:
ln_icon_url = request.build_absolute_uri(ln.icon.url)
# Заменяем различные варианты внутренних Docker URL
ln_icon_url = ln_icon_url.replace('http://web:8000', 'http://localhost:8000')
ln_icon_url = ln_icon_url.replace('http://links-web-1:8000', 'http://localhost:8000')
ln_icon_url = ln_icon_url.replace('http://backend:8000', 'http://localhost:8000')
ln_icon_url = normalize_file_url(ln_icon_url)
grp_data["links"].append({
"id": ln.id,

View File

@@ -184,6 +184,13 @@ MEDIA_URL = os.getenv('DJANGO_MEDIA_URL', '/storage/')
MEDIA_ROOT = BASE_DIR / 'storage'
# Настройки безопасности из переменных окружения
# URL настройки из переменных окружения
BACKEND_URL = os.getenv('DJANGO_BACKEND_URL', 'http://localhost:8000')
BACKEND_PROTOCOL = os.getenv('DJANGO_BACKEND_PROTOCOL', 'http')
BACKEND_DOMAIN = os.getenv('DJANGO_BACKEND_DOMAIN', 'localhost:8000')
MEDIA_BASE_URL = os.getenv('DJANGO_MEDIA_BASE_URL', 'http://localhost:8000')
# Безопасность
SECURE_SSL_REDIRECT = os.getenv('DJANGO_SECURE_SSL_REDIRECT', 'False') == 'True'
SECURE_HSTS_SECONDS = int(os.getenv('DJANGO_SECURE_HSTS_SECONDS', '0'))
SECURE_HSTS_INCLUDE_SUBDOMAINS = os.getenv('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', 'False') == 'True'

59
backend/backend/utils.py Normal file
View File

@@ -0,0 +1,59 @@
"""
Утилиты для работы с URL в Django backend
"""
import os
from django.conf import settings
def get_backend_url():
"""Получить базовый URL backend из настроек"""
return getattr(settings, 'BACKEND_URL', 'http://localhost:8000')
def get_media_base_url():
"""Получить базовый URL для медиа файлов"""
return getattr(settings, 'MEDIA_BASE_URL', 'http://localhost:8000')
def build_absolute_url(path):
"""Построить абсолютный URL для пути"""
base_url = get_backend_url()
if not path.startswith('/'):
path = '/' + path
return f"{base_url.rstrip('/')}{path}"
def build_media_url(media_path):
"""Построить абсолютный URL для медиа файла"""
if not media_path:
return None
base_url = get_media_base_url()
if not media_path.startswith('/'):
media_path = '/' + media_path
return f"{base_url.rstrip('/')}{media_path}"
def normalize_file_url(file_url):
"""
Нормализация URL файлов - заменяет внутренние Docker URL на внешние
"""
if not file_url:
return file_url
base_url = get_backend_url()
# Список внутренних URL для замены
internal_urls = [
'http://web:8000',
'http://links-web-1:8000',
'http://backend:8000',
'http://localhost:8000'
]
# Заменяем все внутренние URL на внешний
for internal_url in internal_urls:
if internal_url in file_url:
file_url = file_url.replace(internal_url, base_url.rstrip('/'))
return file_url

View File

@@ -4,6 +4,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import serializers
from django.contrib.auth import get_user_model
from backend.utils import normalize_file_url
User = get_user_model()
@@ -22,18 +23,16 @@ class UserProfileSerializer(serializers.ModelSerializer):
if obj.avatar:
request = self.context.get('request')
if request:
# Заменяем внутренний Docker URL на localhost для клиента
absolute_uri = request.build_absolute_uri(obj.avatar.url)
return absolute_uri.replace('http://web:8000', 'http://localhost:8000')
return normalize_file_url(absolute_uri)
return None
def get_cover_url(self, obj):
if obj.cover:
request = self.context.get('request')
if request:
# Заменяем внутренний Docker URL на localhost для клиента
absolute_uri = request.build_absolute_uri(obj.cover.url)
return absolute_uri.replace('http://web:8000', 'http://localhost:8000')
return normalize_file_url(absolute_uri)
return None
@api_view(['GET', 'PUT', 'PATCH'])