from django.conf import settings from django.shortcuts import get_object_or_404 from rest_framework.views import APIView from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenRefreshSerializer from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.exceptions import TokenError, InvalidToken from rest_framework import status from rest_framework.response import Response from rest_framework.permissions import AllowAny, IsAuthenticated from django.contrib.auth import authenticate from django.db.models import Case, When, Value, IntegerField, Q from .models import * from .serializers import * from .services import * from projects.serializers import * from portfolios.serializers import * from django.db import transaction class RefreshAPIView(APIView): permission_classes = [AllowAny] # access token 재발급 @transaction.atomic def post(self, request): refresh = request.COOKIES.get("refresh") if not refresh: return Response({"message": "No refresh token"}, status=status.HTTP_400_BAD_REQUEST) if request.data.get("user_id", None) != RefreshToken(refresh).payload.get("user_id", None): return Response({"message": "Wrong userid"}, status=status.HTTP_400_BAD_REQUEST) try: serializer = TokenRefreshSerializer(data={'refresh': refresh}) if serializer.is_valid(): res = Response({"access": serializer.validated_data['access']}, status=status.HTTP_200_OK) res.set_cookie("refresh", serializer.validated_data['refresh'], httponly=True, samesite=None, secure=not settings.DEBUG) return res except TokenError as e: return Response({"message": f"Invalid token: {e}"}, status=status.HTTP_401_UNAUTHORIZED) class JoinAPIView(APIView): permission_classes = [AllowAny] # 회원가입 @transaction.atomic def post(self, request): serializer = JoinSerializer(data=request.data) if serializer.is_valid(): serializer.save() res = Response({"message": "회원가입이 완료되었습니다."}, status=status.HTTP_200_OK) return res return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class LoginAPIView(APIView): permission_classes = [AllowAny] # 로그인 @transaction.atomic def post(self, request): email=request.data.get("email", None) password=request.data.get("password", None) # 로그인 전 쿠키에 있는 리프레시 토큰 무효화 try: refresh = request.COOKIES.get('refresh') if refresh: refresh_token = RefreshToken(refresh) refresh_token.blacklist() except TokenError as e: pass if user := authenticate(email=email, password=password): serializer = TokenObtainPairSerializer(data={'email': email, 'password': password}) # id, 비번 맞나 틀리나 검사 if serializer.is_valid(): res = Response( { "message": "login success", "user_id": user.id, "nickname": user.nickname, "access": serializer.validated_data['access'], "is_custom_url": user.is_custom_url }, status=status.HTTP_200_OK, ) res.set_cookie("refresh", serializer.validated_data['refresh'], httponly=True, samesite=None, secure=not settings.DEBUG) return res else: return Response(serializer.errors) else: # id, 비번 둘 중 하나가 틀렸을 때 return Response({"message": "아이디 혹은 비밀번호가 맞지 않습니다."}, status=status.HTTP_400_BAD_REQUEST) class NicknameAPIView(APIView): permission_classes = [AllowAny] # 닉네임 중복 체크 def get(self, request): nickname = request.GET.get('nickname', None) if not nickname: return Response({"message": "닉네임을 입력하세요."}, status=status.HTTP_400_BAD_REQUEST) try: get_object_or_404(User, nickname=nickname) return Response({"message": "해당 닉네임은 사용할 수 없습니다."}, status=status.HTTP_400_BAD_REQUEST) except: return Response({"message": "사용할 수 있는 닉네임입니다."}, status=status.HTTP_200_OK) class TagUserAPIView(APIView): def get(self, request): nickname = request.query_params.get('nickname') users = User.objects.filter(nickname__icontains=nickname).annotate( priority=Case( When(nickname__iexact=nickname, then=Value(0)), default=Value(1), output_field=IntegerField() ) ).order_by('priority').values('profile_image', 'nickname')[:5] serializer = TagUserSerializer(users, many=True) return Response({'users': serializer.data}) class SetPortofolioRequiredInfoAPIView(APIView): def get(self, request): custom_url = request.GET.get('custom_url', None) if not custom_url: return Response({"message": "no url"}, status=status.HTTP_400_BAD_REQUEST) if User.objects.filter(custom_url=custom_url).exists(): return Response({"message": "already used url"}, status=status.HTTP_400_BAD_REQUEST) else: return Response({"message": "can use this url"}, status=status.HTTP_200_OK) @transaction.atomic def patch(self, request): user = request.user serializer = SetPortofolioRequiredInfoSerializer(user, data=request.data) if serializer.is_valid(): serializer.save() user.is_custom_url = True user.save() return Response({"message": "updated successfully"}, status=status.HTTP_202_ACCEPTED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # 마이페이지 관련 class MyPageProfileAPIView(APIView): # 프로필 조회(안 읽은 알림 수) def get(self, request, nickname): target_user = get_object_or_404(User, nickname=nickname) user = request.user serializer = UserProfileSerializer(target_user) data = serializer.data data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user) data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) # 프로필 수정 @transaction.atomic def patch(self, request, nickname): target_user = get_object_or_404(User, nickname=nickname) user = request.user if user == target_user: serializer = UserProfileSerializer(user, request.data, partial=True) if serializer.is_valid(): serializer.save() data = serializer.data data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user) data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response({"message": "Not account owner"}, status=status.HTTP_400_BAD_REQUEST) class MyPageWorkListAPIView(APIView): # 포폴, 플젝, 작업중, 스크랩 조회(안 읽은 알림 수) def get(self, request, nickname): target_user = get_object_or_404(User, nickname=nickname) user = request.user retreive_type = request.query_params.get('type') data = {} if retreive_type == 'portfolio': portfolios = UserToPortfolioService.get_published_portfolio(target_user) serializer = PortfolioListViewSerializer(portfolios, many=True) data = { "portfolios": serializer.data } elif retreive_type == 'project': solo_projects = UserToProjectService.get_published_solo_project(target_user) team_projects = UserToProjectService.get_published_team_project(target_user) sp_serializer = ProjectListViewSerializer(solo_projects, many=True) tp_serializer = ProjectListViewSerializer(team_projects, many=True) data = { "solo_projects": sp_serializer.data, "team_projects": tp_serializer.data, } elif retreive_type == 'working': if user != target_user: return Response({"message": "Not account owner"}, status=status.HTTP_400_BAD_REQUEST) portfolios = UserToPortfolioService.get_unpublished_portfolio(target_user) solo_projects = UserToProjectService.get_unpublished_solo_project(target_user) team_projects = UserToProjectService.get_unpublished_team_project(target_user) po_serializer = PortfolioListViewSerializer(portfolios, many=True) sp_serializer = ProjectListViewSerializer(solo_projects, many=True) tp_serializer = ProjectListViewSerializer(team_projects, many=True) data = { "portfolios": po_serializer.data, "solo_projects": sp_serializer.data, "team_projects": tp_serializer.data, } elif retreive_type == 'scrap': if user != target_user: return Response({"message": "Not account owner"}, status=status.HTTP_400_BAD_REQUEST) portfolios = UserToPortfolioService.get_scrap_portfolio(target_user) po_serializer = PortfolioListViewSerializer(portfolios, many=True) projects = UserToProjectService.get_scrap_project(target_user) pr_serializer = ProjectListViewSerializer(projects, many=True) data = { "portfolios": po_serializer.data, "projects": pr_serializer.data, } else: return Response({"message": "not allowed retreive_type"}, status=status.HTTP_400_BAD_REQUEST) data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) class MyPageMemberInfoAPIView(APIView): # 내 정보 조회(안 읽은 알림 수) def get(self, request): user = request.user serializer = UserMemberInfoSerializer(user) data = serializer.data data['new_notification_count'] = UserToNotificationService.get_new_notification_count(user) return Response(data, status=status.HTTP_200_OK) # 내 정보 수정 @transaction.atomic def patch(self, request): user = request.user serializer = UserMemberInfoSerializer(user, request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class NotificationReadViewSet(ReadOnlyModelViewSet): serializer_class = NotificationSerializer # 30일 이전 알림만 가져옴 def get_queryset(self): return UserToNotificationService.get_all_notification(self.request.user) def list(self, request, *args, **kwargs): queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) return Response(serializer.data, status=status.HTTP_200_OK) def retrieve(self, request, *args, **kwargs): instance = self.get_object() instance.is_read = True instance.save() serializer = self.get_serializer(instance) return Response(serializer.data, status=status.HTTP_200_OK)