From d05b63fc51859da7592851dfc5c4cec27ca0ec8e Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 14 Nov 2023 14:14:11 +0000 Subject: [PATCH] dev: instance configuration values --- apiserver/plane/api/views/config.py | 32 ++++++++++++------- apiserver/plane/api/views/external.py | 29 ++++++++++++----- .../plane/bgtasks/analytic_plot_export.py | 15 +++++---- .../plane/bgtasks/email_verification_task.py | 16 ++++++---- .../plane/bgtasks/forgot_password_task.py | 17 +++++----- .../plane/bgtasks/magic_link_code_task.py | 16 +++++----- .../plane/bgtasks/project_invitation_task.py | 16 +++++----- .../bgtasks/workspace_invitation_task.py | 16 +++++----- apiserver/plane/license/utils/__init__.py | 0 .../plane/license/utils/instance_value.py | 6 ++++ apiserver/requirements/base.txt | 2 +- 11 files changed, 98 insertions(+), 67 deletions(-) create mode 100644 apiserver/plane/license/utils/__init__.py create mode 100644 apiserver/plane/license/utils/instance_value.py diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py index d035c4740..1d65f3d68 100644 --- a/apiserver/plane/api/views/config.py +++ b/apiserver/plane/api/views/config.py @@ -12,7 +12,8 @@ from sentry_sdk import capture_exception # Module imports from .base import BaseAPIView - +from plane.license.models import Instance +from plane.license.utils.instance_value import get_configuration_value class ConfigurationEndpoint(BaseAPIView): permission_classes = [ @@ -20,18 +21,27 @@ class ConfigurationEndpoint(BaseAPIView): ] def get(self, request): + instance_configuration = Instance.objects.values("key", "value") + data = {} - data["google_client_id"] = os.environ.get("GOOGLE_CLIENT_ID", None) - data["github_client_id"] = os.environ.get("GITHUB_CLIENT_ID", None) - data["github_app_name"] = os.environ.get("GITHUB_APP_NAME", None) + # Authentication + data["google_client_id"] = get_configuration_value(instance_configuration, "GOOGLE_CLIENT_ID") + data["github_client_id"] = get_configuration_value(instance_configuration,"GITHUB_CLIENT_ID") + data["github_app_name"] = get_configuration_value(instance_configuration, "GITHUB_APP_NAME") data["magic_login"] = ( - bool(settings.EMAIL_HOST_USER) and bool(settings.EMAIL_HOST_PASSWORD) - ) and os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "0") == "1" + bool(get_configuration_value(instance_configuration, "EMAIL_HOST_USER")) and bool(get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD")) + ) and get_configuration_value(instance_configuration, "ENABLE_MAGIC_LINK_LOGIN", "0") == "1" data["email_password_login"] = ( - os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1" + get_configuration_value(instance_configuration, "ENABLE_EMAIL_PASSWORD", "0") == "1" ) - data["slack_client_id"] = os.environ.get("SLACK_CLIENT_ID", None) - data["posthog_api_key"] = os.environ.get("POSTHOG_API_KEY", None) - data["posthog_host"] = os.environ.get("POSTHOG_HOST", None) - data["has_unsplash_configured"] = bool(settings.UNSPLASH_ACCESS_KEY) + # Slack client + data["slack_client_id"] = get_configuration_value(instance_configuration, "SLACK_CLIENT_ID") + + # Posthog + data["posthog_api_key"] = get_configuration_value(instance_configuration, "POSTHOG_API_KEY") + data["posthog_host"] = get_configuration_value(instance_configuration, "POSTHOG_HOST") + + # Unsplash + data["has_unsplash_configured"] = bool(get_configuration_value(instance_configuration, "UNSPLASH_ACCESS_KEY")) + return Response(data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/api/views/external.py b/apiserver/plane/api/views/external.py index a04495569..1953743a2 100644 --- a/apiserver/plane/api/views/external.py +++ b/apiserver/plane/api/views/external.py @@ -2,7 +2,7 @@ import requests # Third party imports -import openai +from openai import OpenAI from rest_framework.response import Response from rest_framework import status from rest_framework.permissions import AllowAny @@ -17,7 +17,8 @@ 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 - +from plane.license.models import InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value class GPTIntegrationEndpoint(BaseAPIView): permission_classes = [ @@ -25,7 +26,14 @@ class GPTIntegrationEndpoint(BaseAPIView): ] def post(self, request, slug, project_id): - if not settings.OPENAI_API_KEY or not settings.GPT_ENGINE: + + # Get the configuration value + instance_configuration = InstanceConfiguration.objects.values("key", "value") + api_key = get_configuration_value(instance_configuration, "OPENAI_API_KEY") + gpt_engine = get_configuration_value(instance_configuration, "GPT_ENGINE") + + # Check the keys + if not api_key or not gpt_engine: return Response( {"error": "OpenAI API key and engine is required"}, status=status.HTTP_400_BAD_REQUEST, @@ -41,12 +49,17 @@ class GPTIntegrationEndpoint(BaseAPIView): final_text = task + "\n" + prompt - openai.api_key = settings.OPENAI_API_KEY - response = openai.ChatCompletion.create( - model=settings.GPT_ENGINE, + instance_configuration = InstanceConfiguration.objects.values("key", "value") + + gpt_engine = get_configuration_value(instance_configuration, "GPT_ENGINE") + + client = OpenAI( + api_key=api_key, + ) + + response = client.chat.completions.create( + model=gpt_engine, messages=[{"role": "user", "content": final_text}], - temperature=0.7, - max_tokens=1024, ) workspace = Workspace.objects.get(slug=slug) diff --git a/apiserver/plane/bgtasks/analytic_plot_export.py b/apiserver/plane/bgtasks/analytic_plot_export.py index b171f72db..19ee2306e 100644 --- a/apiserver/plane/bgtasks/analytic_plot_export.py +++ b/apiserver/plane/bgtasks/analytic_plot_export.py @@ -17,6 +17,7 @@ from plane.db.models import Issue from plane.utils.analytics_plot import build_graph_plot from plane.utils.issue_filters import issue_filters from plane.license.models import InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value row_mapping = { "state__name": "State", @@ -50,14 +51,14 @@ def send_export_email(email, slug, csv_buffer): csv_buffer.seek(0) # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) diff --git a/apiserver/plane/bgtasks/email_verification_task.py b/apiserver/plane/bgtasks/email_verification_task.py index 64cb1e6c6..aedc48e0e 100644 --- a/apiserver/plane/bgtasks/email_verification_task.py +++ b/apiserver/plane/bgtasks/email_verification_task.py @@ -12,6 +12,7 @@ from sentry_sdk import capture_exception # Module imports from plane.license.models import InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value @shared_task def email_verification(first_name, email, token, current_site): @@ -34,15 +35,16 @@ def email_verification(first_name, email, token, current_site): text_content = strip_tags(html_content) # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) + # Initiate email alternatives msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) msg.attach_alternative(html_content, "text/html") diff --git a/apiserver/plane/bgtasks/forgot_password_task.py b/apiserver/plane/bgtasks/forgot_password_task.py index a7c628e6a..fa4e2ea45 100644 --- a/apiserver/plane/bgtasks/forgot_password_task.py +++ b/apiserver/plane/bgtasks/forgot_password_task.py @@ -10,7 +10,7 @@ from sentry_sdk import capture_exception # Module imports from plane.license.models import InstanceConfiguration - +from plane.license.utils.instance_value import get_configuration_value @shared_task def forgot_password(first_name, email, uidb64, token, current_site): @@ -32,15 +32,14 @@ def forgot_password(first_name, email, uidb64, token, current_site): text_content = strip_tags(html_content) - # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) # Initiate email alternatives msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) diff --git a/apiserver/plane/bgtasks/magic_link_code_task.py b/apiserver/plane/bgtasks/magic_link_code_task.py index eaebd3f25..e6d047e9c 100644 --- a/apiserver/plane/bgtasks/magic_link_code_task.py +++ b/apiserver/plane/bgtasks/magic_link_code_task.py @@ -10,6 +10,7 @@ from sentry_sdk import capture_exception # Module imports from plane.license.models import InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value @shared_task def magic_link(email, key, token, current_site): @@ -27,15 +28,14 @@ def magic_link(email, key, token, current_site): text_content = strip_tags(html_content) - # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) # Initiate email alternatives msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) diff --git a/apiserver/plane/bgtasks/project_invitation_task.py b/apiserver/plane/bgtasks/project_invitation_task.py index 1a3bb1832..7a47b619d 100644 --- a/apiserver/plane/bgtasks/project_invitation_task.py +++ b/apiserver/plane/bgtasks/project_invitation_task.py @@ -11,7 +11,7 @@ from sentry_sdk import capture_exception # Module imports from plane.db.models import Project, User, ProjectMemberInvite from plane.license.models import InstanceConfiguration - +from plane.license.utils.instance_value import get_configuration_value @shared_task def project_invitation(email, project_id, token, current_site): @@ -45,14 +45,14 @@ def project_invitation(email, project_id, token, current_site): project_member_invite.save() # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) # Initiate email alternatives msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index 9ac8690bc..ad06b53b0 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -13,6 +13,7 @@ from slack_sdk.errors import SlackApiError # Module imports from plane.db.models import Workspace, WorkspaceMemberInvite from plane.license.models import InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value @shared_task def workspace_invitation(email, workspace_id, token, current_site, invitor): @@ -47,15 +48,14 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): workspace_member_invite.message = text_content workspace_member_invite.save() - # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values() + instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") connection = get_connection( - host=instance_configuration.get("EMAIL_HOST", ""), - port=int(instance_configuration.get("EMAIL_PORT", "587")), - username=instance_configuration.get("EMAIL_HOST_USER", ""), - password=instance_configuration.get("EMAIL_HOST_PASSWORD", ""), - use_tls=bool(instance_configuration.get("EMAIL_USE_TLS", "")), - use_ssl=bool(instance_configuration.get("EMAIL_USE_SSL", "")) + host=get_configuration_value(instance_configuration, "EMAIL_HOST"), + port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + username=get_configuration_value(instance_configuration, "EMAIL_HOST_USER"), + password=get_configuration_value(instance_configuration, "EMAIL_HOST_PASSWORD"), + use_tls=bool(get_configuration_value(instance_configuration, "EMAIL_USE_TLS", "1")), + use_ssl=bool(get_configuration_value(instance_configuration, "EMAIL_USE_SSL", "0")), ) # Initiate email alternatives msg = EmailMultiAlternatives(subject=subject, text_content=text_content, from_email=settings.EMAIL_FROM, to=[email], connection=connection) diff --git a/apiserver/plane/license/utils/__init__.py b/apiserver/plane/license/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py new file mode 100644 index 000000000..efca2799c --- /dev/null +++ b/apiserver/plane/license/utils/instance_value.py @@ -0,0 +1,6 @@ +# Helper function to return value from the passed key +def get_configuration_value(query, key, default=None): + for item in query: + if item['key'] == key: + return item.get("value", default) + return default diff --git a/apiserver/requirements/base.txt b/apiserver/requirements/base.txt index 249b29d48..0334915e2 100644 --- a/apiserver/requirements/base.txt +++ b/apiserver/requirements/base.txt @@ -26,7 +26,7 @@ google-api-python-client==2.97.0 django-redis==5.3.0 uvicorn==0.23.2 channels==4.0.0 -openai==0.28.0 +openai==1.2.4 slack-sdk==3.21.3 celery==5.3.4 django_celery_beat==2.5.0