diff --git a/apiserver/plane/api/serializers/__init__.py b/apiserver/plane/api/serializers/__init__.py index ba494ec9e..8d43d90ff 100644 --- a/apiserver/plane/api/serializers/__init__.py +++ b/apiserver/plane/api/serializers/__init__.py @@ -38,4 +38,6 @@ from .issue import ( IssueStateSerializer, ) -from .module import ModuleWriteSerializer, ModuleSerializer, ModuleIssueSerializer \ No newline at end of file +from .module import ModuleWriteSerializer, ModuleSerializer, ModuleIssueSerializer + +from .api_token import APITokenSerializer \ No newline at end of file diff --git a/apiserver/plane/api/serializers/api_token.py b/apiserver/plane/api/serializers/api_token.py new file mode 100644 index 000000000..247b3f0e7 --- /dev/null +++ b/apiserver/plane/api/serializers/api_token.py @@ -0,0 +1,8 @@ +from .base import BaseSerializer +from plane.db.models import APIToken + + +class APITokenSerializer(BaseSerializer): + class Meta: + model = APIToken + fields = "__all__" diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 38d2b4013..98c2e87d2 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -84,6 +84,9 @@ from plane.api.views import ( ModuleViewSet, ModuleIssueViewSet, ## End Modules + # Api Tokens + ApiTokenEndpoint, + ## End Api Tokens ) @@ -679,4 +682,8 @@ urlpatterns = [ name="project-module-issues", ), ## End Modules + # API Tokens + path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-token"), + path("api-tokens//", ApiTokenEndpoint.as_view(), name="api-token"), + ## End API Tokens ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 933315277..1212e0dca 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -72,3 +72,5 @@ from .authentication import ( ) from .module import ModuleViewSet, ModuleIssueViewSet + +from .api_token import ApiTokenEndpoint \ No newline at end of file diff --git a/apiserver/plane/api/views/api_token.py b/apiserver/plane/api/views/api_token.py new file mode 100644 index 000000000..4ed3d9de0 --- /dev/null +++ b/apiserver/plane/api/views/api_token.py @@ -0,0 +1,62 @@ +# Python import +from uuid import uuid4 + +# Third party +from rest_framework.response import Response +from rest_framework import status +from sentry_sdk import capture_exception + +# Module import +from .base import BaseAPIView +from plane.db.models import APIToken +from plane.api.serializers import APITokenSerializer + + +class ApiTokenEndpoint(BaseAPIView): + def post(self, request): + try: + + label = request.data.get("label", str(uuid4().hex)) + + api_token = APIToken.objects.create( + label=label, + user=request.user, + ) + + serializer = APITokenSerializer(api_token) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + def get(self, request): + try: + api_tokens = APIToken.objects.filter(user=request.user) + serializer = APITokenSerializer(api_tokens, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + def delete(self, request, pk): + try: + api_token = APIToken.objects.get(pk=pk) + api_token.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + except APIToken.DoesNotExist: + return Response( + {"error": "Token does not exists"}, status=status.HTTP_400_BAD_REQUEST + ) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index b6a19b67a..ef7ad5b8d 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -38,3 +38,5 @@ from .shortcut import Shortcut from .view import View from .module import Module, ModuleMember, ModuleIssue, ModuleLink + +from .api_token import APIToken \ No newline at end of file diff --git a/apiserver/plane/db/models/api_token.py b/apiserver/plane/db/models/api_token.py new file mode 100644 index 000000000..32ba013bc --- /dev/null +++ b/apiserver/plane/db/models/api_token.py @@ -0,0 +1,39 @@ +# Python imports +from uuid import uuid4 + +# Django imports +from django.db import models +from django.conf import settings + +from .base import BaseModel + + +def generate_label_token(): + return uuid4().hex + + +def generate_token(): + return uuid4().hex + uuid4().hex + + +class APIToken(BaseModel): + + token = models.CharField(max_length=255, unique=True, default=generate_token) + label = models.CharField(max_length=255, default=generate_label_token) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="bot_tokens", + ) + user_type = models.PositiveSmallIntegerField( + choices=((0, "Human"), (1, "Bot")), default=0 + ) + + class Meta: + verbose_name = "API Token" + verbose_name_plural = "API Tokems" + db_table = "api_tokens" + ordering = ("-created_at",) + + def __str__(self): + return str(self.user.name) diff --git a/apiserver/plane/db/models/user.py b/apiserver/plane/db/models/user.py index 1621b19ea..896681808 100644 --- a/apiserver/plane/db/models/user.py +++ b/apiserver/plane/db/models/user.py @@ -68,6 +68,7 @@ class User(AbstractBaseUser, PermissionsMixin): last_workspace_id = models.UUIDField(null=True) my_issues_prop = models.JSONField(null=True) role = models.CharField(max_length=300, null=True, blank=True) + is_bot = models.BooleanField(default=False) USERNAME_FIELD = "email" @@ -101,7 +102,7 @@ class User(AbstractBaseUser, PermissionsMixin): @receiver(post_save, sender=User) def send_welcome_email(sender, instance, created, **kwargs): try: - if created: + if created and not instance.is_bot: first_name = instance.first_name.capitalize() to_email = instance.email from_email_string = f"Team Plane "