From 65ab8ebd38402f398f0204ffaf40a703688f3254 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 3 Oct 2023 17:56:26 +0530 Subject: [PATCH 1/4] dev: configuration endpoint for frontend clients --- apiserver/plane/api/urls.py | 10 +++++++++ apiserver/plane/api/views/__init__.py | 4 +++- apiserver/plane/api/views/config.py | 31 +++++++++++++++++++++++++++ apiserver/plane/api/views/unsplash.py | 1 + 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 apiserver/plane/api/views/config.py create mode 100644 apiserver/plane/api/views/unsplash.py diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index c10c4a74562..24c227f0436 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -186,6 +186,9 @@ ## Exporter ExportIssuesEndpoint, ## End Exporter + # Configuration + ConfigurationEndpoint, + ## End Configuration ) @@ -1728,4 +1731,11 @@ name="workspace-project-boards", ), ## End Public Boards + # Configuration + path( + "configs/", + ConfigurationEndpoint.as_view(), + name="configuration", + ), + ## End Configuration ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index c03d6d5b7f9..5906aa72511 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -169,4 +169,6 @@ from .notification import NotificationViewSet, UnreadNotificationEndpoint, MarkAllReadNotificationViewSet -from .exporter import ExportIssuesEndpoint \ No newline at end of file +from .exporter import ExportIssuesEndpoint + +from .config import ConfigurationEndpoint \ No newline at end of file diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py new file mode 100644 index 00000000000..3cd53fe002d --- /dev/null +++ b/apiserver/plane/api/views/config.py @@ -0,0 +1,31 @@ +# Python imports +import os + +# Third party imports +from rest_framework.permissions import AllowAny +from rest_framework import status +from rest_framework.response import Response +from sentry_sdk import capture_exception + +# Module imports +from .base import BaseAPIView + + +class ConfigurationEndpoint(BaseAPIView): + permission_classes = [ + AllowAny, + ] + + def get(self, request): + try: + data = {} + data["google"] = os.environ.get("GOOGLE_CLIENT_ID", None) + data["github"] = os.environ.get("GITHUB_CLIENT_ID", None) + data["github_app_name"] = os.environ.get("GITHUB_APP_NAME", None) + return Response(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, + ) diff --git a/apiserver/plane/api/views/unsplash.py b/apiserver/plane/api/views/unsplash.py new file mode 100644 index 00000000000..f262dca1e30 --- /dev/null +++ b/apiserver/plane/api/views/unsplash.py @@ -0,0 +1 @@ +# Third party imports From ff5f99b5bdfd0349c57af9459c22f2a16966af75 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 3 Oct 2023 18:25:38 +0530 Subject: [PATCH 2/4] dev: configuration enable magic and email/password signup --- apiserver/.env.example | 3 +++ apiserver/plane/api/views/config.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/apiserver/.env.example b/apiserver/.env.example index 4969f176656..edf31f86bd1 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -59,3 +59,6 @@ DEFAULT_PASSWORD="password123" # SignUps ENABLE_SIGNUP="1" + +# Enable Email/Password Signup +ENABLE_EMAIL_PASSWORD="1" \ No newline at end of file diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py index 3cd53fe002d..848934ee381 100644 --- a/apiserver/plane/api/views/config.py +++ b/apiserver/plane/api/views/config.py @@ -1,6 +1,9 @@ # Python imports import os +# Django imports +from django.conf import settings + # Third party imports from rest_framework.permissions import AllowAny from rest_framework import status @@ -22,6 +25,8 @@ def get(self, request): data["google"] = os.environ.get("GOOGLE_CLIENT_ID", None) data["github"] = os.environ.get("GITHUB_CLIENT_ID", None) data["github_app_name"] = os.environ.get("GITHUB_APP_NAME", None) + data["magic_login"] = bool(settings.EMAIL_HOST_USER) and bool(settings.EMAIL_HOST_PASSWORD) + data["email_password_login"] = os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1" return Response(data, status=status.HTTP_200_OK) except Exception as e: capture_exception(e) From cb22f33215a1a120f3ff15ef5212cccd327fead1 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 3 Oct 2023 18:53:33 +0530 Subject: [PATCH 3/4] dev: update unsplash keys --- apiserver/plane/api/views/unsplash.py | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/apiserver/plane/api/views/unsplash.py b/apiserver/plane/api/views/unsplash.py index f262dca1e30..0e00a9ab404 100644 --- a/apiserver/plane/api/views/unsplash.py +++ b/apiserver/plane/api/views/unsplash.py @@ -1 +1,37 @@ +# Python imports +import requests + +# Django imports +from django.conf import settings + # Third party imports +from rest_framework import status +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from sentry_sdk import capture_exception + +# Module imports +from .base import BaseAPIView + + +class UnsplashEndpoint(BaseAPIView): + permission_classes = [ + AllowAny, + ] + + def get(self, request): + try: + query = request.GET.get("query", False) + page = request.GET.get("page", 1) + per_page = request.GET.get("per_page", 20) + url = ( + f"https://api.unsplash.com/search/photos/?client_id=${settings.UNSPLASH_ACCESS_KEY}&query=${query}&page=${page}&per_page=${per_page}" + if query + else f"https://api.unsplash.com/photos/?client_id=${unsplashKey}&page=${page}&per_page=${per_page}" + ) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) From 340251be7c7fbf1317fe61884bd19590dcae952f Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 4 Oct 2023 12:31:05 +0530 Subject: [PATCH 4/4] dev: add unsplash API and add env for magic login --- apiserver/.env.example | 5 ++- apiserver/plane/api/urls.py | 18 ++++---- apiserver/plane/api/views/__init__.py | 5 +-- apiserver/plane/api/views/config.py | 8 +++- .../plane/api/views/{gpt.py => external.py} | 45 ++++++++++++++++++- apiserver/plane/api/views/release.py | 21 --------- apiserver/plane/api/views/unsplash.py | 37 --------------- apiserver/plane/settings/local.py | 3 ++ apiserver/plane/settings/production.py | 2 + apiserver/plane/settings/selfhosted.py | 1 + apiserver/plane/settings/staging.py | 4 ++ 11 files changed, 75 insertions(+), 74 deletions(-) rename apiserver/plane/api/views/{gpt.py => external.py} (62%) delete mode 100644 apiserver/plane/api/views/release.py delete mode 100644 apiserver/plane/api/views/unsplash.py diff --git a/apiserver/.env.example b/apiserver/.env.example index edf31f86bd1..93b4fe0cb77 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -61,4 +61,7 @@ DEFAULT_PASSWORD="password123" ENABLE_SIGNUP="1" # Enable Email/Password Signup -ENABLE_EMAIL_PASSWORD="1" \ No newline at end of file +ENABLE_EMAIL_PASSWORD="1" + +# Enable Magic link Login +ENABLE_MAGIC_LINK_LOGIN="0" \ No newline at end of file diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 24c227f0436..7d6b8e83d5d 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -150,12 +150,11 @@ GlobalSearchEndpoint, IssueSearchEndpoint, ## End Search - # Gpt + # External GPTIntegrationEndpoint, - ## End Gpt - # Release Notes ReleaseNotesEndpoint, - ## End Release Notes + UnsplashEndpoint, + ## End External # Inbox InboxViewSet, InboxIssueViewSet, @@ -1449,20 +1448,23 @@ name="project-issue-search", ), ## End Search - # Gpt + # External path( "workspaces//projects//ai-assistant/", GPTIntegrationEndpoint.as_view(), name="importer", ), - ## End Gpt - # Release Notes path( "release-notes/", ReleaseNotesEndpoint.as_view(), name="release-notes", ), - ## End Release Notes + path( + "unsplash/", + UnsplashEndpoint.as_view(), + name="release-notes", + ), + ## End External # Inbox path( "workspaces//projects//inboxes/", diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 5906aa72511..35863a7c058 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -147,16 +147,13 @@ from .search import GlobalSearchEndpoint, IssueSearchEndpoint -from .gpt import GPTIntegrationEndpoint +from .external import GPTIntegrationEndpoint, ReleaseNotesEndpoint, UnsplashEndpoint from .estimate import ( ProjectEstimatePointEndpoint, BulkEstimatePointEndpoint, ) - -from .release import ReleaseNotesEndpoint - from .inbox import InboxViewSet, InboxIssueViewSet, InboxIssuePublicViewSet from .analytic import ( diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py index 848934ee381..ea1b39d9ce8 100644 --- a/apiserver/plane/api/views/config.py +++ b/apiserver/plane/api/views/config.py @@ -25,8 +25,12 @@ def get(self, request): data["google"] = os.environ.get("GOOGLE_CLIENT_ID", None) data["github"] = os.environ.get("GITHUB_CLIENT_ID", None) data["github_app_name"] = os.environ.get("GITHUB_APP_NAME", None) - data["magic_login"] = bool(settings.EMAIL_HOST_USER) and bool(settings.EMAIL_HOST_PASSWORD) - data["email_password_login"] = os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1" + data["magic_login"] = ( + bool(settings.EMAIL_HOST_USER) and bool(settings.EMAIL_HOST_PASSWORD) + ) and os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "0") == "1" + data["email_password_login"] = ( + os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1" + ) return Response(data, status=status.HTTP_200_OK) except Exception as e: capture_exception(e) diff --git a/apiserver/plane/api/views/gpt.py b/apiserver/plane/api/views/external.py similarity index 62% rename from apiserver/plane/api/views/gpt.py rename to apiserver/plane/api/views/external.py index 63c3f4f18f1..00a0270e498 100644 --- a/apiserver/plane/api/views/gpt.py +++ b/apiserver/plane/api/views/external.py @@ -2,9 +2,10 @@ import requests # Third party imports +import openai from rest_framework.response import Response from rest_framework import status -import openai +from rest_framework.permissions import AllowAny from sentry_sdk import capture_exception # Django imports @@ -15,6 +16,7 @@ from plane.api.permissions import ProjectEntityPermission from plane.db.models import Workspace, Project from plane.api.serializers import ProjectLiteSerializer, WorkspaceLiteSerializer +from plane.utils.integrations.github import get_release_notes class GPTIntegrationEndpoint(BaseAPIView): @@ -73,3 +75,44 @@ def post(self, request, slug, project_id): {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) + + +class ReleaseNotesEndpoint(BaseAPIView): + def get(self, request): + try: + release_notes = get_release_notes() + return Response(release_notes, 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, + ) + + +class UnsplashEndpoint(BaseAPIView): + + def get(self, request): + try: + query = request.GET.get("query", False) + page = request.GET.get("page", 1) + per_page = request.GET.get("per_page", 20) + + url = ( + f"https://api.unsplash.com/search/photos/?client_id={settings.UNSPLASH_ACCESS_KEY}&query={query}&page=${page}&per_page={per_page}" + if query + else f"https://api.unsplash.com/photos/?client_id={settings.UNSPLASH_ACCESS_KEY}&page={page}&per_page={per_page}" + ) + + headers = { + "Content-Type": "application/json", + } + + resp = requests.get(url=url, headers=headers) + return Response(resp.json(), 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, + ) diff --git a/apiserver/plane/api/views/release.py b/apiserver/plane/api/views/release.py deleted file mode 100644 index de827c896e3..00000000000 --- a/apiserver/plane/api/views/release.py +++ /dev/null @@ -1,21 +0,0 @@ -# Third party imports -from rest_framework.response import Response -from rest_framework import status -from sentry_sdk import capture_exception - -# Module imports -from .base import BaseAPIView -from plane.utils.integrations.github import get_release_notes - - -class ReleaseNotesEndpoint(BaseAPIView): - def get(self, request): - try: - release_notes = get_release_notes() - return Response(release_notes, 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, - ) diff --git a/apiserver/plane/api/views/unsplash.py b/apiserver/plane/api/views/unsplash.py deleted file mode 100644 index 0e00a9ab404..00000000000 --- a/apiserver/plane/api/views/unsplash.py +++ /dev/null @@ -1,37 +0,0 @@ -# Python imports -import requests - -# Django imports -from django.conf import settings - -# Third party imports -from rest_framework import status -from rest_framework.response import Response -from rest_framework.permissions import AllowAny -from sentry_sdk import capture_exception - -# Module imports -from .base import BaseAPIView - - -class UnsplashEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] - - def get(self, request): - try: - query = request.GET.get("query", False) - page = request.GET.get("page", 1) - per_page = request.GET.get("per_page", 20) - url = ( - f"https://api.unsplash.com/search/photos/?client_id=${settings.UNSPLASH_ACCESS_KEY}&query=${query}&page=${page}&per_page=${per_page}" - if query - else f"https://api.unsplash.com/photos/?client_id=${unsplashKey}&page=${page}&per_page=${per_page}" - ) - 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/settings/local.py b/apiserver/plane/settings/local.py index 9d293c0191e..6f4833a6c70 100644 --- a/apiserver/plane/settings/local.py +++ b/apiserver/plane/settings/local.py @@ -114,3 +114,6 @@ GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False) ENABLE_SIGNUP = os.environ.get("ENABLE_SIGNUP", "1") == "1" + +# Unsplash Access key +UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY") diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index e434f974271..3557d266349 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -238,3 +238,5 @@ SCOUT_KEY = os.environ.get("SCOUT_KEY", "") SCOUT_NAME = "Plane" +# Unsplash Access key +UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY") diff --git a/apiserver/plane/settings/selfhosted.py b/apiserver/plane/settings/selfhosted.py index 948ba22da45..ee529a7c332 100644 --- a/apiserver/plane/settings/selfhosted.py +++ b/apiserver/plane/settings/selfhosted.py @@ -126,3 +126,4 @@ OPENAI_API_BASE = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", False) GPT_ENGINE = os.environ.get("GPT_ENGINE", "gpt-3.5-turbo") + diff --git a/apiserver/plane/settings/staging.py b/apiserver/plane/settings/staging.py index 5e274f8f32e..f776afd9117 100644 --- a/apiserver/plane/settings/staging.py +++ b/apiserver/plane/settings/staging.py @@ -218,3 +218,7 @@ GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False) ENABLE_SIGNUP = os.environ.get("ENABLE_SIGNUP", "1") == "1" + + +# Unsplash Access key +UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY")