Files
links/backend/api/views.py
Andrey K. Choi e82f0f8e6f
Some checks failed
continuous-integration/drone/push Build is failing
Fix hardcoded localhost:8000 URLs
- 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
2025-11-08 19:25:35 +09:00

194 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# coding: utf-8
from rest_framework import generics, viewsets, permissions, status
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
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 (
RegisterSerializer,
UserSerializer,
LinkSerializer,
LinkGroupSerializer,
)
User = get_user_model()
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
permission_classes = [permissions.AllowAny]
serializer_class = RegisterSerializer
@method_decorator(csrf_exempt, name='dispatch')
class LoginView(TokenObtainPairView):
permission_classes = [permissions.AllowAny]
class LinkGroupViewSet(viewsets.ModelViewSet):
queryset = LinkGroup.objects.all()
serializer_class = LinkGroupSerializer
permission_classes = [permissions.IsAuthenticated]
parser_classes = [MultiPartParser, FormParser, JSONParser]
def get_queryset(self):
return self.queryset.filter(owner=self.request.user).order_by('order')
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class LinkViewSet(viewsets.ModelViewSet):
queryset = Link.objects.all()
serializer_class = LinkSerializer
permission_classes = [permissions.IsAuthenticated]
parser_classes = [MultiPartParser, FormParser, JSONParser]
def get_queryset(self):
return Link.objects.filter(owner=self.request.user).order_by('order')
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class UserProfileView(generics.RetrieveAPIView):
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user
class UserLinksListView(generics.ListAPIView):
serializer_class = LinkSerializer
permission_classes = [permissions.AllowAny]
def get_queryset(self):
username = self.kwargs['username']
return Link.objects.filter(owner__username=username).order_by('order')
class PublicUserGroupsView(APIView):
"""
GET /api/users/{username}/public/
"""
permission_classes = [permissions.AllowAny]
def get(self, request, username):
# 1. Ищем пользователя
user = get_object_or_404(User, username=username)
# 2. Получаем настройки дизайна пользователя
from customization.models import DesignSettings
try:
design_settings = DesignSettings.objects.get(user=user)
# Заменяем Docker URL на внешний для клиента
background_image_url = None
if design_settings.background_image:
background_image_url = request.build_absolute_uri(design_settings.background_image.url)
background_image_url = normalize_file_url(background_image_url)
design_data = {
'theme_color': design_settings.theme_color,
'background_image': background_image_url,
'dashboard_layout': design_settings.dashboard_layout,
'groups_default_expanded': design_settings.groups_default_expanded,
'show_group_icons': design_settings.show_group_icons,
'show_link_icons': design_settings.show_link_icons,
'dashboard_background_color': design_settings.dashboard_background_color,
'font_family': design_settings.font_family,
'header_text_color': getattr(design_settings, 'header_text_color', '#000000'),
'group_text_color': getattr(design_settings, 'group_text_color', '#333333'),
'link_text_color': getattr(design_settings, 'link_text_color', '#666666'),
'cover_overlay_enabled': getattr(design_settings, 'cover_overlay_enabled', False),
'cover_overlay_color': getattr(design_settings, 'cover_overlay_color', '#000000'),
'cover_overlay_opacity': getattr(design_settings, 'cover_overlay_opacity', 0.5),
}
except DesignSettings.DoesNotExist:
# Настройки по умолчанию
design_data = {
'theme_color': '#ffffff',
'background_image': None,
'dashboard_layout': 'list',
'groups_default_expanded': True,
'show_group_icons': True,
'show_link_icons': True,
'dashboard_background_color': '#f8f9fa',
'font_family': 'sans-serif',
}
# 3. Берём только публичные группы со ссылками
groups_qs = LinkGroup.objects.filter(
owner=user,
is_public=True # Показываем только публичные группы
).prefetch_related('links')
# Формируем URL аватара и обложки с заменой Docker URL
avatar_url = None
if user.avatar:
avatar_url = request.build_absolute_uri(user.avatar.url)
avatar_url = normalize_file_url(avatar_url)
cover_url = None
if user.cover:
cover_url = request.build_absolute_uri(user.cover.url)
cover_url = normalize_file_url(cover_url)
result = {
"username": user.username,
"full_name": user.full_name,
"bio": user.bio,
"avatar": avatar_url,
"cover": cover_url,
"design_settings": design_data,
"groups": []
}
for grp in groups_qs:
# icon у группы (абсолютный URL с заменой Docker URL)
grp_icon_url = None
if grp.icon:
grp_icon_url = request.build_absolute_uri(grp.icon.url)
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)
grp_bg_url = normalize_file_url(grp_bg_url)
grp_data = {
"id": grp.id,
"name": grp.name,
"description": grp.description,
"icon_url": grp_icon_url, # Используем icon_url для консистентности с API
"background_image": grp_bg_url,
"header_color": grp.header_color,
"is_favorite": grp.is_favorite,
"links": [],
}
for ln in grp.links.all():
# icon у ссылки с заменой Docker URL
ln_icon_url = None
if ln.icon:
ln_icon_url = request.build_absolute_uri(ln.icon.url)
ln_icon_url = normalize_file_url(ln_icon_url)
grp_data["links"].append({
"id": ln.id,
"title": ln.title,
"url": ln.url,
"icon_url": ln_icon_url, # Используем icon_url для консистентности
"description": ln.description,
})
result["groups"].append(grp_data)
return Response(result, status=status.HTTP_200_OK)