diff --git a/Dockerfile b/Dockerfile index 5d47907..6d9dcc3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,35 +13,34 @@ ENV PYTHONUNBUFFERED 1 # 로컬 파일 시스템의 requirements.txt 파일을 컨테이너의 /tmp/requirements.txt로 복사합니다. # 이 파일은 필요한 Python 패키지들을 명시합니다. COPY ./requirements.txt /tmp/requirements.txt -COPY ./requirements.dev.txt /tmp/requirements.dev.txt COPY ./potato_project /app WORKDIR /app EXPOSE 8000 ARG DEV=false -RUN python -m venv /py && \ - /py/bin/pip install --upgrade pip && \ - /py/bin/pip install -r /tmp/requirements.txt && \ - apk add --update --no-cache postgresql-client jpeg-dev && \ - apk add --update --no-cache --virtual .tmp-build-deps \ - build-base postgresql-dev musl-dev zlib zlib-dev linux-headers && \ - if [ $DEV = "true" ]; \ - then /py/bin/pip install -r /tmp/requirements.dev.txt ; \ - fi && \ - rm -rf /tmp && \ - apk del .tmp-build-deps && \ - adduser \ - --disabled-password \ - --no-create-home \ - django-user +# 가상 환경 설정 및 패키지 설치 +RUN python -m venv /py +RUN /py/bin/pip install --upgrade pip +RUN /py/bin/pip install --no-cache-dir -r /tmp/requirements.txt + +# 시스템 패키지 설치 +RUN apk add --update --no-cache jpeg-dev +RUN apk add --update --no-cache --virtual .tmp-build-deps \ + build-base musl-dev zlib zlib-dev linux-headers \ + && apk del .tmp-build-deps + +# django-user 생성 및 권한 설정 +RUN if ! getent passwd django-user; then adduser -D django-user; fi +USER root +RUN chown -R django-user:django-user /py/lib/python3.11/site-packages +USER django-user +# 추가 패키지 설치 ENV PATH="/py/bin:$PATH" +RUN /py/bin/pip install --no-cache-dir pytest pytest-django django-cors-headers -USER django-user -# 이 명령어를 추가하여 pytest를 설치합니다. -RUN /py/bin/pip install pytest pytest-django # 개발용 @@ -88,4 +87,5 @@ RUN /py/bin/pip install pytest pytest-django # USER django-user # # 이 명령어를 추가하여 pytest를 설치합니다. -# RUN /py/bin/pip install pytest pytest-django \ No newline at end of file +# RUN /py/bin/pip install pytest pytest-django + diff --git a/docker-compose.yml b/docker-compose.yml index 16a6afb..380cbf8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,14 +18,13 @@ services: python manage.py migrate && python manage.py runserver --noreload 0.0.0.0:8000" environment: - - DB_HOST=${DB_HOST} - - DB_NAME=${DB_NAME} - - DB_USER=${DB_USER} - - DB_PASSWORD=${DB_PASSWORD} + - DB_HOST=${RDS_HOSTNAME} + - DB_NAME=${RDS_DB_NAME} + - DB_USER=${RDS_USERNAME} + - DB_PASSWORD=${RDS_PASSWORD} - PYDEVD_DISABLE_FILE_VALIDATION=1 env_file: - - .env - + - .env # 개발용 @@ -56,7 +55,7 @@ services: # - .env # depends_on: # - db - + # db: # PostgreSQL Database # image: postgres:16-alpine @@ -68,16 +67,3 @@ services: # - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # env_file: # - .env - - - - db: # PostgreSQL Database - image: postgres:16-alpine - volumes: - - ./data/db:/var/lib/postgresql/data - environment: - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - env_file: - - .env \ No newline at end of file diff --git a/potato_project/app/settings.py b/potato_project/app/settings.py index f0f2833..a0abbe7 100644 --- a/potato_project/app/settings.py +++ b/potato_project/app/settings.py @@ -53,8 +53,10 @@ "allauth.account", "allauth.socialaccount", "allauth.socialaccount.providers.github", + "corsheaders", ] + INSTALLED_APPS = DJANGO_SYSTEM_APPS + CUSTOM_USER_APPS # Custom user model @@ -71,6 +73,7 @@ # 미들웨어 설정 MIDDLEWARE = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -115,6 +118,9 @@ "USER": os.environ.get("RDS_USERNAME"), "PASSWORD": os.environ.get("RDS_PASSWORD"), "PORT": os.environ.get("RDS_PORT", 5432), + "OPTIONS": { + "client_encoding": "UTF8", # UTF-8 문자셋 설정 + }, } } @@ -205,6 +211,51 @@ } } -SOCIALACCOUNT_LOGIN_ON_GET = True -LOGIN_REDIRECT_URL = "/oauth-callback/" -ACCOUNT_LOGOUT_REDIRECT_URL = "/landing/" +SOCIALACCOUNT_LOGIN_ON_GET = False +# LOGIN_REDIRECT_URL = "/oauth-callback/" +# ACCOUNT_LOGOUT_REDIRECT_URL = "/landing/" + +DEFAULT_CHARSET = "utf-8" + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + }, + "root": { + "handlers": ["console"], + "level": "DEBUG", + }, +} + +# CORS_ORIGIN_WHITELIST = ['http://localhost:5173', 'http://127.0.0.1:5173', 'https://www.gitpotatoes.com',] # 특정 Origin만 허용 +CORS_ALLOWED_ORIGINS = [ + "https://www.gitpotatoes.com", # 실제 배포 프론트엔드 URL + # 'http://localhost:5173', # 프론트엔드 로컬 서버 URL + # 'http://127.0.0.1:5173', # 프론트엔드 로컬 서버 URL +] +CORS_ALLOW_CREDENTIALS = True # 쿠키 등 credential 정보 허용 +CORS_ALLOW_METHODS = [ + "DELETE", + "GET", + "OPTIONS", + "PATCH", + "POST", + "PUT", +] +CORS_ALLOW_HEADERS = [ + "accept", + "accept-encoding", + "authorization", + "content-type", + "dnt", + "origin", + "user-agent", + "x-csrftoken", + "x-requested-with", +] +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + diff --git a/potato_project/attendances/views.py b/potato_project/attendances/views.py index 3014709..a85f113 100644 --- a/potato_project/attendances/views.py +++ b/potato_project/attendances/views.py @@ -40,7 +40,9 @@ def increment(self, request): # 출석날짜가 오늘이면 이미 출석함을 반환 attendance = self.get_user_attendance(user) if attendance and attendance.date == today: - return Response({"오늘은 출석을 이미 하셨어요!"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"오늘은 출석을 이미 하셨어요!"}, status=status.HTTP_400_BAD_REQUEST + ) # 새로운 출석 기록 생성 new_attendance = Attendance.objects.create( @@ -71,4 +73,6 @@ def decrement(self, request): user.save() # 성공 응답 반환 - return Response({"message": "물건을 구매했습니다.", "total_coins": user.total_coins}) + return Response( + {"message": "물건을 구매했습니다.", "total_coins": user.total_coins} + ) diff --git a/potato_project/githubs/apps.py b/potato_project/githubs/apps.py index b3180a3..e012887 100644 --- a/potato_project/githubs/apps.py +++ b/potato_project/githubs/apps.py @@ -4,3 +4,6 @@ class GithubsConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "githubs" + + def ready(self): + import githubs.signals diff --git a/potato_project/githubs/models.py b/potato_project/githubs/models.py index ba83439..894e14e 100644 --- a/potato_project/githubs/models.py +++ b/potato_project/githubs/models.py @@ -8,7 +8,7 @@ class Github(TimeStampedModel): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="User_id") commit_num = models.BigIntegerField(verbose_name="Commit Number") - date = models.DateField(default=timezone.now) + date = models.DateField() def __str__(self): return f"{self.date}-{self.commit_num}" diff --git a/potato_project/githubs/signals.py b/potato_project/githubs/signals.py new file mode 100644 index 0000000..63c04a7 --- /dev/null +++ b/potato_project/githubs/signals.py @@ -0,0 +1,150 @@ +from datetime import date, timedelta + +from django.db.models.signals import post_save +from django.dispatch import receiver +from githubs.models import Github +from potatoes.models import Potato + + +@receiver(post_save, sender=Github) +def get_winter_potato(sender, instance, **kwargs): + # 새 Github 데이터가 생성 or 업데이트, 날짜가 크리스마스이며, commit_num이 > + if ( + instance.commit_num >= 1 + and instance.date.month == 12 + and instance.date.day == 25 + ): # 월과 일만 비교 + try: + # potato_type_id=6인 감자 조회 + potato = Potato.objects.get(user=instance.user, potato_type_id=6) + if not potato.is_acquired: # 이미 획득한 경우에는 변경하지 않음 + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + # 해당 감자가 없는 경우 에러 처리 (필요에 따라 추가) + pass + + +@receiver(post_save, sender=Github) +def get_ghost_potato(sender, instance, **kwargs): + if ( + instance.commit_num >= 1 + and instance.date.month == 10 + and instance.date.day == 31 + ): + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=7) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=Github) +def get_crystal_potato(sender, instance, **kwargs): + if instance.commit_num >= 1: + # 30일 전 날짜 계산 + thirty_days_ago = instance.date - timedelta(days=30) + + # 30일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() + ) + if oldest_record and (instance.date - oldest_record.date).days >= 30: + # 30일 연속 커밋 여부 확인 + commits_in_30_days = ( + Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ) + .values("date") + .distinct() + .count() + ) + + if commits_in_30_days == 30: + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=8) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + else: + # 30일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) + + +@receiver(post_save, sender=Github) +def get_dirty_potato(sender, instance, **kwargs): + if instance.commit_num == 0: + # 30일 전 날짜 계산 + thirty_days_ago = instance.date - timedelta(days=30) + + # 30일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() + ) + if oldest_record and (instance.date - oldest_record.date).days >= 30: + # 30일 동안 커밋이 있었는지 확인 + any_commits_in_30_days = Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ).exists() + + if not any_commits_in_30_days: + # 30일 연속 커밋이 없는 경우 감자 아이디 9 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=9) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 + else: + # 30일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) + + +@receiver(post_save, sender=Github) +def get_green_potato(sender, instance, **kwargs): + if instance.commit_num == 0: + # 90일 전 날짜 계산 + ninety_days_ago = instance.date - timedelta(days=90) + + # 90일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() + ) + if oldest_record and (instance.date - oldest_record.date).days >= 90: + # 90일 동안 커밋이 있었는지 확인 + any_commits_in_90_days = Github.objects.filter( + user=instance.user, + date__gte=ninety_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ).exists() + + if not any_commits_in_90_days: + # 90일 연속 커밋이 없는 경우 감자 아이디 10 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=10) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 + else: + # 90일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) diff --git a/potato_project/githubs/views.py b/potato_project/githubs/views.py index bd3d652..d471aef 100644 --- a/potato_project/githubs/views.py +++ b/potato_project/githubs/views.py @@ -1,11 +1,11 @@ from datetime import datetime, timedelta -# githubs api를 불러오는 함수 -# 깃허브 API 호출 서비스 +import pytz import requests from django.db.models import Avg, Sum from django.http import JsonResponse from django.utils import timezone +from rest_framework import status from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView @@ -13,22 +13,25 @@ from .models import Github as GithubModel -# 깃허브 API 호출 서비스 class GitHubAPIService: def __init__(self, access_token): self.access_token = access_token - def get_commits(self, repo): + def get_commits(self, repo, author): github_api_url = f"https://api.github.com/repos/{repo}/commits" + params = { + "author": author, # author 파라미터 설정 + "per_page": 100, + } headers = { "Authorization": f"token {self.access_token}", "Accept": "application/vnd.github.v3+json", } - response = requests.get(github_api_url, headers=headers) + response = requests.get(github_api_url, headers=headers, params=params) if response.status_code == 200: - return response.json(), 200## + return response.json(), 200 else: return None, response.status_code @@ -42,26 +45,11 @@ def get_repos(self, username): response = requests.get(github_api_url, headers=headers) if response.status_code == 200: - return response.json(), 200## + return response.json(), 200 else: return None, response.status_code - def get_total_commits(self, username): - github_api_url = f"https://api.github.com/search/commits?q=author:{username}" - headers = { - "Authorization": f"token {self.access_token}", - "Accept": "application/vnd.github.v3+json", - } - - response = requests.get(github_api_url, headers=headers) - - if response.status_code == 200: - data = response.json() - return data["total_count"], 200 - else: - return None, response.status_code - - + # 데이터베이스에 커밋과 날짜를 저장하는 함수 class GitHubDatabaseService: @staticmethod @@ -83,9 +71,12 @@ def get(self, request): return Response({"error": "깃허브 액세스 토큰이 없습니다."}, status=401) github_service = GitHubAPIService(user.github_access_token) + db_service = GitHubDatabaseService() # 오늘 날짜 - today = timezone.now().date() + today = timezone.localtime(timezone.now()).date() + + print("today: ", today) # 7일 전 날짜 week_ago = today - timedelta(days=7) @@ -94,60 +85,75 @@ def get(self, request): if repos is not None: # 오늘, 7일간 커밋 수 계산 - today_commit_count = 0 - week_commit_count = 0 + today_commits = 0 for repo in repos: - commits, _ = github_service.get_commits(repo["full_name"])## + commits, _ = github_service.get_commits( + repo["full_name"], user.username + ) if commits is not None: for commit in commits: - commit_date = datetime.strptime( - commit["commit"]["author"]["date"], "%Y-%m-%dT%H:%M:%SZ" - ).date() + commit_date = ( + timezone.make_aware( + datetime.strptime( + commit["commit"]["author"]["date"], + "%Y-%m-%dT%H:%M:%SZ", + ), + timezone=pytz.UTC, + ) + .astimezone(timezone.get_current_timezone()) + .date() + ) if commit_date == today: - today_commit_count += 1 - if commit_date >= week_ago: - week_commit_count += 1 - - # 7일 평균 커밋 수 계산 - week_average_commit_count = round(week_commit_count / 7, 2) - - # 총 커밋 수 가져오기 (get_total_commits 사용)## - total_commit_count, _ = github_service.get_total_commits(user.username) + today_commits += 1 # 오늘 커밋 수 데이터베이스에 저장 db_service = GitHubDatabaseService() - db_service.update_or_create_commit_record(user, today_commit_count, today) + db_service.update_or_create_commit_record(user, today_commits, today) - # 경험치 및 레벨 업데이트 - user.exp = ( + # 최근 7일간 커밋 수 및 전체 커밋 수 계산 + week_commits = ( + GithubModel.objects.filter(user=user, date__gte=week_ago).aggregate( + Sum("commit_num") + )["commit_num__sum"] + or 0 + ) + total_commits = ( GithubModel.objects.filter(user=user).aggregate(Sum("commit_num"))[ "commit_num__sum" ] or 0 ) - level_up_threshold = 50 * 1.5**user.potato_level - while user.exp >= level_up_threshold: + + # 7일 평균 커밋 수 계산 + week_average_commit_count = round(week_commits / 7, 2) + + # 경험치 및 레벨 업데이트 + user.potato_exp = total_commits + user.potato_level = 1 + level_up_threshold = int(50 * 1.5 ** (user.potato_level - 1)) + while user.potato_exp >= level_up_threshold: user.potato_level += 1 - user.exp -= level_up_threshold - level_up_threshold = 50 * 1.5**user.potato_level + user.potato_exp -= level_up_threshold + level_up_threshold = int(50 * 1.5 ** (user.potato_level - 1)) user.save() # 다음 레벨까지 필요한 경험치 계산 - next_level_exp = 50 * 1.5**user.potato_level - user.exp + next_level_exp = int(50 * 1.5 ** (user.potato_level - 1)) - user.potato_exp # 응답 데이터 생성 commit_statistics = { - "today_commit_count": today_commit_count, - "week_commit_count": week_commit_count, - "total_commit_count": total_commit_count, + "today_commit_count": today_commits, + "week_commit_count": week_commits, + "total_commit_count": total_commits, "week_average_commit_count": week_average_commit_count, "level": user.potato_level, - "exp": user.exp, - "next_level_exp": next_level_exp, + "exp": user.potato_exp, + "next_level_exp": int( + next_level_exp + ), # 다음 레벨까지 필요한 경험치 추가 } - return JsonResponse(commit_statistics, safe=False) else: return Response({"error": "커밋 요청을 실패했습니다."}, status=status_code) diff --git a/potato_project/potato_types/views.py b/potato_project/potato_types/views.py index 7836229..0cb5a08 100644 --- a/potato_project/potato_types/views.py +++ b/potato_project/potato_types/views.py @@ -2,6 +2,7 @@ from django.db import DatabaseError from rest_framework import status from rest_framework.exceptions import ValidationError +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView @@ -11,6 +12,8 @@ # 전체 감자 목록 조회 class PotatoesList(APIView): + permission_classes = [AllowAny] + def get(self, request): try: potatoes = PotatoType.objects.all() diff --git a/potato_project/potatoes/views.py b/potato_project/potatoes/views.py index e60fca8..96d0078 100644 --- a/potato_project/potatoes/views.py +++ b/potato_project/potatoes/views.py @@ -56,8 +56,8 @@ class PotatoSelectPatch(APIView): def patch(self, request): try: - potato_id = request.data.get("id") - potato = Potato.objects.get(id=potato_id, user=request.user) + potato_type_id = request.data.get("id") + potato = Potato.objects.get(potato_type=potato_type_id, user=request.user) if not potato: return Response( diff --git a/potato_project/stacks/views.py b/potato_project/stacks/views.py index 2295b5b..0f8d617 100644 --- a/potato_project/stacks/views.py +++ b/potato_project/stacks/views.py @@ -2,6 +2,7 @@ from django.db import DatabaseError from rest_framework import status from rest_framework.exceptions import ValidationError +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView @@ -11,6 +12,8 @@ # 전체 스택 조회 class StackList(APIView): + permission_classes = [AllowAny] + def get(self, request): try: stacks = Stack.objects.all() diff --git a/potato_project/todos/models.py b/potato_project/todos/models.py index d2d4326..e4ba831 100644 --- a/potato_project/todos/models.py +++ b/potato_project/todos/models.py @@ -4,7 +4,6 @@ class Todo(TimeStampedModel): - # field이름은 _id를 붙이지 않는게 좋다고하네? user = models.ForeignKey(User, on_delete=models.CASCADE) task = models.CharField(max_length=50) is_done = models.BooleanField(default=False) diff --git a/potato_project/todos/views.py b/potato_project/todos/views.py index 1048ae8..ea0454d 100644 --- a/potato_project/todos/views.py +++ b/potato_project/todos/views.py @@ -4,6 +4,7 @@ from django.shortcuts import get_object_or_404 from django.utils import timezone from rest_framework import generics, permissions, status +from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @@ -20,14 +21,14 @@ def perform_create(self, serializer): date_str = self.request.data.get("date") # 프론트엔드에서 전달된 날짜 문자열 try: # datetime 객체 생성 - date_obj = datetime.strptime(date_str, "%Y-%m-%d").date() + date = datetime.strptime(date_str, "%Y-%m-%d").date() except (ValueError, TypeError): return Response( {"error": "Invalid date format or missing date."}, status=status.HTTP_400_BAD_REQUEST, ) - serializer.save(user=self.request.user, date=date_obj) + serializer.save(user=self.request.user, date=date) # 2. 투두리스트 항목 수정 (UI에서 입력 받은 데이터 + 선택된 날짜로 수정) @@ -46,14 +47,10 @@ def perform_update(self, serializer): date_str = self.request.data.get("date") try: # datetime 객체 생성 - date_obj = datetime.strptime(date_str, "%Y-%m-%d").date() + date = datetime.strptime(date_str, "%Y-%m-%d").date() + serializer.save(date=date) # serializer에 날짜 저장 except (ValueError, TypeError): - return Response( - {"error": "Invalid date format or missing date."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - serializer.save(date=date_obj) + raise ValidationError({"error": "Invalid date format or missing date."}) # 3. 투두리스트 항목 삭제 @@ -93,6 +90,7 @@ class TodoMarkUndoneView(generics.UpdateAPIView): def get_object(self): todo_id = self.kwargs.get("id") + return get_object_or_404(Todo, id=todo_id, user=self.request.user) def get_queryset(self): diff --git a/potato_project/users/apps.py b/potato_project/users/apps.py index 88f7b17..4697735 100644 --- a/potato_project/users/apps.py +++ b/potato_project/users/apps.py @@ -4,3 +4,6 @@ class UsersConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "users" + + def ready(self): + import users.signals diff --git a/potato_project/users/models.py b/potato_project/users/models.py index dccfea6..40c6596 100644 --- a/potato_project/users/models.py +++ b/potato_project/users/models.py @@ -56,7 +56,7 @@ class User(AbstractBaseUser, PermissionsMixin, TimeStampedModel): nickname = models.CharField(max_length=255, null=False) # 감자 관련 필드 - potato_level = models.PositiveIntegerField(null=False, default=0) + potato_level = models.PositiveIntegerField(null=False, default=1) potato_exp = models.PositiveIntegerField(null=False, default=0) total_coins = models.PositiveIntegerField(default=0) diff --git a/potato_project/users/signals.py b/potato_project/users/signals.py new file mode 100644 index 0000000..d4fc90c --- /dev/null +++ b/potato_project/users/signals.py @@ -0,0 +1,97 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from potatoes.models import Potato +from users.models import User + + +@receiver(post_save, sender=User) +def get_level_two_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 2로 변경되었고, 이전 레벨이 2가 아닐 때만 실행 + if ( + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 2 + ): + try: + # potato_type_id=2인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=2) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + # 해당 감자가 없는 경우 에러 처리 (필요에 따라 추가) + pass + + +@receiver(post_save, sender=User) +def get_level_three_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 3로 변경되었고, 이전 레벨이 3가 아닐 때만 실행 + if ( + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 3 + ): + try: + # potato_type_id=3인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=3) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=User) +def get_level_four_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 4로 변경되었고, 이전 레벨이 4가 아닐 때만 실행 + if ( + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 4 + ): + try: + # potato_type_id=4인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=4) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=User) +def get_level_five_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 5로 변경되었고, 이전 레벨이 5가 아닐 때만 실행 + if ( + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 5 + ): + try: + # potato_type_id=5인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=5) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass diff --git a/potato_project/users/urls.py b/potato_project/users/urls.py index eaea092..d4ce7d3 100644 --- a/potato_project/users/urls.py +++ b/potato_project/users/urls.py @@ -14,7 +14,7 @@ views.CustomTokenRefreshView.as_view(), name="token_refresh", ), - path("api/accounts/logout/", views.logout_view, name="logout"), + path("api/logout/", views.logout_view, name="logout"), path("api/accounts/profile/", views.UserDetail.as_view(), name="user_detail"), path( "api/accounts/baekjoon_id/", diff --git a/potato_project/users/views.py b/potato_project/users/views.py index d52037f..a148123 100644 --- a/potato_project/users/views.py +++ b/potato_project/users/views.py @@ -29,10 +29,9 @@ load_dotenv() # .env 파일 로드 - state = os.environ.get("STATE") -# BASE_URL = "http://43.201.150.178:8000/" -BASE_URL = "http://localhost:8000/" # 프론트엔드 URL로 변경해야 함 +BASE_URL = "https://api.gitpotatoes.com/" +# BASE_URL = "http://localhost:8000" GITHUB_CALLBACK_URI = BASE_URL + "accounts/github/callback/" @@ -97,6 +96,7 @@ def github_callback(request): "https://api.github.com/user", headers={"Authorization": f"Bearer {access_token}"}, ) + user_response.encoding = "utf-8" user_json = user_response.json() # 에러 처리 @@ -180,7 +180,20 @@ def github_callback(request): secure=True, samesite="Lax", ) - return response + + jwt_access_token = login_data.get("access_token") + jwt_refresh_token = login_data.get("refresh_token") + user_data = ( + { + "pk": user.pk, + "username": user.username, + "profile_url": user.profile_url, + "nickname": user.nickname, + }, + ) + redirect_url = f"https://www.gitpotatoes.com/oauth-callback?access_token={jwt_access_token}&refresh_token={jwt_refresh_token}&user={json.dumps(user_data)}" + return redirect(redirect_url) + # return response class GithubLogin(SocialLoginView): @@ -210,7 +223,6 @@ def get_response(self): "nickname": user.nickname, }, } - # return redirect(settings.LOGIN_REDIRECT_URL) return JsonResponse(response_data) return response diff --git a/readme.md b/readme.md index 9d3c494..b33d976 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,284 @@ + +
+ 이 프로젝트는 사용자의 GitHub 및 Baekjoon 활동을 기반으로 감자 캐릭터를 성장시키는 게임형 웹 애플리케이션입니다.
사용자는 자신의 활동을 통해 감자 캐릭터를 키우고, 게임의 재미를 통해 동기 부여를 받으며 일상적인 업무와 목표를 관리할 수 있습니다.