diff --git a/codes/models.py b/codes/models.py index b95593f..43c84c7 100644 --- a/codes/models.py +++ b/codes/models.py @@ -8,12 +8,12 @@ from common.utils.codeManger import set_expire class CertificationCode(BaseModel): use_type = models.CharField(choices=CertificateCodeUseType.choices, max_length=5) code = models.CharField(max_length=6) - expire_at = models.DateTimeField(default=set_expire(minutes=5)) + expire_at = models.DateTimeField(default=set_expire) is_used = models.BooleanField(default=False) identifier = models.CharField(max_length=40) class InviteCode(BaseModel): use_type = models.CharField(choices=InviteCodeUseType.choices, max_length=5) code = models.CharField(max_length=10) - expire_at = models.DateTimeField(default=set_expire(minutes=10080)) # 일주일은 10080분 + expire_at = models.DateTimeField(default=set_expire) # 일주일은 10080분 identifier = models.CharField(max_length=40) diff --git a/requirements.txt b/requirements.txt index 513391e..3db3c27 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/users/models.py b/users/models.py index a08f0e7..032c4d4 100644 --- a/users/models.py +++ b/users/models.py @@ -28,18 +28,20 @@ class UserManager(BaseUserManager): class User(BaseModel, AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) - is_email_verified = models.BooleanField(default=False) + realname = models.CharField(max_length=10, blank=True) + # is_email_verified = models.BooleanField(default=False) is_plers_terms_of_service = models.BooleanField(default=False) is_terms_of_service_colio = models.BooleanField(default=False) is_consent_personal_info = models.BooleanField(default=False) is_consent_third_party_sharing = models.BooleanField(default=False) is_consent_marketing = models.BooleanField(default=False) phone = models.CharField(max_length=11, blank=True) - is_phone_verified = models.BooleanField(default=False) + # is_phone_verified = models.BooleanField(default=False) nickname = models.CharField(max_length=20, blank=True) gender = models.CharField(choices=GenderChoices.choices, max_length=1, blank=True) birth_date = models.CharField(max_length=10, blank=True) custom_url = models.CharField(max_length=20, blank=True) + is_custom_url = models.BooleanField(default=False) job_and_interests = ArrayField(models.CharField(max_length=20), default=list, blank=True) skills = ArrayField(models.CharField(max_length=20), default=list, blank=True) external_links = ArrayField(models.TextField(), default=list, blank=True) @@ -47,7 +49,7 @@ class User(BaseModel, AbstractBaseUser, PermissionsMixin): profile_image = models.ImageField(upload_to='', blank=True) is_staff = models.BooleanField(default=False) - is_active = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) objects = UserManager() diff --git a/users/serializers.py b/users/serializers.py new file mode 100644 index 0000000..7b05ed5 --- /dev/null +++ b/users/serializers.py @@ -0,0 +1,23 @@ +from .models import * +from rest_framework import serializers + +class JoinSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = [ + 'email', + 'password', + 'is_plers_terms_of_service', + 'is_terms_of_service_colio', + 'is_consent_personal_info', + 'is_consent_third_party_sharing', + 'is_consent_marketing', + 'realname', + 'phone', + 'nickname', + 'gender', + 'birth_date' + ] + + def create(self, validated_data): + return User.objects.create_user(**validated_data) \ No newline at end of file diff --git a/users/urls.py b/users/urls.py new file mode 100644 index 0000000..8d3fc65 --- /dev/null +++ b/users/urls.py @@ -0,0 +1,12 @@ +from django.urls import path + +from .views import * + +app_name = 'users' + +urlpatterns = [ + path('refresh-token/', RefreshAPIView.as_view()), + path('join/', JoinAPIView.as_view()), + path('login/', LoginAPIView.as_view()), + path('check/', NicknameAPIView.as_view()), +] \ No newline at end of file diff --git a/users/views.py b/users/views.py index 91ea44a..68efe8a 100644 --- a/users/views.py +++ b/users/views.py @@ -1,3 +1,97 @@ -from django.shortcuts import render +from django.shortcuts import get_object_or_404 -# Create your views here. +from rest_framework.views import APIView +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 .models import * +from .serializers import * + + +class RefreshAPIView(APIView): + permission_classes = [AllowAny] + # access token 재발급 + 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="Lax", secure=True) + return res + except TokenError as e: + return Response({"message": f"Invalid token: {e}"}, status=status.HTTP_401_UNAUTHORIZED) + + + +class JoinAPIView(APIView): + permission_classes = [AllowAny] + # 회원가입 + 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] + # 로그인 + 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="Lax", secure=True) + 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) + \ No newline at end of file