@@ -4,6 +4,11 @@ from common.models.baseModels import BaseModel
|
||||
from common.models.choiceModels import CertificateCodeUseType, InviteCodeUseType
|
||||
from common.utils.codeManger import set_expire
|
||||
|
||||
# from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
# from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
# from projects.models import Project
|
||||
|
||||
|
||||
class CertificationCode(BaseModel):
|
||||
use_type = models.CharField(choices=CertificateCodeUseType.choices, max_length=5)
|
||||
@@ -17,3 +22,4 @@ class InviteCode(BaseModel):
|
||||
code = models.CharField(max_length=10)
|
||||
expire_at = models.DateTimeField(default=set_expire) # 일주일은 10080분
|
||||
identifier = models.CharField(max_length=40)
|
||||
|
||||
|
||||
@@ -8,3 +8,7 @@ from common.utils.codeManger import set_expire, generate_code
|
||||
class CertificateCodeSerializer(serializers.Serializer):
|
||||
identifier = serializers.CharField(max_length=40, write_only=True)
|
||||
code = serializers.CharField(max_length=6, write_only=True, required=False)
|
||||
|
||||
class InviteCodeSerializer(serializers.Serializer):
|
||||
identifier = serializers.CharField(max_length=40, write_only=True)
|
||||
code = serializers.CharField(max_length=10, write_only=True, required=False)
|
||||
@@ -10,16 +10,25 @@ from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
from .models import CertificationCode
|
||||
from .models import *
|
||||
|
||||
from common.models.choiceModels import CertificateCodeUseType
|
||||
from common.models.choiceModels import CertificateCodeUseType, InviteCodeUseType
|
||||
from common.utils.codeManger import set_expire
|
||||
|
||||
from solapi import SolapiMessageService
|
||||
from solapi.model import RequestMessage
|
||||
|
||||
from users.models import User
|
||||
|
||||
from projects.models import Project, ProjectTeamList
|
||||
|
||||
# from .schemas import send_sms_post_schema # Swagger나 drf-spectacular 등에 사용되는 데코레이터
|
||||
|
||||
INVITE_CHOICE_USE_TYPE ={
|
||||
'p': InviteCodeUseType.PROJECT,
|
||||
'h': InviteCodeUseType.HACKATHON
|
||||
}
|
||||
|
||||
class CertificateService:
|
||||
@staticmethod
|
||||
def send(code, identifier):
|
||||
@@ -80,3 +89,45 @@ class SmsService(CertificateService):
|
||||
except Exception as e:
|
||||
# print(f"메시지 발송 실패: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
class InviteService:
|
||||
|
||||
# 종류마다 사용하는 테이블이 다르므로 오버라이딩
|
||||
@staticmethod
|
||||
def add_member(invitee, work):
|
||||
pass
|
||||
|
||||
# url 생성
|
||||
@staticmethod
|
||||
def create_invite_object_and_url(use_type, identifier, code) -> str:
|
||||
InviteCode.objects.create(
|
||||
use_type = INVITE_CHOICE_USE_TYPE[use_type],
|
||||
code = code,
|
||||
expire_at = set_expire(10080),
|
||||
identifier = identifier,
|
||||
)
|
||||
return f"https://{settings.DOMAIN_NAME}/invite?t={use_type}&i={identifier}&c={code}"
|
||||
|
||||
# 코드 유효성 검사
|
||||
@staticmethod
|
||||
def check_code(use_type, identifier, code) -> bool:
|
||||
if InviteCode.objects.filter(
|
||||
use_type = INVITE_CHOICE_USE_TYPE[use_type],
|
||||
identifier = identifier,
|
||||
code = code,
|
||||
expire_at__gte=now()
|
||||
).exists():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class ProjectInviteService(InviteService):
|
||||
|
||||
# 사용자 초대
|
||||
@staticmethod
|
||||
def add_member(invitee: User, work: Project):
|
||||
return ProjectTeamList.objects.create(user=invitee, project=work)
|
||||
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ app_name = 'codes'
|
||||
|
||||
urlpatterns = [
|
||||
path('certificate/', CertificationAPIView.as_view()),
|
||||
# path('invite/', )
|
||||
path('invite/', InviteByLinkAPIView.as_view())
|
||||
]
|
||||
@@ -14,11 +14,21 @@ from common.models.choiceModels import CertificateCodeUseType
|
||||
from common.utils.codeManger import generate_code
|
||||
|
||||
|
||||
certificate_use_type = {
|
||||
CERTIFICATE_SERVICE_USE_TYPE = {
|
||||
"phone": SmsService,
|
||||
# "email": EmailService
|
||||
}
|
||||
|
||||
INVITE_USE_TYPE = {
|
||||
"p": {
|
||||
"word": "project",
|
||||
"service": ProjectInviteService,
|
||||
"model": Project,
|
||||
"team_model": ProjectTeamList
|
||||
}
|
||||
# "h": HackathonInviteService
|
||||
}
|
||||
|
||||
class CertificationAPIView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@@ -26,7 +36,9 @@ class CertificationAPIView(APIView):
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
use_type = request.query_params.get("type")
|
||||
serv = certificate_use_type[use_type]
|
||||
if use_type not in CERTIFICATE_SERVICE_USE_TYPE:
|
||||
return Response({"message": "Not defined use_type"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
serv = CERTIFICATE_SERVICE_USE_TYPE[use_type]
|
||||
serializer = CertificateCodeSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
create_code = generate_code(6)
|
||||
@@ -44,7 +56,9 @@ class CertificationAPIView(APIView):
|
||||
@transaction.atomic
|
||||
def patch(self, request):
|
||||
use_type = request.query_params.get("type")
|
||||
serv = certificate_use_type[use_type]
|
||||
if use_type not in CERTIFICATE_SERVICE_USE_TYPE:
|
||||
return Response({"message": "Not defined use_type"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
serv = CERTIFICATE_SERVICE_USE_TYPE[use_type]
|
||||
code = request.data.get('code', None)
|
||||
if not code:
|
||||
return Response({"message": "no code"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -54,3 +68,49 @@ class CertificationAPIView(APIView):
|
||||
return Response({"message": "certificated successfully"}, status=status.HTTP_200_OK)
|
||||
return Response({"message": "wrong code, please retry"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class InviteByLinkAPIView(APIView):
|
||||
|
||||
# 링크 초대(복사)
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
use_type = request.query_params.get("type")
|
||||
if use_type not in INVITE_USE_TYPE:
|
||||
return Response({"message": "Not defined use_type"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
serv = INVITE_USE_TYPE[use_type]['service']
|
||||
user = request.user
|
||||
serializer = InviteCodeSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
work = get_object_or_404(INVITE_USE_TYPE[use_type]['model'], id=serializer.validated_data['identifier'])
|
||||
if user != work.owner: # 유저 권한 추가될 시 수정 필요
|
||||
return Response({"message": "Not owner"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
create_code = generate_code(10)
|
||||
invite_url = serv.create_invite_object_and_url(use_type, serializer.validated_data['identifier'], create_code)
|
||||
return Response({"invite_url": invite_url})
|
||||
|
||||
# 링크 확인
|
||||
@transaction.atomic
|
||||
def patch(self, request):
|
||||
use_type = request.query_params.get("type")
|
||||
if use_type not in INVITE_USE_TYPE:
|
||||
return Response({"message": "Not defined use_type"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
serv = INVITE_USE_TYPE[use_type]['service']
|
||||
user = request.user
|
||||
serializer = InviteCodeSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
if not serv.check_code(use_type, serializer.validated_data['identifier'], serializer.validated_data['code']):
|
||||
return Response({"message": "Not correct or expired code"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
work = get_object_or_404(INVITE_USE_TYPE[use_type]['model'], id=serializer.validated_data['identifier'])
|
||||
filter_dict = {INVITE_USE_TYPE[use_type]['word']:work}
|
||||
|
||||
if INVITE_USE_TYPE[use_type]['team_model'].objects.filter(user=user, **filter_dict).exists():
|
||||
return Response({"message": "already invited member"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if serv.add_member(user, work):
|
||||
return Response({"message": "invite success"}, status=status.HTTP_200_OK)
|
||||
return Response({"message": "invite failed"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -9,8 +9,8 @@ class CertificateCodeUseType(models.TextChoices):
|
||||
PHONE = 'phone', 'phone'
|
||||
|
||||
class InviteCodeUseType(models.TextChoices):
|
||||
PROJECT = '프로젝트', '프로젝트'
|
||||
HACKATHON = '해커톤', '해커톤'
|
||||
PROJECT = 'p', 'p'
|
||||
HACKATHON = 'h', 'h'
|
||||
|
||||
class NotificationType(models.TextChoices):
|
||||
INVITE = '초대', '초대'
|
||||
|
||||
@@ -29,6 +29,9 @@ environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
|
||||
|
||||
SECRET_KEY = env('SECRET_KEY')
|
||||
|
||||
DOMAIN_NAME = 'colio.co.kr'
|
||||
DEV_DOMAIN_NAME = env('DEV_DOMAIN_NAME')
|
||||
|
||||
SOLAPI_API_KEY = env('SOLAPI_API_KEY')
|
||||
SOLAPI_API_SECRET = env('SOLAPI_API_SECRET')
|
||||
FROM_PHONE_NUMBER = env('FROM_PHONE_NUMBER')
|
||||
|
||||
Reference in New Issue
Block a user