From 1984a242d09e5d3f9401e7369ae2c71f37dc4c31 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 6 Dec 2023 19:19:42 +0530 Subject: [PATCH 01/11] dev: remove license engine communication --- apiserver/plane/celery.py | 4 -- .../management/commands/register_instance.py | 60 +++++-------------- apiserver/plane/settings/common.py | 1 - 3 files changed, 14 insertions(+), 51 deletions(-) diff --git a/apiserver/plane/celery.py b/apiserver/plane/celery.py index eb90c6205..442e72836 100644 --- a/apiserver/plane/celery.py +++ b/apiserver/plane/celery.py @@ -28,10 +28,6 @@ app.conf.beat_schedule = { "task": "plane.bgtasks.file_asset_task.delete_file_asset", "schedule": crontab(hour=0, minute=0), }, - "check-instance-verification": { - "task": "plane.license.bgtasks.instance_verification_task.instance_verification_task", - "schedule": crontab(minute=0, hour='*/4'), - }, } # Load task modules from all registered Django app configs. diff --git a/apiserver/plane/license/management/commands/register_instance.py b/apiserver/plane/license/management/commands/register_instance.py index 9d467b9dd..e6cfa7167 100644 --- a/apiserver/plane/license/management/commands/register_instance.py +++ b/apiserver/plane/license/management/commands/register_instance.py @@ -30,13 +30,11 @@ class Command(BaseCommand): # Load JSON content from the file data = json.load(file) - machine_signature = options.get("machine_signature", False) + machine_signature = options.get("machine_signature", "machine-signature") if not machine_signature: raise CommandError("Machine signature is required") - # Check if machine is online - headers = {"Content-Type": "application/json"} payload = { "instance_key": settings.INSTANCE_KEY, "version": data.get("version", 0.1), @@ -44,51 +42,21 @@ class Command(BaseCommand): "user_count": User.objects.filter(is_bot=False).count(), } - try: - response = requests.post( - f"{settings.LICENSE_ENGINE_BASE_URL}/api/instances/", - headers=headers, - data=json.dumps(payload), - timeout=30 - ) + instance = Instance.objects.create( + instance_name="Plane Free", + instance_id=secrets.token_hex(12), + license_key=None, + api_key=secrets.token_hex(8), + version=payload.get("version"), + last_checked_at=timezone.now(), + user_count=payload.get("user_count", 0), + ) - if response.status_code == 201: - data = response.json() - # Create instance - instance = Instance.objects.create( - instance_name="Plane Free", - instance_id=data.get("id"), - license_key=data.get("license_key"), - api_key=data.get("api_key"), - version=data.get("version"), - last_checked_at=timezone.now(), - user_count=data.get("user_count", 0), - is_verified=True, - ) - - self.stdout.write( - self.style.SUCCESS( - f"Instance successfully registered and verified" - ) - ) - return - except requests.RequestException as _e: - instance = Instance.objects.create( - instance_name="Plane Free", - instance_id=secrets.token_hex(12), - license_key=None, - api_key=secrets.token_hex(8), - version=payload.get("version"), - last_checked_at=timezone.now(), - user_count=payload.get("user_count", 0), + self.stdout.write( + self.style.SUCCESS( + f"Instance registered" ) - self.stdout.write( - self.style.SUCCESS( - f"Instance successfully registered" - ) - ) - return - raise CommandError("Instance could not be registered") + ) else: self.stdout.write( self.style.SUCCESS( diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 8ac7090d7..002c2accb 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -287,7 +287,6 @@ CELERY_IMPORTS = ( "plane.bgtasks.issue_automation_task", "plane.bgtasks.exporter_expired_task", "plane.bgtasks.file_asset_task", - "plane.license.bgtasks.instance_verification_task", ) # Sentry Settings From 9adb02036dcfa893d7afa628c3dfdc4b795893e2 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 6 Dec 2023 19:25:40 +0530 Subject: [PATCH 02/11] dev: remove license engine base url --- .../bgtasks/instance_verification_task.py | 136 ------------------ apiserver/plane/settings/common.py | 5 - 2 files changed, 141 deletions(-) delete mode 100644 apiserver/plane/license/bgtasks/instance_verification_task.py diff --git a/apiserver/plane/license/bgtasks/instance_verification_task.py b/apiserver/plane/license/bgtasks/instance_verification_task.py deleted file mode 100644 index 3ffaa6ba8..000000000 --- a/apiserver/plane/license/bgtasks/instance_verification_task.py +++ /dev/null @@ -1,136 +0,0 @@ -# Python imports -import os -import json -import requests - -# Django imports -from django.conf import settings - -# Third party imports -from celery import shared_task - -# Module imports -from plane.db.models import User -from plane.license.models import Instance, InstanceAdmin - - -def instance_verification(instance): - with open("package.json", "r") as file: - # Load JSON content from the file - data = json.load(file) - - headers = {"Content-Type": "application/json"} - payload = { - "instance_key": settings.INSTANCE_KEY, - "version": data.get("version", 0.1), - "machine_signature": os.environ.get("MACHINE_SIGNATURE", "machine-signature"), - "user_count": User.objects.filter(is_bot=False).count(), - } - # Register the instance - response = requests.post( - f"{settings.LICENSE_ENGINE_BASE_URL}/api/instances/", - headers=headers, - data=json.dumps(payload), - timeout=30, - ) - - # check response status - if response.status_code == 201: - data = response.json() - # Update instance - instance.instance_id = data.get("id") - instance.license_key = data.get("license_key") - instance.api_key = data.get("api_key") - instance.version = data.get("version") - instance.user_count = data.get("user_count", 0) - instance.is_verified = True - instance.save() - else: - return - - -def admin_verification(instance): - # Save the user in control center - headers = { - "Content-Type": "application/json", - "x-instance-id": instance.instance_id, - "x-api-key": instance.api_key, - } - - # Get all the unverified instance admins - instance_admins = InstanceAdmin.objects.filter(is_verified=False).select_related( - "user" - ) - updated_instance_admin = [] - - # Verify the instance admin - for instance_admin in instance_admins: - instance_admin.is_verified = True - # Create the admin - response = requests.post( - f"{settings.LICENSE_ENGINE_BASE_URL}/api/instances/users/register/", - headers=headers, - data=json.dumps( - { - "email": str(instance_admin.user.email), - "signup_mode": "EMAIL", - "is_admin": True, - } - ), - timeout=30, - ) - updated_instance_admin.append(instance_admin) - - # update all the instance admins - InstanceAdmin.objects.bulk_update( - updated_instance_admin, ["is_verified"], batch_size=10 - ) - return - -def instance_user_count(instance): - try: - instance_users = User.objects.filter(is_bot=False).count() - - # Update the count in the license engine - payload = { - "user_count": instance_users, - } - - # Save the user in control center - headers = { - "Content-Type": "application/json", - "x-instance-id": instance.instance_id, - "x-api-key": instance.api_key, - } - - # Update the license engine - _ = requests.post( - f"{settings.LICENSE_ENGINE_BASE_URL}/api/instances/", - headers=headers, - data=json.dumps(payload), - timeout=30, - ) - return - except requests.RequestException: - return - - -@shared_task -def instance_verification_task(): - try: - # Get the first instance - instance = Instance.objects.first() - - # Only register instance if it is not verified - if not instance.is_verified: - instance_verification(instance=instance) - - # Admin verifications - admin_verification(instance=instance) - - # Update user count - instance_user_count(instance=instance) - - return - except requests.RequestException: - return diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 002c2accb..046a61aaf 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -327,11 +327,6 @@ USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1 POSTHOG_API_KEY = os.environ.get("POSTHOG_API_KEY", False) POSTHOG_HOST = os.environ.get("POSTHOG_HOST", False) -# License engine base url -LICENSE_ENGINE_BASE_URL = os.environ.get( - "LICENSE_ENGINE_BASE_URL", "https://control-center.plane.so" -) - # instance key INSTANCE_KEY = os.environ.get( "INSTANCE_KEY", "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3" From 7b8a3d2e95971a4b04f93ed46e9a071e44e89319 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 6 Dec 2023 22:34:36 +0530 Subject: [PATCH 03/11] dev: update instance configuration function --- apiserver/plane/app/views/auth_extended.py | 39 ++-- apiserver/plane/app/views/authentication.py | 39 ++-- apiserver/plane/app/views/config.py | 203 ++++++++++++------ apiserver/plane/app/views/external.py | 43 ++-- apiserver/plane/app/views/oauth.py | 43 ++-- .../plane/bgtasks/analytic_plot_export.py | 8 +- .../plane/bgtasks/forgot_password_task.py | 10 +- .../plane/bgtasks/magic_link_code_task.py | 7 +- .../plane/bgtasks/project_invitation_task.py | 4 +- .../bgtasks/workspace_invitation_task.py | 9 +- .../plane/license/utils/instance_value.py | 151 ++++++++----- apiserver/plane/settings/common.py | 3 + 12 files changed, 327 insertions(+), 232 deletions(-) diff --git a/apiserver/plane/app/views/auth_extended.py b/apiserver/plane/app/views/auth_extended.py index b169fde65..660829f4b 100644 --- a/apiserver/plane/app/views/auth_extended.py +++ b/apiserver/plane/app/views/auth_extended.py @@ -34,7 +34,7 @@ from plane.app.serializers import ( from plane.db.models import User, WorkspaceMemberInvite from plane.license.utils.instance_value import get_configuration_value from plane.bgtasks.forgot_password_task import forgot_password -from plane.license.models import Instance, InstanceConfiguration +from plane.license.models import Instance from plane.settings.redis import redis_instance from plane.bgtasks.magic_link_code_task import magic_link from plane.bgtasks.event_tracking_task import auth_events @@ -321,8 +321,19 @@ class EmailCheckEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) - # Get the configurations - instance_configuration = InstanceConfiguration.objects.values("key", "value") + # Get configuration values + ENABLE_SIGNUP, ENABLE_MAGIC_LINK_LOGIN = get_configuration_value( + [ + { + "key": "ENABLE_SIGNUP", + "default": os.environ.get("ENABLE_SIGNUP"), + }, + { + "key": "ENABLE_MAGIC_LINK_LOGIN", + "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN"), + }, + ] + ) email = request.data.get("email", False) @@ -347,12 +358,7 @@ class EmailCheckEndpoint(BaseAPIView): if user is None: # Create the user if ( - get_configuration_value( - instance_configuration, - "ENABLE_SIGNUP", - os.environ.get("ENABLE_SIGNUP", "0"), - ) - == "0" + ENABLE_SIGNUP == "0" and not WorkspaceMemberInvite.objects.filter( email=email, ).exists() @@ -372,13 +378,8 @@ class EmailCheckEndpoint(BaseAPIView): is_password_autoset=True, ) - if not bool( - get_configuration_value( - instance_configuration, - "ENABLE_MAGIC_LINK_LOGIN", - os.environ.get("ENABLE_MAGIC_LINK_LOGIN"), - ), + ENABLE_MAGIC_LINK_LOGIN, ): return Response( {"error": "Magic link sign in is disabled."}, @@ -413,13 +414,7 @@ class EmailCheckEndpoint(BaseAPIView): else: if user.is_password_autoset: ## Generate a random token - if not bool( - get_configuration_value( - instance_configuration, - "ENABLE_MAGIC_LINK_LOGIN", - os.environ.get("ENABLE_MAGIC_LINK_LOGIN"), - ), - ): + if not bool(ENABLE_MAGIC_LINK_LOGIN): return Response( {"error": "Magic link sign in is disabled."}, status=status.HTTP_400_BAD_REQUEST, diff --git a/apiserver/plane/app/views/authentication.py b/apiserver/plane/app/views/authentication.py index 32b8a34f4..2a94b5f1f 100644 --- a/apiserver/plane/app/views/authentication.py +++ b/apiserver/plane/app/views/authentication.py @@ -27,7 +27,7 @@ from plane.db.models import ( ProjectMember, ) from plane.settings.redis import redis_instance -from plane.license.models import InstanceConfiguration, Instance +from plane.license.models import Instance from plane.license.utils.instance_value import get_configuration_value from plane.bgtasks.event_tracking_task import auth_events @@ -52,8 +52,6 @@ class SignUpEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) - instance_configuration = InstanceConfiguration.objects.values("key", "value") - email = request.data.get("email", False) password = request.data.get("password", False) ## Raise exception if any of the above are missing @@ -73,14 +71,20 @@ class SignUpEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) + # get configuration values + # Get configuration values + ENABLE_SIGNUP = get_configuration_value( + [ + { + "key": "ENABLE_SIGNUP", + "default": os.environ.get("ENABLE_SIGNUP"), + }, + ] + ) + # If the sign up is not enabled and the user does not have invite disallow him from creating the account if ( - get_configuration_value( - instance_configuration, - "ENABLE_SIGNUP", - os.environ.get("ENABLE_SIGNUP", "0"), - ) - == "0" + ENABLE_SIGNUP == "0" and not WorkspaceMemberInvite.objects.filter( email=email, ).exists() @@ -169,16 +173,17 @@ class SignInEndpoint(BaseAPIView): # Create the user else: - # Get the configurations - instance_configuration = InstanceConfiguration.objects.values("key", "value") + ENABLE_SIGNUP = get_configuration_value( + [ + { + "key": "ENABLE_SIGNUP", + "default": os.environ.get("ENABLE_SIGNUP"), + }, + ] + ) # Create the user if ( - get_configuration_value( - instance_configuration, - "ENABLE_SIGNUP", - os.environ.get("ENABLE_SIGNUP", "0"), - ) - == "0" + ENABLE_SIGNUP == "0" and not WorkspaceMemberInvite.objects.filter( email=email, ).exists() diff --git a/apiserver/plane/app/views/config.py b/apiserver/plane/app/views/config.py index cef68d6d0..9ed82a573 100644 --- a/apiserver/plane/app/views/config.py +++ b/apiserver/plane/app/views/config.py @@ -11,7 +11,6 @@ from rest_framework.response import Response # Module imports from .base import BaseAPIView -from plane.license.models import InstanceConfiguration from plane.license.utils.instance_value import get_configuration_value @@ -21,89 +20,155 @@ class ConfigurationEndpoint(BaseAPIView): ] def get(self, request): - instance_configuration = InstanceConfiguration.objects.values("key", "value") + print( + get_configuration_value( + [ + { + "key": "GOOGLE_CLIENT_ID", + "default": os.environ.get("GOOGLE_CLIENT_ID", None), + }, + { + "key": "GITHUB_CLIENT_ID", + "default": os.environ.get("GITHUB_CLIENT_ID", None), + }, + { + "key": "GITHUB_APP_NAME", + "default": os.environ.get("GITHUB_APP_NAME", None), + }, + { + "key": "EMAIL_HOST_USER", + "default": os.environ.get("EMAIL_HOST_USER", None), + }, + { + "key": "EMAIL_HOST_PASSWORD", + "default": os.environ.get("EMAIL_HOST_PASSWORD", None), + }, + { + "key": "ENABLE_MAGIC_LINK_LOGIN", + "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), + }, + { + "key": "ENABLE_EMAIL_PASSWORD", + "default": os.environ.get("ENABLE_EMAIL_PASSWORD", "1"), + }, + { + "key": "SLACK_CLIENT_ID", + "default": os.environ.get("SLACK_CLIENT_ID", "1"), + }, + { + "key": "POSTHOG_API_KEY", + "default": os.environ.get("POSTHOG_API_KEY", "1"), + }, + { + "key": "POSTHOG_HOST", + "default": os.environ.get("POSTHOG_HOST", "1"), + }, + { + "key": "UNSPLASH_ACCESS_KEY", + "default": os.environ.get("UNSPLASH_ACCESS_KEY", "1"), + }, + { + "key": "OPENAI_API_KEY", + "default": os.environ.get("OPENAI_API_KEY", "1"), + }, + ] + ) + ) + # Get all the configuration + ( + GOOGLE_CLIENT_ID, + GITHUB_CLIENT_ID, + GITHUB_APP_NAME, + EMAIL_HOST_USER, + EMAIL_HOST_PASSWORD, + ENABLE_MAGIC_LINK_LOGIN, + ENABLE_EMAIL_PASSWORD, + SLACK_CLIENT_ID, + POSTHOG_API_KEY, + POSTHOG_HOST, + UNSPLASH_ACCESS_KEY, + OPENAI_API_KEY, + ) = get_configuration_value( + [ + { + "key": "GOOGLE_CLIENT_ID", + "default": os.environ.get("GOOGLE_CLIENT_ID", None), + }, + { + "key": "GITHUB_CLIENT_ID", + "default": os.environ.get("GITHUB_CLIENT_ID", None), + }, + { + "key": "GITHUB_APP_NAME", + "default": os.environ.get("GITHUB_APP_NAME", None), + }, + { + "key": "EMAIL_HOST_USER", + "default": os.environ.get("EMAIL_HOST_USER", None), + }, + { + "key": "EMAIL_HOST_PASSWORD", + "default": os.environ.get("EMAIL_HOST_PASSWORD", None), + }, + { + "key": "ENABLE_MAGIC_LINK_LOGIN", + "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), + }, + { + "key": "ENABLE_EMAIL_PASSWORD", + "default": os.environ.get("ENABLE_EMAIL_PASSWORD", "1"), + }, + { + "key": "SLACK_CLIENT_ID", + "default": os.environ.get("SLACK_CLIENT_ID", "1"), + }, + { + "key": "POSTHOG_API_KEY", + "default": os.environ.get("POSTHOG_API_KEY", "1"), + }, + { + "key": "POSTHOG_HOST", + "default": os.environ.get("POSTHOG_HOST", "1"), + }, + { + "key": "UNSPLASH_ACCESS_KEY", + "default": os.environ.get("UNSPLASH_ACCESS_KEY", "1"), + }, + { + "key": "OPENAI_API_KEY", + "default": os.environ.get("OPENAI_API_KEY", "1"), + }, + ] + ) + print("hewllo") data = {} # Authentication - data["google_client_id"] = get_configuration_value( - instance_configuration, - "GOOGLE_CLIENT_ID", - os.environ.get("GOOGLE_CLIENT_ID", None), - ) - data["github_client_id"] = get_configuration_value( - instance_configuration, - "GITHUB_CLIENT_ID", - os.environ.get("GITHUB_CLIENT_ID", None), - ) - data["github_app_name"] = get_configuration_value( - instance_configuration, - "GITHUB_APP_NAME", - os.environ.get("GITHUB_APP_NAME", None), - ) + data["google_client_id"] = GOOGLE_CLIENT_ID + data["github_client_id"] = GITHUB_CLIENT_ID + data["github_app_name"] = GITHUB_APP_NAME data["magic_login"] = ( - bool( - get_configuration_value( - instance_configuration, - "EMAIL_HOST_USER", - os.environ.get("EMAIL_HOST_USER", None), - ), - ) - and bool( - get_configuration_value( - instance_configuration, - "EMAIL_HOST_PASSWORD", - os.environ.get("EMAIL_HOST_PASSWORD", None), - ) - ) - ) and get_configuration_value( - instance_configuration, "ENABLE_MAGIC_LINK_LOGIN", "1" - ) == "1" + bool(EMAIL_HOST_USER) and bool(EMAIL_HOST_PASSWORD) + ) and ENABLE_MAGIC_LINK_LOGIN == "1" - data["email_password_login"] = ( - get_configuration_value( - instance_configuration, "ENABLE_EMAIL_PASSWORD", "1" - ) - == "1" - ) + data["email_password_login"] = ENABLE_EMAIL_PASSWORD == "1" # Slack client - data["slack_client_id"] = get_configuration_value( - instance_configuration, - "SLACK_CLIENT_ID", - os.environ.get("SLACK_CLIENT_ID", None), - ) + data["slack_client_id"] = SLACK_CLIENT_ID # Posthog - data["posthog_api_key"] = get_configuration_value( - instance_configuration, - "POSTHOG_API_KEY", - os.environ.get("POSTHOG_API_KEY", None), - ) - data["posthog_host"] = get_configuration_value( - instance_configuration, - "POSTHOG_HOST", - os.environ.get("POSTHOG_HOST", None), - ) + data["posthog_api_key"] = POSTHOG_API_KEY + data["posthog_host"] = POSTHOG_HOST # Unsplash - data["has_unsplash_configured"] = bool( - get_configuration_value( - instance_configuration, - "UNSPLASH_ACCESS_KEY", - os.environ.get("UNSPLASH_ACCESS_KEY", None), - ) - ) + data["has_unsplash_configured"] = UNSPLASH_ACCESS_KEY # Open AI settings - data["has_openai_configured"] = bool( - get_configuration_value( - instance_configuration, - "OPENAI_API_KEY", - os.environ.get("OPENAI_API_KEY", None), - ) - ) + data["has_openai_configured"] = bool(OPENAI_API_KEY) + # File size settings data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880)) + # is self managed data["is_self_managed"] = bool(int(os.environ.get("IS_SELF_MANAGED", "1"))) return Response(data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/external.py b/apiserver/plane/app/views/external.py index b9f8a0cf0..448234fc1 100644 --- a/apiserver/plane/app/views/external.py +++ b/apiserver/plane/app/views/external.py @@ -1,6 +1,7 @@ # Python imports import requests import os + # Third party imports from openai import OpenAI from rest_framework.response import Response @@ -15,23 +16,31 @@ from plane.app.permissions import ProjectEntityPermission from plane.db.models import Workspace, Project from plane.app.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 = [ ProjectEntityPermission, ] def post(self, request, slug, project_id): + OPENAI_API_KEY, GPT_ENGINE = get_configuration_value( + [ + { + "key": "OPENAI_API_KEY", + "default": os.environ.get("OPENAI_API_KEY", None), + }, + { + "key": "GPT_ENGINE", + "default": os.environ.get("GPT_ENGINE", "gpt-3.5-turbo"), + }, + ] + ) # Get the configuration value - instance_configuration = InstanceConfiguration.objects.values("key", "value") - api_key = get_configuration_value(instance_configuration, "OPENAI_API_KEY", os.environ.get("OPENAI_API_KEY")) - gpt_engine = get_configuration_value(instance_configuration, "GPT_ENGINE", os.environ.get("GPT_ENGINE", "gpt-3.5-turbo")) - # Check the keys - if not api_key or not gpt_engine: + if not OPENAI_API_KEY or not GPT_ENGINE: return Response( {"error": "OpenAI API key and engine is required"}, status=status.HTTP_400_BAD_REQUEST, @@ -48,11 +57,11 @@ class GPTIntegrationEndpoint(BaseAPIView): final_text = task + "\n" + prompt client = OpenAI( - api_key=api_key, + api_key=OPENAI_API_KEY, ) response = client.chat.completions.create( - model=gpt_engine, + model=GPT_ENGINE, messages=[{"role": "user", "content": final_text}], ) @@ -79,13 +88,17 @@ class ReleaseNotesEndpoint(BaseAPIView): class UnsplashEndpoint(BaseAPIView): - def get(self, request): - instance_configuration = InstanceConfiguration.objects.values("key", "value") - unsplash_access_key = get_configuration_value(instance_configuration, "UNSPLASH_ACCESS_KEY", os.environ.get("UNSPLASH_ACCESS_KEY")) - + UNSPLASH_ACESS_KEY = get_configuration_value( + [ + { + "key": "UNSPLASH_ACESS_KEY", + "default": os.environ.get("UNSPLASH_ACCESS_KEY"), + } + ] + ) # Check unsplash access key - if not unsplash_access_key: + if not UNSPLASH_ACESS_KEY: return Response([], status=status.HTTP_200_OK) # Query parameters @@ -94,9 +107,9 @@ class UnsplashEndpoint(BaseAPIView): per_page = request.GET.get("per_page", 20) url = ( - f"https://api.unsplash.com/search/photos/?client_id={unsplash_access_key}&query={query}&page=${page}&per_page={per_page}" + f"https://api.unsplash.com/search/photos/?client_id={UNSPLASH_ACESS_KEY}&query={query}&page=${page}&per_page={per_page}" if query - else f"https://api.unsplash.com/photos/?client_id={unsplash_access_key}&page={page}&per_page={per_page}" + else f"https://api.unsplash.com/photos/?client_id={UNSPLASH_ACESS_KEY}&page={page}&per_page={per_page}" ) headers = { diff --git a/apiserver/plane/app/views/oauth.py b/apiserver/plane/app/views/oauth.py index 85e6ac957..0ffb76db2 100644 --- a/apiserver/plane/app/views/oauth.py +++ b/apiserver/plane/app/views/oauth.py @@ -30,7 +30,7 @@ from plane.db.models import ( ) from plane.bgtasks.event_tracking_task import auth_events from .base import BaseAPIView -from plane.license.models import InstanceConfiguration, Instance +from plane.license.models import Instance from plane.license.utils.instance_value import get_configuration_value @@ -147,18 +147,20 @@ class OauthEndpoint(BaseAPIView): id_token = request.data.get("credential", False) client_id = request.data.get("clientId", False) - instance_configuration = InstanceConfiguration.objects.values( - "key", "value" + GOOGLE_CLIENT_ID, GITHUB_CLIENT_ID = get_configuration_value( + [ + { + "key": "GOOGLE_CLIENT_ID", + "default": os.environ.get("GOOGLE_CLIENT_ID"), + }, + { + "key": "GITHUB_CLIENT_ID", + "default": os.environ.get("GITHUB_CLIENT_ID"), + }, + ] ) - if not get_configuration_value( - instance_configuration, - "GOOGLE_CLIENT_ID", - os.environ.get("GOOGLE_CLIENT_ID"), - ) or not get_configuration_value( - instance_configuration, - "GITHUB_CLIENT_ID", - os.environ.get("GITHUB_CLIENT_ID"), - ): + + if not GOOGLE_CLIENT_ID or not GITHUB_CLIENT_ID: return Response( {"error": "Github or Google login is not configured"}, status=status.HTTP_400_BAD_REQUEST, @@ -298,17 +300,16 @@ class OauthEndpoint(BaseAPIView): return Response(data, status=status.HTTP_200_OK) except User.DoesNotExist: - ## Signup Case - instance_configuration = InstanceConfiguration.objects.values( - "key", "value" + ENABLE_SIGNUP = get_configuration_value( + [ + { + "key": "ENABLE_SIGNUP", + "default": os.environ.get("ENABLE_SIGNUP", "0"), + } + ] ) if ( - get_configuration_value( - instance_configuration, - "ENABLE_SIGNUP", - os.environ.get("ENABLE_SIGNUP", "0"), - ) - == "0" + ENABLE_SIGNUP == "0" and not WorkspaceMemberInvite.objects.filter( email=email, ).exists() diff --git a/apiserver/plane/bgtasks/analytic_plot_export.py b/apiserver/plane/bgtasks/analytic_plot_export.py index 5d4f58eba..4aa86f6ca 100644 --- a/apiserver/plane/bgtasks/analytic_plot_export.py +++ b/apiserver/plane/bgtasks/analytic_plot_export.py @@ -18,7 +18,6 @@ from sentry_sdk import capture_exception 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, Instance from plane.license.utils.instance_value import get_email_configuration row_mapping = { @@ -52,11 +51,6 @@ def send_export_email(email, slug, csv_buffer, rows): csv_buffer.seek(0) - # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") - ( EMAIL_HOST, EMAIL_HOST_USER, @@ -64,7 +58,7 @@ def send_export_email(email, slug, csv_buffer, rows): EMAIL_PORT, EMAIL_USE_TLS, EMAIL_FROM, - ) = get_email_configuration(instance_configuration=instance_configuration) + ) = get_email_configuration() connection = get_connection( host=EMAIL_HOST, diff --git a/apiserver/plane/bgtasks/forgot_password_task.py b/apiserver/plane/bgtasks/forgot_password_task.py index b24d81d93..563cc8a40 100644 --- a/apiserver/plane/bgtasks/forgot_password_task.py +++ b/apiserver/plane/bgtasks/forgot_password_task.py @@ -14,7 +14,6 @@ from celery import shared_task from sentry_sdk import capture_exception # Module imports -from plane.license.models import InstanceConfiguration, Instance from plane.license.utils.instance_value import get_email_configuration @@ -26,10 +25,6 @@ def forgot_password(first_name, email, uidb64, token, current_site): ) abs_url = str(current_site) + relative_link - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") - ( EMAIL_HOST, EMAIL_HOST_USER, @@ -37,7 +32,7 @@ def forgot_password(first_name, email, uidb64, token, current_site): EMAIL_PORT, EMAIL_USE_TLS, EMAIL_FROM, - ) = get_email_configuration(instance_configuration=instance_configuration) + ) = get_email_configuration() subject = "A new password to your Plane account has been requested" @@ -51,9 +46,6 @@ def forgot_password(first_name, email, uidb64, token, current_site): text_content = strip_tags(html_content) - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") connection = get_connection( host=EMAIL_HOST, port=int(EMAIL_PORT), diff --git a/apiserver/plane/bgtasks/magic_link_code_task.py b/apiserver/plane/bgtasks/magic_link_code_task.py index 2e8a7de16..55bbfa0d6 100644 --- a/apiserver/plane/bgtasks/magic_link_code_task.py +++ b/apiserver/plane/bgtasks/magic_link_code_task.py @@ -14,17 +14,12 @@ from celery import shared_task from sentry_sdk import capture_exception # Module imports -from plane.license.models import InstanceConfiguration, Instance from plane.license.utils.instance_value import get_email_configuration @shared_task def magic_link(email, key, token, current_site): try: - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") - ( EMAIL_HOST, EMAIL_HOST_USER, @@ -32,7 +27,7 @@ def magic_link(email, key, token, current_site): EMAIL_PORT, EMAIL_USE_TLS, EMAIL_FROM, - ) = get_email_configuration(instance_configuration=instance_configuration) + ) = get_email_configuration() # Send the mail subject = f"Your unique Plane login code is {token}" diff --git a/apiserver/plane/bgtasks/project_invitation_task.py b/apiserver/plane/bgtasks/project_invitation_task.py index 1ff160afc..4ec06e623 100644 --- a/apiserver/plane/bgtasks/project_invitation_task.py +++ b/apiserver/plane/bgtasks/project_invitation_task.py @@ -13,7 +13,6 @@ 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_email_configuration @shared_task @@ -47,7 +46,6 @@ def project_invitation(email, project_id, token, current_site, invitor): project_member_invite.save() # Configure email connection from the database - instance_configuration = InstanceConfiguration.objects.filter(key__startswith='EMAIL_').values("key", "value") ( EMAIL_HOST, EMAIL_HOST_USER, @@ -55,7 +53,7 @@ def project_invitation(email, project_id, token, current_site, invitor): EMAIL_PORT, EMAIL_USE_TLS, EMAIL_FROM, - ) = get_email_configuration(instance_configuration=instance_configuration) + ) = get_email_configuration() connection = get_connection( host=EMAIL_HOST, diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index 08fbaea62..1bdc48ca3 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -17,7 +17,6 @@ from slack_sdk.errors import SlackApiError # Module imports from plane.db.models import Workspace, WorkspaceMemberInvite, User -from plane.license.models import InstanceConfiguration, Instance from plane.license.utils.instance_value import get_email_configuration @@ -37,9 +36,6 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): # The complete url including the domain abs_url = str(current_site) + relative_link - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") ( EMAIL_HOST, @@ -48,7 +44,7 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): EMAIL_PORT, EMAIL_USE_TLS, EMAIL_FROM, - ) = get_email_configuration(instance_configuration=instance_configuration) + ) = get_email_configuration() # Subject of the email subject = f"{user.first_name or user.display_name or user.email} has invited you to join them in {workspace.name} on Plane" @@ -69,9 +65,6 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): workspace_member_invite.message = text_content workspace_member_invite.save() - instance_configuration = InstanceConfiguration.objects.filter( - key__startswith="EMAIL_" - ).values("key", "value") connection = get_connection( host=EMAIL_HOST, port=int(EMAIL_PORT), diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index 547467f52..53481108a 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -1,63 +1,104 @@ +# Python imports import os +# Django imports +from django.conf import settings + +# Module imports +from plane.license.models import InstanceConfiguration +from plane.license.utils.encryption import decrypt_data + + + +def create_lookup_dict(list_of_dicts): + return {item['key']: item for item in list_of_dicts} + + # 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 +def get_configuration_value(keys): + environment_list = [] + if settings.SKIP_ENV_VAR: + # Get the configurations + instance_configuration = InstanceConfiguration.objects.values( + "key", "value", "is_encrypted" + ) + + for key in keys: + for item in instance_configuration: + if key.get("key") == item.get("key"): + if item.get("is_encrypted", False): + environment_list.append(decrypt_data(item.get("value"))) + else: + environment_list.append(item.get("value")) + + break + else: + environment_list.append(key.get("default")) + + return tuple(environment_list) -def get_email_configuration(instance_configuration): - # Get the configuration variables - EMAIL_HOST_USER = get_configuration_value( - instance_configuration, - "EMAIL_HOST_USER", - os.environ.get("EMAIL_HOST_USER", None), - ) +def get_email_configuration(): + if settings.SKIP_ENV_VAR: + ( + EMAIL_HOST_USER, + EMAIL_HOST_PASSWORD, + EMAIL_HOST, + EMAIL_FROM, + EMAIL_USE_TLS, + EMAIL_PORT, + ) = get_configuration_value( + [ + { + "key": "EMAIL_HOST_USER", + "default": os.environ.get("EMAIL_HOST_USER", None), + }, + { + "key": "EMAIL_HOST_PASSWORD", + "default": os.environ.get("EMAIL_HOST_PASSWORD", None), + }, + { + "key": "EMAIL_HOST", + "default": os.environ.get("EMAIL_HOST", None), + }, + { + "key": "EMAIL_FROM", + "default": os.environ.get("EMAIL_FROM", None), + }, + { + "key": "EMAIL_USE_TLS", + "default": os.environ.get("EMAIL_USE_TLS", "1"), + }, + { + "key": "EMAIL_PORT", + "default": os.environ.get("EMAIL_PORT", 587), + }, + ] + ) + return ( + EMAIL_HOST, + EMAIL_HOST_USER, + EMAIL_HOST_PASSWORD, + EMAIL_PORT, + EMAIL_USE_TLS, + EMAIL_FROM, + ) - EMAIL_HOST_PASSWORD = get_configuration_value( - instance_configuration, - "EMAIL_HOST_PASSWORD", - os.environ.get("EMAIL_HOST_PASSWORD", None), - ) + else: + # Get email configuration directly from os + EMAIL_HOST = os.environ.get("EMAIL_HOST") + EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") + EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD") + EMAIL_PORT = os.environ.get("EMAIL_PORT", 587) + EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "1") + EMAIL_FROM = os.environ.get("EMAIL_FROM", "Team Plane ") - EMAIL_HOST = get_configuration_value( - instance_configuration, - "EMAIL_HOST", - os.environ.get("EMAIL_HOST", None), - ) - - EMAIL_FROM = get_configuration_value( - instance_configuration, - "EMAIL_FROM", - os.environ.get("EMAIL_FROM", None), - ) - - EMAIL_USE_TLS = get_configuration_value( - instance_configuration, - "EMAIL_USE_TLS", - os.environ.get("EMAIL_USE_TLS", "1"), - ) - - EMAIL_PORT = get_configuration_value( - instance_configuration, - "EMAIL_PORT", - 587, - ) - - EMAIL_FROM = get_configuration_value( - instance_configuration, - "EMAIL_FROM", - os.environ.get("EMAIL_FROM", "Team Plane "), - ) - - return ( - EMAIL_HOST, - EMAIL_HOST_USER, - EMAIL_HOST_PASSWORD, - EMAIL_PORT, - EMAIL_USE_TLS, - EMAIL_FROM, - ) + return ( + EMAIL_HOST, + EMAIL_HOST_USER, + EMAIL_HOST_PASSWORD, + EMAIL_PORT, + EMAIL_USE_TLS, + EMAIL_FROM, + ) diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 046a61aaf..76528176b 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -331,3 +331,6 @@ POSTHOG_HOST = os.environ.get("POSTHOG_HOST", False) INSTANCE_KEY = os.environ.get( "INSTANCE_KEY", "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3" ) + +# Skip environment variable configuration +SKIP_ENV_VAR = os.environ.get("SKIP_ENV_VAR", "1") == "1" From b912bb21ad7470569e2c847606765561a15eb219 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 11:52:48 +0530 Subject: [PATCH 04/11] chore: removed the print statement --- apiserver/plane/app/views/config.py | 56 +---------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/apiserver/plane/app/views/config.py b/apiserver/plane/app/views/config.py index 9ed82a573..fb32e8570 100644 --- a/apiserver/plane/app/views/config.py +++ b/apiserver/plane/app/views/config.py @@ -20,60 +20,6 @@ class ConfigurationEndpoint(BaseAPIView): ] def get(self, request): - print( - get_configuration_value( - [ - { - "key": "GOOGLE_CLIENT_ID", - "default": os.environ.get("GOOGLE_CLIENT_ID", None), - }, - { - "key": "GITHUB_CLIENT_ID", - "default": os.environ.get("GITHUB_CLIENT_ID", None), - }, - { - "key": "GITHUB_APP_NAME", - "default": os.environ.get("GITHUB_APP_NAME", None), - }, - { - "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER", None), - }, - { - "key": "EMAIL_HOST_PASSWORD", - "default": os.environ.get("EMAIL_HOST_PASSWORD", None), - }, - { - "key": "ENABLE_MAGIC_LINK_LOGIN", - "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), - }, - { - "key": "ENABLE_EMAIL_PASSWORD", - "default": os.environ.get("ENABLE_EMAIL_PASSWORD", "1"), - }, - { - "key": "SLACK_CLIENT_ID", - "default": os.environ.get("SLACK_CLIENT_ID", "1"), - }, - { - "key": "POSTHOG_API_KEY", - "default": os.environ.get("POSTHOG_API_KEY", "1"), - }, - { - "key": "POSTHOG_HOST", - "default": os.environ.get("POSTHOG_HOST", "1"), - }, - { - "key": "UNSPLASH_ACCESS_KEY", - "default": os.environ.get("UNSPLASH_ACCESS_KEY", "1"), - }, - { - "key": "OPENAI_API_KEY", - "default": os.environ.get("OPENAI_API_KEY", "1"), - }, - ] - ) - ) # Get all the configuration ( @@ -141,7 +87,7 @@ class ConfigurationEndpoint(BaseAPIView): }, ] ) - print("hewllo") + data = {} # Authentication data["google_client_id"] = GOOGLE_CLIENT_ID From 5a364855e8d42364b2e70cac0c7dc9b21419e90c Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 12:42:02 +0530 Subject: [PATCH 05/11] chore: changed config variables --- apiserver/plane/app/views/auth_extended.py | 57 ++++++----- apiserver/plane/app/views/authentication.py | 42 ++++----- apiserver/plane/app/views/external.py | 10 +- apiserver/plane/app/views/oauth.py | 40 ++++---- apiserver/plane/app/views/workspace.py | 17 ++-- .../plane/bgtasks/event_tracking_task.py | 94 ++++++++++++------- 6 files changed, 140 insertions(+), 120 deletions(-) diff --git a/apiserver/plane/app/views/auth_extended.py b/apiserver/plane/app/views/auth_extended.py index 660829f4b..049e5aab9 100644 --- a/apiserver/plane/app/views/auth_extended.py +++ b/apiserver/plane/app/views/auth_extended.py @@ -387,16 +387,15 @@ class EmailCheckEndpoint(BaseAPIView): ) # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium="MAGIC_LINK", - first_time=True, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="MAGIC_LINK", + first_time=True, + ) key, token, current_attempt = generate_magic_token(email=email) if not current_attempt: return Response( @@ -420,16 +419,15 @@ class EmailCheckEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium="MAGIC_LINK", - first_time=False, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="MAGIC_LINK", + first_time=False, + ) # Generate magic token key, token, current_attempt = generate_magic_token(email=email) @@ -449,16 +447,15 @@ class EmailCheckEndpoint(BaseAPIView): status=status.HTTP_200_OK, ) else: - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium="EMAIL", - first_time=False, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="EMAIL", + first_time=False, + ) # User should enter password to login return Response( diff --git a/apiserver/plane/app/views/authentication.py b/apiserver/plane/app/views/authentication.py index 2a94b5f1f..811eeb959 100644 --- a/apiserver/plane/app/views/authentication.py +++ b/apiserver/plane/app/views/authentication.py @@ -73,7 +73,7 @@ class SignUpEndpoint(BaseAPIView): # get configuration values # Get configuration values - ENABLE_SIGNUP = get_configuration_value( + ENABLE_SIGNUP, = get_configuration_value( [ { "key": "ENABLE_SIGNUP", @@ -173,7 +173,7 @@ class SignInEndpoint(BaseAPIView): # Create the user else: - ENABLE_SIGNUP = get_configuration_value( + ENABLE_SIGNUP, = get_configuration_value( [ { "key": "ENABLE_SIGNUP", @@ -269,16 +269,15 @@ class SignInEndpoint(BaseAPIView): workspace_member_invites.delete() project_member_invites.delete() # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium="EMAIL", - first_time=False, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="EMAIL", + first_time=False, + ) access_token, refresh_token = get_tokens_for_user(user) data = { @@ -352,16 +351,15 @@ class MagicSignInEndpoint(BaseAPIView): status=status.HTTP_403_FORBIDDEN, ) # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium="MAGIC_LINK", - first_time=False, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="MAGIC_LINK", + first_time=False, + ) user.is_active = True user.is_email_verified = True diff --git a/apiserver/plane/app/views/external.py b/apiserver/plane/app/views/external.py index 448234fc1..97d509c1e 100644 --- a/apiserver/plane/app/views/external.py +++ b/apiserver/plane/app/views/external.py @@ -89,16 +89,16 @@ class ReleaseNotesEndpoint(BaseAPIView): class UnsplashEndpoint(BaseAPIView): def get(self, request): - UNSPLASH_ACESS_KEY = get_configuration_value( + UNSPLASH_ACCESS_KEY, = get_configuration_value( [ { - "key": "UNSPLASH_ACESS_KEY", + "key": "UNSPLASH_ACCESS_KEY", "default": os.environ.get("UNSPLASH_ACCESS_KEY"), } ] ) # Check unsplash access key - if not UNSPLASH_ACESS_KEY: + if not UNSPLASH_ACCESS_KEY: return Response([], status=status.HTTP_200_OK) # Query parameters @@ -107,9 +107,9 @@ class UnsplashEndpoint(BaseAPIView): per_page = request.GET.get("per_page", 20) url = ( - f"https://api.unsplash.com/search/photos/?client_id={UNSPLASH_ACESS_KEY}&query={query}&page=${page}&per_page={per_page}" + f"https://api.unsplash.com/search/photos/?client_id={UNSPLASH_ACCESS_KEY}&query={query}&page=${page}&per_page={per_page}" if query - else f"https://api.unsplash.com/photos/?client_id={UNSPLASH_ACESS_KEY}&page={page}&per_page={per_page}" + else f"https://api.unsplash.com/photos/?client_id={UNSPLASH_ACCESS_KEY}&page={page}&per_page={per_page}" ) headers = { diff --git a/apiserver/plane/app/views/oauth.py b/apiserver/plane/app/views/oauth.py index 0ffb76db2..e12cba2ae 100644 --- a/apiserver/plane/app/views/oauth.py +++ b/apiserver/plane/app/views/oauth.py @@ -280,16 +280,15 @@ class OauthEndpoint(BaseAPIView): ) # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium=medium.upper(), - first_time=False, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium=medium.upper(), + first_time=False, + ) access_token, refresh_token = get_tokens_for_user(user) @@ -300,7 +299,7 @@ class OauthEndpoint(BaseAPIView): return Response(data, status=status.HTTP_200_OK) except User.DoesNotExist: - ENABLE_SIGNUP = get_configuration_value( + ENABLE_SIGNUP, = get_configuration_value( [ { "key": "ENABLE_SIGNUP", @@ -412,16 +411,15 @@ class OauthEndpoint(BaseAPIView): project_member_invites.delete() # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - auth_events.delay( - user=user.id, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="SIGN_IN", - medium=medium.upper(), - first_time=True, - ) + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium=medium.upper(), + first_time=True, + ) SocialLoginConnection.objects.update_or_create( medium=medium, diff --git a/apiserver/plane/app/views/workspace.py b/apiserver/plane/app/views/workspace.py index 5e50f28f0..ed72dbcf1 100644 --- a/apiserver/plane/app/views/workspace.py +++ b/apiserver/plane/app/views/workspace.py @@ -408,15 +408,14 @@ class WorkspaceJoinEndpoint(BaseAPIView): workspace_invite.delete() # Send event - if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: - workspace_invite_event.delay( - user=user.id if user is not None else None, - email=email, - user_agent=request.META.get("HTTP_USER_AGENT"), - ip=request.META.get("REMOTE_ADDR"), - event_name="MEMBER_ACCEPTED", - accepted_from="EMAIL", - ) + workspace_invite_event.delay( + user=user.id if user is not None else None, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="MEMBER_ACCEPTED", + accepted_from="EMAIL", + ) return Response( {"message": "Workspace Invitation Accepted"}, diff --git a/apiserver/plane/bgtasks/event_tracking_task.py b/apiserver/plane/bgtasks/event_tracking_task.py index 25479d3ee..7d26dd4ab 100644 --- a/apiserver/plane/bgtasks/event_tracking_task.py +++ b/apiserver/plane/bgtasks/event_tracking_task.py @@ -1,50 +1,78 @@ import uuid +import os -from posthog import Posthog -from django.conf import settings - -#third party imports +# third party imports from celery import shared_task from sentry_sdk import capture_exception +from posthog import Posthog + +# module imports +from plane.license.utils.instance_value import get_configuration_value + + +def posthogConfiguration(): + POSTHOG_API_KEY, POSTHOG_HOST = get_configuration_value( + [ + { + "key": "POSTHOG_API_KEY", + "default": os.environ.get("POSTHOG_API_KEY", None), + }, + { + "key": "POSTHOG_HOST", + "default": os.environ.get("POSTHOG_HOST", None), + }, + ] + ) + if POSTHOG_API_KEY and POSTHOG_HOST: + return POSTHOG_API_KEY, POSTHOG_HOST + else: + return None, None @shared_task def auth_events(user, email, user_agent, ip, event_name, medium, first_time): try: - posthog = Posthog(settings.POSTHOG_API_KEY, host=settings.POSTHOG_HOST) - posthog.capture( - email, - event=event_name, - properties={ - "event_id": uuid.uuid4().hex, - "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, - "medium": medium, - "first_time": first_time - } - ) + POSTHOG_API_KEY, POSTHOG_HOST = posthogConfiguration() + + if POSTHOG_API_KEY and POSTHOG_HOST: + posthog = Posthog(POSTHOG_API_KEY, host=POSTHOG_HOST) + posthog.capture( + email, + event=event_name, + properties={ + "event_id": uuid.uuid4().hex, + "user": {"email": email, "id": str(user)}, + "device_ctx": { + "ip": ip, + "user_agent": user_agent, + }, + "medium": medium, + "first_time": first_time + } + ) except Exception as e: capture_exception(e) + @shared_task def workspace_invite_event(user, email, user_agent, ip, event_name, accepted_from): try: - posthog = Posthog(settings.POSTHOG_API_KEY, host=settings.POSTHOG_HOST) - posthog.capture( - email, - event=event_name, - properties={ - "event_id": uuid.uuid4().hex, - "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, - "accepted_from": accepted_from - } - ) + POSTHOG_API_KEY, POSTHOG_HOST = posthogConfiguration() + + if POSTHOG_API_KEY and POSTHOG_HOST: + posthog = Posthog(POSTHOG_API_KEY, host=POSTHOG_HOST) + posthog.capture( + email, + event=event_name, + properties={ + "event_id": uuid.uuid4().hex, + "user": {"email": email, "id": str(user)}, + "device_ctx": { + "ip": ip, + "user_agent": user_agent, + }, + "accepted_from": accepted_from + } + ) except Exception as e: capture_exception(e) \ No newline at end of file From 279044e895213727577c437e7e56c5f458d6f3e3 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 12:51:10 +0530 Subject: [PATCH 06/11] chore: cleanup --- apiserver/plane/license/utils/instance_value.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index 53481108a..92fc6e6ac 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -9,12 +9,6 @@ from plane.license.models import InstanceConfiguration from plane.license.utils.encryption import decrypt_data - -def create_lookup_dict(list_of_dicts): - return {item['key']: item for item in list_of_dicts} - - - # Helper function to return value from the passed key def get_configuration_value(keys): environment_list = [] From 1fd7372c1852cbb32d72f162880a2f119a4a2645 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 13:58:57 +0530 Subject: [PATCH 07/11] chore: added SKIP_ENV_VAR --- .../plane/license/utils/instance_value.py | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index 92fc6e6ac..58a16b4f4 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -29,20 +29,17 @@ def get_configuration_value(keys): break else: environment_list.append(key.get("default")) + else: + # Get the configuration from os + for key in keys: + environment_list.append(os.environ.get(key.get("key"), key.get("default"))) return tuple(environment_list) def get_email_configuration(): - if settings.SKIP_ENV_VAR: - ( - EMAIL_HOST_USER, - EMAIL_HOST_PASSWORD, - EMAIL_HOST, - EMAIL_FROM, - EMAIL_USE_TLS, - EMAIL_PORT, - ) = get_configuration_value( + return ( + get_configuration_value( [ { "key": "EMAIL_HOST_USER", @@ -58,7 +55,7 @@ def get_email_configuration(): }, { "key": "EMAIL_FROM", - "default": os.environ.get("EMAIL_FROM", None), + "default": os.environ.get("EMAIL_FROM", "Team Plane "), }, { "key": "EMAIL_USE_TLS", @@ -70,29 +67,5 @@ def get_email_configuration(): }, ] ) - return ( - EMAIL_HOST, - EMAIL_HOST_USER, - EMAIL_HOST_PASSWORD, - EMAIL_PORT, - EMAIL_USE_TLS, - EMAIL_FROM, - ) + ) - else: - # Get email configuration directly from os - EMAIL_HOST = os.environ.get("EMAIL_HOST") - EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") - EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD") - EMAIL_PORT = os.environ.get("EMAIL_PORT", 587) - EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "1") - EMAIL_FROM = os.environ.get("EMAIL_FROM", "Team Plane ") - - return ( - EMAIL_HOST, - EMAIL_HOST_USER, - EMAIL_HOST_PASSWORD, - EMAIL_PORT, - EMAIL_USE_TLS, - EMAIL_FROM, - ) From bd25013feefb9c2a19c3a821ea24be47bdf7ed99 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 14:07:52 +0530 Subject: [PATCH 08/11] chore: changed the EMAIL_FROM --- apiserver/plane/license/utils/instance_value.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index 58a16b4f4..f26724183 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -54,16 +54,16 @@ def get_email_configuration(): "default": os.environ.get("EMAIL_HOST", None), }, { - "key": "EMAIL_FROM", - "default": os.environ.get("EMAIL_FROM", "Team Plane "), + "key": "EMAIL_PORT", + "default": os.environ.get("EMAIL_PORT", 587), }, { "key": "EMAIL_USE_TLS", "default": os.environ.get("EMAIL_USE_TLS", "1"), }, { - "key": "EMAIL_PORT", - "default": os.environ.get("EMAIL_PORT", 587), + "key": "EMAIL_FROM", + "default": os.environ.get("EMAIL_FROM", "Team Plane "), }, ] ) From 99aab52c46db3c8f6c15ad12c6be43962d63f079 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Thu, 7 Dec 2023 14:21:28 +0530 Subject: [PATCH 09/11] dev: patch endpoint for workspace --- apiserver/plane/app/serializers/workspace.py | 10 ++++++++++ apiserver/plane/app/urls/workspace.py | 1 + 2 files changed, 11 insertions(+) diff --git a/apiserver/plane/app/serializers/workspace.py b/apiserver/plane/app/serializers/workspace.py index 48a4bc44e..f0ad4b4ab 100644 --- a/apiserver/plane/app/serializers/workspace.py +++ b/apiserver/plane/app/serializers/workspace.py @@ -95,6 +95,16 @@ class WorkSpaceMemberInviteSerializer(BaseSerializer): class Meta: model = WorkspaceMemberInvite fields = "__all__" + read_only_fields = [ + "id", + "email", + "token", + "workspace", + "message", + "responded_at", + "created_at", + "updated_at", + ] class TeamSerializer(BaseSerializer): diff --git a/apiserver/plane/app/urls/workspace.py b/apiserver/plane/app/urls/workspace.py index 739d17c55..2c3638842 100644 --- a/apiserver/plane/app/urls/workspace.py +++ b/apiserver/plane/app/urls/workspace.py @@ -65,6 +65,7 @@ urlpatterns = [ { "delete": "destroy", "get": "retrieve", + "patch": "partial_update", } ), name="workspace-invitations", From 30e764e917876fa74858670dffab104e686e1ee2 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Thu, 7 Dec 2023 14:21:45 +0530 Subject: [PATCH 10/11] dev: custom port for takeoff script --- apiserver/bin/takeoff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/bin/takeoff b/apiserver/bin/takeoff index 891ec1472..0ec2e495c 100755 --- a/apiserver/bin/takeoff +++ b/apiserver/bin/takeoff @@ -27,4 +27,4 @@ python manage.py configure_instance # Create the default bucket python manage.py create_bucket -exec gunicorn -w $GUNICORN_WORKERS -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:8000 --max-requests 1200 --max-requests-jitter 1000 --access-logfile - +exec gunicorn -w $GUNICORN_WORKERS -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:${PORT:-8000} --max-requests 1200 --max-requests-jitter 1000 --access-logfile - From 056b12d7d75f36f5ca8d0d60d34de47e8afc02e5 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 7 Dec 2023 14:29:43 +0530 Subject: [PATCH 11/11] chore: changed my sequence --- apiserver/plane/license/utils/instance_value.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index f26724183..e56525893 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -41,17 +41,17 @@ def get_email_configuration(): return ( get_configuration_value( [ + { + "key": "EMAIL_HOST", + "default": os.environ.get("EMAIL_HOST"), + }, { "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER", None), + "default": os.environ.get("EMAIL_HOST_USER"), }, { "key": "EMAIL_HOST_PASSWORD", - "default": os.environ.get("EMAIL_HOST_PASSWORD", None), - }, - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST", None), + "default": os.environ.get("EMAIL_HOST_PASSWORD"), }, { "key": "EMAIL_PORT",