diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py index 1d65f3d681..237d8d6bf8 100644 --- a/apiserver/plane/api/views/config.py +++ b/apiserver/plane/api/views/config.py @@ -12,8 +12,9 @@ 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 +from plane.license.models import Instance, InstanceConfiguration +from plane.license.utils.instance_value import get_configuration_value + class ConfigurationEndpoint(BaseAPIView): permission_classes = [ @@ -21,27 +22,75 @@ class ConfigurationEndpoint(BaseAPIView): ] def get(self, request): - instance_configuration = Instance.objects.values("key", "value") + instance_configuration = InstanceConfiguration.objects.values("key", "value") data = {} # 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["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["magic_login"] = ( - 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" + bool( + get_configuration_value( + instance_configuration, + "EMAIL_HOST_USER", + os.environ.get("GITHUB_APP_NAME", None), + ), + ) + and bool( + get_configuration_value( + instance_configuration, + "EMAIL_HOST_PASSWORD", + os.environ.get("GITHUB_APP_NAME", None), + ) + ) + ) and get_configuration_value( + instance_configuration, "ENABLE_MAGIC_LINK_LOGIN", "0" + ) == "1" data["email_password_login"] = ( - get_configuration_value(instance_configuration, "ENABLE_EMAIL_PASSWORD", "0") == "1" + get_configuration_value( + instance_configuration, "ENABLE_EMAIL_PASSWORD", "0" + ) + == "1" ) # Slack client - data["slack_client_id"] = get_configuration_value(instance_configuration, "SLACK_CLIENT_ID") - + data["slack_client_id"] = get_configuration_value( + instance_configuration, + "SLACK_CLIENT_ID", + os.environ.get("SLACK_CLIENT_ID", None), + ) + # Posthog - data["posthog_api_key"] = get_configuration_value(instance_configuration, "POSTHOG_API_KEY") - data["posthog_host"] = get_configuration_value(instance_configuration, "POSTHOG_HOST") + 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), + ) # Unsplash - data["has_unsplash_configured"] = bool(get_configuration_value(instance_configuration, "UNSPLASH_ACCESS_KEY")) + data["has_unsplash_configured"] = bool( + get_configuration_value( + instance_configuration, + "UNSPLASH_ACCESS_KEY", + os.environ.get("UNSPLASH_ACCESS_KEY", None), + ) + ) return Response(data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index 90bbed4afb..477c7f6fa1 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -19,7 +19,6 @@ from plane.license.utils.instance_value import get_configuration_value @shared_task def workspace_invitation(email, workspace_id, token, current_site, invitor): try: - user = User.objects.get(email=invitor) workspace = Workspace.objects.get(pk=workspace_id) @@ -28,9 +27,7 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): ) # Relative link - relative_link = ( - f"/workspace-invitations/?invitation_id={workspace_member_invite.id}&email={email}&slug={workspace.slug}" - ) + relative_link = f"/workspace-invitations/?invitation_id={workspace_member_invite.id}&email={email}&slug={workspace.slug}" # The complete url including the domain abs_url = current_site + relative_link @@ -57,17 +54,33 @@ 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") + instance_configuration = InstanceConfiguration.objects.filter( + key__startswith="EMAIL_" + ).values("key", "value") connection = get_connection( host=get_configuration_value(instance_configuration, "EMAIL_HOST"), - port=int(get_configuration_value(instance_configuration, "EMAIL_PORT", "587")), + 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")), + 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, body=text_content, from_email=get_configuration_value(instance_configuration, "EMAIL_FROM"), to=[email], connection=connection) + msg = EmailMultiAlternatives( + subject=subject, + body=text_content, + from_email=get_configuration_value(instance_configuration, "EMAIL_FROM"), + to=[email], + connection=connection, + ) msg.attach_alternative(html_content, "text/html") msg.send() diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 856d2d37ed..c3d1c6adbb 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -5,6 +5,7 @@ import ssl import certifi from datetime import timedelta from urllib.parse import urlparse + # Django imports from django.core.management.utils import get_random_secret_key @@ -236,7 +237,6 @@ if AWS_S3_ENDPOINT_URL: AWS_S3_URL_PROTOCOL = f"{parsed_url.scheme}:" - # JWT Auth Configuration SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": timedelta(minutes=10080), @@ -332,4 +332,3 @@ SCOUT_NAME = "Plane" # Set the variable true if running in docker environment DOCKERIZED = int(os.environ.get("DOCKERIZED", 1)) == 1 USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1 - diff --git a/web/layouts/admin-layout/header.tsx b/web/layouts/admin-layout/header.tsx new file mode 100644 index 0000000000..f2a3e22663 --- /dev/null +++ b/web/layouts/admin-layout/header.tsx @@ -0,0 +1,21 @@ +import { FC } from "react"; +// ui +import { Breadcrumbs } from "@plane/ui"; +// icons +import { Settings } from "lucide-react"; + +export const InstanceAdminHeader: FC = () => ( +
+
+
+ + } + label="General" + /> + +
+
+
+); diff --git a/web/layouts/admin-layout/index.ts b/web/layouts/admin-layout/index.ts new file mode 100644 index 0000000000..8a235ad000 --- /dev/null +++ b/web/layouts/admin-layout/index.ts @@ -0,0 +1,3 @@ +export * from "./layout"; +export * from "./sidebar"; +export * from "./header"; diff --git a/web/layouts/admin-layout/layout.tsx b/web/layouts/admin-layout/layout.tsx new file mode 100644 index 0000000000..1a1dbfa639 --- /dev/null +++ b/web/layouts/admin-layout/layout.tsx @@ -0,0 +1,32 @@ +import { FC, ReactNode } from "react"; +// layouts +import { UserAuthWrapper } from "layouts/auth-layout"; +// components +import { InstanceAdminSidebar } from "./sidebar"; +import { InstanceAdminHeader } from "./header"; + +export interface IInstanceAdminLayout { + children: ReactNode; +} + +export const InstanceAdminLayout: FC = (props) => { + const { children } = props; + + return ( + <> + +
+ +
+ +
+
+ <>{children} +
+
+
+
+
+ + ); +}; diff --git a/web/layouts/admin-layout/sidebar.tsx b/web/layouts/admin-layout/sidebar.tsx new file mode 100644 index 0000000000..0960389d47 --- /dev/null +++ b/web/layouts/admin-layout/sidebar.tsx @@ -0,0 +1,26 @@ +import { FC } from "react"; +import { observer } from "mobx-react-lite"; +// components +import { WorkspaceHelpSection } from "components/workspace"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; + +export interface IAppSidebar {} + +export const InstanceAdminSidebar: FC = observer(() => { + // store + const { theme: themStore } = useMobxStore(); + + return ( +
+
+ +
+
+ ); +}); diff --git a/web/pages/admin/index.tsx b/web/pages/admin/index.tsx new file mode 100644 index 0000000000..49133ecd5e --- /dev/null +++ b/web/pages/admin/index.tsx @@ -0,0 +1,16 @@ +import { ReactElement } from "react"; +// layouts +import { InstanceAdminLayout } from "layouts/admin-layout"; +// types +import { NextPageWithLayout } from "types/app"; + +const InstanceAdminPage: NextPageWithLayout = () => { + console.log("admin page"); + return
Admin Page
; +}; + +InstanceAdminPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default InstanceAdminPage;