From 69b1d0a9292db24066bbb7c7db5cf68bde6c090f Mon Sep 17 00:00:00 2001 From: Jigin Jayaprakash <145117767+JiginJayaprakash@users.noreply.github.com> Date: Thu, 4 Jan 2024 17:26:14 +0530 Subject: [PATCH 01/17] Fixes 3299 (#3308) --- apiserver/.env.example | 2 -- 1 file changed, 2 deletions(-) diff --git a/apiserver/.env.example b/apiserver/.env.example index 37178b398..3ac9a3aeb 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -39,8 +39,6 @@ OPENAI_API_BASE="https://api.openai.com/v1" # deprecated OPENAI_API_KEY="sk-" # deprecated GPT_ENGINE="gpt-3.5-turbo" # deprecated -# Github -GITHUB_CLIENT_SECRET="" # For fetching release notes # Settings related to Docker DOCKERIZED=1 # deprecated From 5cd93f5e59b8937206662a9f0dd6ff1441e411f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:45:10 +0530 Subject: [PATCH 02/17] chore(deps): bump tj-actions/changed-files in /.github/workflows (#3327) Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 38 to 41. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/v38...v41) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-test-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test-pull-request.yml b/.github/workflows/build-test-pull-request.yml index fd5d5ad03..296e965d7 100644 --- a/.github/workflows/build-test-pull-request.yml +++ b/.github/workflows/build-test-pull-request.yml @@ -25,7 +25,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v38 + uses: tj-actions/changed-files@v41 with: files_yaml: | apiserver: From 02a776396be3169c883df18fd82890cd9d6e442d Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:26:32 +0530 Subject: [PATCH 03/17] fix: security warnings related to information exposure and regex validations (#3325) --- apiserver/plane/api/views/base.py | 5 ++--- apiserver/plane/app/views/base.py | 10 ++++------ apiserver/plane/space/views/base.py | 9 ++++----- apiserver/plane/utils/issue_search.py | 4 ++-- apiserver/plane/utils/paginator.py | 2 +- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/apiserver/plane/api/views/base.py b/apiserver/plane/api/views/base.py index abde4e8b0..035266bd5 100644 --- a/apiserver/plane/api/views/base.py +++ b/apiserver/plane/api/views/base.py @@ -104,15 +104,14 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): ) if isinstance(e, ObjectDoesNotExist): - model_name = str(exc).split(" matching query does not exist.")[0] return Response( - {"error": f"{model_name} does not exist."}, + {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) if isinstance(e, KeyError): return Response( - {"error": f"key {e} does not exist"}, + {"error": f" The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/base.py b/apiserver/plane/app/views/base.py index 32449597b..aeb7c015e 100644 --- a/apiserver/plane/app/views/base.py +++ b/apiserver/plane/app/views/base.py @@ -112,16 +112,15 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): ) if isinstance(e, ObjectDoesNotExist): - model_name = str(exc).split(" matching query does not exist.")[0] return Response( - {"error": f"{model_name} does not exist."}, + {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) if isinstance(e, KeyError): capture_exception(e) return Response( - {"error": f"key {e} does not exist"}, + {"error": f"The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST, ) @@ -201,14 +200,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): ) if isinstance(e, ObjectDoesNotExist): - model_name = str(exc).split(" matching query does not exist.")[0] return Response( - {"error": f"{model_name} does not exist."}, + {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) if isinstance(e, KeyError): - return Response({"error": f"key {e} does not exist"}, status=status.HTTP_400_BAD_REQUEST) + return Response({"error": f"The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST) if settings.DEBUG: print(e) diff --git a/apiserver/plane/space/views/base.py b/apiserver/plane/space/views/base.py index b1d749a09..7a819095b 100644 --- a/apiserver/plane/space/views/base.py +++ b/apiserver/plane/space/views/base.py @@ -85,14 +85,14 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): if isinstance(e, ObjectDoesNotExist): model_name = str(exc).split(" matching query does not exist.")[0] return Response( - {"error": f"{model_name} does not exist."}, + {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) if isinstance(e, KeyError): capture_exception(e) return Response( - {"error": f"key {e} does not exist"}, + {"error": "The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST, ) @@ -172,14 +172,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): ) if isinstance(e, ObjectDoesNotExist): - model_name = str(exc).split(" matching query does not exist.")[0] return Response( - {"error": f"{model_name} does not exist."}, + {"error": f"The required object does not exist."}, status=status.HTTP_404_NOT_FOUND, ) if isinstance(e, KeyError): - return Response({"error": f"key {e} does not exist"}, status=status.HTTP_400_BAD_REQUEST) + return Response({"error": "The required key does not exist."}, status=status.HTTP_400_BAD_REQUEST) if settings.DEBUG: print(e) diff --git a/apiserver/plane/utils/issue_search.py b/apiserver/plane/utils/issue_search.py index 40f85dde4..d38b1f4c3 100644 --- a/apiserver/plane/utils/issue_search.py +++ b/apiserver/plane/utils/issue_search.py @@ -12,8 +12,8 @@ def search_issues(query, queryset): fields = ["name", "sequence_id"] q = Q() for field in fields: - if field == "sequence_id": - sequences = re.findall(r"\d+\.\d+|\d+", query) + if field == "sequence_id" and len(query) <= 20: + sequences = re.findall(r"[A-Za-z0-9]{1,12}-\d+", query) for sequence_id in sequences: q |= Q(**{"sequence_id": sequence_id}) else: diff --git a/apiserver/plane/utils/paginator.py b/apiserver/plane/utils/paginator.py index 793614cc0..3563dad34 100644 --- a/apiserver/plane/utils/paginator.py +++ b/apiserver/plane/utils/paginator.py @@ -188,7 +188,7 @@ class BasePaginator: try: cursor_result = paginator.get_result(limit=per_page, cursor=input_cursor) except BadPaginationError as e: - raise ParseError(detail=str(e)) + raise ParseError(detail="Error in parsing") # Serialize result according to the on_result function if on_results: From 4b0ccea1461b7ca38761dfe0d0f07c2f94425005 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:27:09 +0530 Subject: [PATCH 04/17] fix: jira importer validations (#3323) * fix: jira importer validations * dev: update validation for cloud hostname * dev: update the function to be used externally * dev: update codeql workflow * dev: update repository selection api --- .github/workflows/codeql.yml | 4 +-- apiserver/plane/app/views/importer.py | 31 ++++++++++++------- apiserver/plane/utils/importers/jira.py | 22 +++++++++++-- .../integration/single-integration-card.tsx | 2 +- web/services/project/project.service.ts | 6 +++- 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 29fbde453..9f6ab1bfb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,10 +2,10 @@ name: "CodeQL" on: push: - branches: [ 'develop', 'hot-fix', 'stage-release' ] + branches: [ 'develop', 'preview', 'master' ] pull_request: # The branches below must be a subset of the branches above - branches: [ 'develop' ] + branches: [ 'develop', 'preview', 'master' ] schedule: - cron: '53 19 * * 5' diff --git a/apiserver/plane/app/views/importer.py b/apiserver/plane/app/views/importer.py index b99d663e2..00d698ac5 100644 --- a/apiserver/plane/app/views/importer.py +++ b/apiserver/plane/app/views/importer.py @@ -35,14 +35,13 @@ from plane.app.serializers import ( ModuleSerializer, ) from plane.utils.integrations.github import get_github_repo_details -from plane.utils.importers.jira import jira_project_issue_summary +from plane.utils.importers.jira import jira_project_issue_summary, is_allowed_hostname from plane.bgtasks.importer_task import service_importer from plane.utils.html_processor import strip_tags from plane.app.permissions import WorkSpaceAdminPermission class ServiceIssueImportSummaryEndpoint(BaseAPIView): - def get(self, request, slug, service): if service == "github": owner = request.GET.get("owner", False) @@ -122,6 +121,7 @@ class ImportServiceEndpoint(BaseAPIView): permission_classes = [ WorkSpaceAdminPermission, ] + def post(self, request, slug, service): project_id = request.data.get("project_id", False) @@ -174,6 +174,21 @@ class ImportServiceEndpoint(BaseAPIView): data = request.data.get("data", False) metadata = request.data.get("metadata", False) config = request.data.get("config", False) + + cloud_hostname = metadata.get("cloud_hostname", False) + + if not cloud_hostname: + return Response( + {"error": "Cloud hostname is required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not is_allowed_hostname(cloud_hostname): + return Response( + {"error": "Hostname is not a valid hostname."}, + status=status.HTTP_400_BAD_REQUEST, + ) + if not data or not metadata: return Response( {"error": "Data, config and metadata are required"}, @@ -221,9 +236,7 @@ class ImportServiceEndpoint(BaseAPIView): return Response(serializer.data) def delete(self, request, slug, service, pk): - importer = Importer.objects.get( - pk=pk, service=service, workspace__slug=slug - ) + importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug) if importer.imported_data is not None: # Delete all imported Issues @@ -241,9 +254,7 @@ class ImportServiceEndpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) def patch(self, request, slug, service, pk): - importer = Importer.objects.get( - pk=pk, service=service, workspace__slug=slug - ) + importer = Importer.objects.get(pk=pk, service=service, workspace__slug=slug) serializer = ImporterSerializer(importer, data=request.data, partial=True) if serializer.is_valid(): serializer.save() @@ -479,9 +490,7 @@ class BulkImportModulesEndpoint(BaseAPIView): [ ModuleLink( module=module, - url=module_data.get("link", {}).get( - "url", "https://plane.so" - ), + url=module_data.get("link", {}).get("url", "https://plane.so"), title=module_data.get("link", {}).get( "title", "Original Issue" ), diff --git a/apiserver/plane/utils/importers/jira.py b/apiserver/plane/utils/importers/jira.py index b427ba14f..3081096fe 100644 --- a/apiserver/plane/utils/importers/jira.py +++ b/apiserver/plane/utils/importers/jira.py @@ -2,13 +2,31 @@ import requests from requests.auth import HTTPBasicAuth from sentry_sdk import capture_exception +from urllib.parse import urlparse + +def is_allowed_hostname(hostname): + allowed_lists = ["atl-paas.net", "atlassian.com", "atlassian.net", "jira.com"] + # Extract the base domain from the hostname + parsed_uri = urlparse(f"https://{hostname}") # Add scheme for urlparse to work properly + domain = parsed_uri.netloc.split(":")[0] # Removes port number if included + base_domain = ".".join(domain.split(".")[-2:]) # Extract base domain + + # Check if the base domain is in the allowed list + return base_domain in allowed_lists + def jira_project_issue_summary(email, api_token, project_key, hostname): try: + + + if not is_allowed_hostname(hostname): + print("Errored Hostname") + return {"error": "Invalid or unauthorized hostname"} + auth = HTTPBasicAuth(email, api_token) headers = {"Accept": "application/json"} - issue_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype=Story" + issue_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype!=Epic" issue_response = requests.request( "GET", issue_url, headers=headers, auth=auth ).json()["total"] @@ -18,7 +36,7 @@ def jira_project_issue_summary(email, api_token, project_key, hostname): "GET", module_url, headers=headers, auth=auth ).json()["total"] - status_url = f"https://{hostname}/rest/api/3/status/?jql=project={project_key}" + status_url = f"https://{hostname}/rest/api/3/project/${project_key}/statuses" status_response = requests.request( "GET", status_url, headers=headers, auth=auth ).json() diff --git a/web/components/integration/single-integration-card.tsx b/web/components/integration/single-integration-card.tsx index e07f580e7..6692bacd6 100644 --- a/web/components/integration/single-integration-card.tsx +++ b/web/components/integration/single-integration-card.tsx @@ -139,7 +139,7 @@ export const SingleIntegrationCard: React.FC = observer(({ integration }) variant="danger" onClick={() => { if (!isUserAdmin) return; - handleRemoveIntegration; + handleRemoveIntegration(); }} disabled={!isUserAdmin} loading={deletingIntegration} diff --git a/web/services/project/project.service.ts b/web/services/project/project.service.ts index 7e8821cf5..501abe676 100644 --- a/web/services/project/project.service.ts +++ b/web/services/project/project.service.ts @@ -86,7 +86,11 @@ export class ProjectService extends APIService { } async getGithubRepositories(url: string): Promise { - return this.request(url) + return this.request({ + method: "get", + url, + headers: this.getAccessToken() ? this.getHeaders() : {}, + }) .then((response) => response?.data) .catch((error) => { throw error?.response?.data; From d887b780aea5efba3f3d28c47d7d83f8b3e1e21c Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:40:23 +0530 Subject: [PATCH 05/17] fix: update jira summary endpoints (#3333) * dev: update jira summary endpoints * dev: update jira project key validations * dev: updated key length --- apiserver/plane/utils/importers/jira.py | 70 +++++++++++++++++++------ 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/apiserver/plane/utils/importers/jira.py b/apiserver/plane/utils/importers/jira.py index 3081096fe..5e8c31f97 100644 --- a/apiserver/plane/utils/importers/jira.py +++ b/apiserver/plane/utils/importers/jira.py @@ -1,53 +1,89 @@ import requests +import re from requests.auth import HTTPBasicAuth from sentry_sdk import capture_exception +from urllib.parse import urlparse, urljoin -from urllib.parse import urlparse def is_allowed_hostname(hostname): - allowed_lists = ["atl-paas.net", "atlassian.com", "atlassian.net", "jira.com"] - # Extract the base domain from the hostname - parsed_uri = urlparse(f"https://{hostname}") # Add scheme for urlparse to work properly - domain = parsed_uri.netloc.split(":")[0] # Removes port number if included - base_domain = ".".join(domain.split(".")[-2:]) # Extract base domain + allowed_domains = ["atl-paas.net", "atlassian.com", "atlassian.net", "jira.com"] + parsed_uri = urlparse(f"https://{hostname}") + domain = parsed_uri.netloc.split(":")[0] # Ensures no port is included + base_domain = ".".join(domain.split(".")[-2:]) + return base_domain in allowed_domains - # Check if the base domain is in the allowed list - return base_domain in allowed_lists + +def is_valid_project_key(project_key): + if project_key: + project_key = project_key.strip().upper() + # Adjust the regular expression as needed based on your specific requirements. + if len(project_key) > 30: + return False + # Check the validity of the key as well + pattern = re.compile(r'^[A-Z0-9]{1,10}$') + return pattern.match(project_key) is not None + else: + False + +def generate_valid_project_key(project_key): + return project_key.strip().upper() + +def generate_url(hostname, path): + if not is_allowed_hostname(hostname): + raise ValueError("Invalid or unauthorized hostname") + return urljoin(f"https://{hostname}", path) def jira_project_issue_summary(email, api_token, project_key, hostname): try: - - if not is_allowed_hostname(hostname): - print("Errored Hostname") return {"error": "Invalid or unauthorized hostname"} + if not is_valid_project_key(project_key): + return {"error": "Invalid project key"} + auth = HTTPBasicAuth(email, api_token) headers = {"Accept": "application/json"} + + # make the project key upper case + project_key = generate_valid_project_key(project_key) - issue_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype!=Epic" + # issues + issue_url = generate_url( + hostname, + f"/rest/api/3/search?jql=project={project_key} AND issuetype!=Epic", + ) issue_response = requests.request( "GET", issue_url, headers=headers, auth=auth ).json()["total"] - module_url = f"https://{hostname}/rest/api/3/search?jql=project={project_key} AND issuetype=Epic" + # modules + module_url = generate_url( + hostname, f"/rest/api/3/search?jql=project={project_key} AND issuetype=Epic" + ) module_response = requests.request( "GET", module_url, headers=headers, auth=auth ).json()["total"] - status_url = f"https://{hostname}/rest/api/3/project/${project_key}/statuses" + # status + status_url = generate_url( + hostname, f"/rest/api/3/project/${project_key}/statuses" + ) status_response = requests.request( "GET", status_url, headers=headers, auth=auth ).json() - labels_url = f"https://{hostname}/rest/api/3/label/?jql=project={project_key}" + # labels + labels_url = generate_url( + hostname, f"/rest/api/3/label/?jql=project={project_key}" + ) labels_response = requests.request( "GET", labels_url, headers=headers, auth=auth ).json()["total"] - users_url = ( - f"https://{hostname}/rest/api/3/users/search?jql=project={project_key}" + # users + users_url = generate_url( + hostname, f"/rest/api/3/users/search?jql=project={project_key}" ) users_response = requests.request( "GET", users_url, headers=headers, auth=auth From 2580e66d4b887f9a11b7b13c98edd92be7f2be11 Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:50:51 +0530 Subject: [PATCH 06/17] fixes web container public assets (#3336) --- web/Dockerfile.web | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/web/Dockerfile.web b/web/Dockerfile.web index d9260e61d..e0d525c2c 100644 --- a/web/Dockerfile.web +++ b/web/Dockerfile.web @@ -1,3 +1,6 @@ +# ****************************************** +# STAGE 1: Build the project +# ****************************************** FROM node:18-alpine AS builder RUN apk add --no-cache libc6-compat # Set working directory @@ -8,6 +11,10 @@ COPY . . RUN turbo prune --scope=web --docker + +# ****************************************** +# STAGE 2: Install dependencies & build the project +# ****************************************** # Add lockfile and package.json's of isolated subworkspace FROM node:18-alpine AS installer @@ -31,6 +38,11 @@ ENV NEXT_PUBLIC_DEPLOY_URL=$NEXT_PUBLIC_DEPLOY_URL RUN yarn turbo run build --filter=web + +# ****************************************** +# STAGE 3: Copy the project and start it +# ****************************************** + FROM node:18-alpine AS runner WORKDIR /app @@ -46,6 +58,7 @@ COPY --from=installer /app/web/package.json . # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=installer --chown=captain:plane /app/web/.next/standalone ./ COPY --from=installer --chown=captain:plane /app/web/.next ./web/.next +COPY --from=installer --chown=captain:plane /app/web/public ./web/public ARG NEXT_PUBLIC_API_BASE_URL="" ARG NEXT_PUBLIC_DEPLOY_URL="" From a70f551d1791ad9d9eadd2f7fc03eac2a521db4c Mon Sep 17 00:00:00 2001 From: AbId KhAn Date: Wed, 10 Jan 2024 14:05:24 +0600 Subject: [PATCH 07/17] Fix env substitute issue in websocket docker setup (#3296) * fix websocket connection issue in docker makeplane/plane#3195 * fix websocket connection issue for local env with removing from the prod nginx conf template makeplane/plane#319 * fix env substitution issue of proxy_set_header makeplane/plane#3196 * review fixes --------- Co-authored-by: Manish Gupta --- docker-compose-local.yml | 3 --- nginx/env.sh | 2 ++ nginx/nginx.conf.dev | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose-local.yml b/docker-compose-local.yml index 4e1e3b39f..b0fb9da24 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -44,9 +44,6 @@ services: env_file: - .env environment: - POSTGRES_USER: ${PGUSER} - POSTGRES_DB: ${PGDATABASE} - POSTGRES_PASSWORD: ${PGPASSWORD} PGDATA: /var/lib/postgresql/data web: diff --git a/nginx/env.sh b/nginx/env.sh index 59e4a46a0..7db471eca 100644 --- a/nginx/env.sh +++ b/nginx/env.sh @@ -1,4 +1,6 @@ #!/bin/sh +export dollar="$" +export http_upgrade="http_upgrade" envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf exec nginx -g 'daemon off;' diff --git a/nginx/nginx.conf.dev b/nginx/nginx.conf.dev index 182fc4d83..f86c84aa8 100644 --- a/nginx/nginx.conf.dev +++ b/nginx/nginx.conf.dev @@ -19,7 +19,7 @@ http { location / { proxy_pass http://web:3000/; proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; + proxy_set_header Upgrade ${dollar}http_upgrade; proxy_set_header Connection "upgrade"; } From 942785b7c0e4da9b917f5bf175a7da3d09168dab Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:09:42 +0530 Subject: [PATCH 08/17] dev: remove slack ping (#3377) --- apiserver/plane/bgtasks/importer_task.py | 10 ------ apiserver/plane/bgtasks/user_welcome_task.py | 36 ------------------- .../bgtasks/workspace_invitation_task.py | 11 ------ apiserver/plane/db/models/user.py | 27 -------------- apiserver/plane/settings/common.py | 2 +- 5 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 apiserver/plane/bgtasks/user_welcome_task.py diff --git a/apiserver/plane/bgtasks/importer_task.py b/apiserver/plane/bgtasks/importer_task.py index 84d10ecd3..5fa44fefd 100644 --- a/apiserver/plane/bgtasks/importer_task.py +++ b/apiserver/plane/bgtasks/importer_task.py @@ -25,7 +25,6 @@ from plane.db.models import ( User, IssueProperty, ) -from plane.bgtasks.user_welcome_task import send_welcome_slack @shared_task @@ -55,15 +54,6 @@ def service_importer(service, importer_id): ignore_conflicts=True, ) - _ = [ - send_welcome_slack.delay( - str(user.id), - True, - f"{user.email} was imported to Plane from {service}", - ) - for user in new_users - ] - workspace_users = User.objects.filter( email__in=[ user.get("email").strip().lower() diff --git a/apiserver/plane/bgtasks/user_welcome_task.py b/apiserver/plane/bgtasks/user_welcome_task.py deleted file mode 100644 index 33f4b5686..000000000 --- a/apiserver/plane/bgtasks/user_welcome_task.py +++ /dev/null @@ -1,36 +0,0 @@ -# Django imports -from django.conf import settings - -# Third party imports -from celery import shared_task -from sentry_sdk import capture_exception -from slack_sdk import WebClient -from slack_sdk.errors import SlackApiError - -# Module imports -from plane.db.models import User - - -@shared_task -def send_welcome_slack(user_id, created, message): - try: - instance = User.objects.get(pk=user_id) - - if created and not instance.is_bot: - # Send message on slack as well - if settings.SLACK_BOT_TOKEN: - client = WebClient(token=settings.SLACK_BOT_TOKEN) - try: - _ = client.chat_postMessage( - channel="#trackers", - text=message, - ) - except SlackApiError as e: - print(f"Got an error: {e.response['error']}") - return - except Exception as e: - # Print logs if in DEBUG mode - if settings.DEBUG: - print(e) - capture_exception(e) - return diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index 7039cb875..b17c346fb 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -83,17 +83,6 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): msg.attach_alternative(html_content, "text/html") msg.send() - # Send message on slack as well - if settings.SLACK_BOT_TOKEN: - client = WebClient(token=settings.SLACK_BOT_TOKEN) - try: - _ = client.chat_postMessage( - channel="#trackers", - text=f"{workspace_member_invite.email} has been invited to {workspace.name} as a {workspace_member_invite.role}", - ) - except SlackApiError as e: - print(f"Got an error: {e.response['error']}") - return except (Workspace.DoesNotExist, WorkspaceMemberInvite.DoesNotExist) as e: print("Workspace or WorkspaceMember Invite Does not exists") diff --git a/apiserver/plane/db/models/user.py b/apiserver/plane/db/models/user.py index fe75a6a26..92f2fe839 100644 --- a/apiserver/plane/db/models/user.py +++ b/apiserver/plane/db/models/user.py @@ -6,16 +6,9 @@ import pytz # Django imports from django.db import models -from django.db.models.signals import post_save -from django.dispatch import receiver from django.contrib.auth.models import AbstractBaseUser, UserManager, PermissionsMixin from django.utils import timezone -from django.conf import settings -# Third party imports -from sentry_sdk import capture_exception -from slack_sdk import WebClient -from slack_sdk.errors import SlackApiError def get_default_onboarding(): @@ -122,23 +115,3 @@ class User(AbstractBaseUser, PermissionsMixin): self.is_staff = True super(User, self).save(*args, **kwargs) - - -@receiver(post_save, sender=User) -def send_welcome_slack(sender, instance, created, **kwargs): - try: - if created and not instance.is_bot: - # Send message on slack as well - if settings.SLACK_BOT_TOKEN: - client = WebClient(token=settings.SLACK_BOT_TOKEN) - try: - _ = client.chat_postMessage( - channel="#trackers", - text=f"New user {instance.email} has signed up and begun the onboarding journey.", - ) - except SlackApiError as e: - print(f"Got an error: {e.response['error']}") - return - except Exception as e: - capture_exception(e) - return diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 971ed5543..938910a33 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -310,7 +310,7 @@ if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get( # Application Envs PROXY_BASE_URL = os.environ.get("PROXY_BASE_URL", False) # For External -SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", False) + FILE_SIZE_LIMIT = int(os.environ.get("FILE_SIZE_LIMIT", 5242880)) # Unsplash Access key From c0cd201b7cbf6c2f1ca0452fe4e1c4cf6356010a Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:35:21 +0530 Subject: [PATCH 09/17] fix error meesage on successful error message (#3387) Co-authored-by: Rahul R --- .../issues/issue-layouts/roots/cycle-layout-root.tsx | 4 ++-- web/store/issues/project-issues/cycle/issue.store.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index f77dfbed4..d301c3395 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -47,8 +47,8 @@ export const CycleLayoutRoot: React.FC = observer(() => { const activeLayout = issueFilters?.displayFilters?.layout; - const cycleDetails = cycleId ? cycleStore.cycle_details[cycleId.toString()] : undefined; - const cycleStatus = cycleDetails?.status.toLocaleLowerCase() ?? "draft"; + const cycleDetails = cycleId ? cycleStore?.cycle_details?.[cycleId.toString()] : undefined; + const cycleStatus = cycleDetails?.status?.toLocaleLowerCase() ?? "draft"; return ( <> diff --git a/web/store/issues/project-issues/cycle/issue.store.ts b/web/store/issues/project-issues/cycle/issue.store.ts index b5c0088bd..048371a86 100644 --- a/web/store/issues/project-issues/cycle/issue.store.ts +++ b/web/store/issues/project-issues/cycle/issue.store.ts @@ -200,7 +200,7 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor try { const response = await this.rootStore.projectIssues.createIssue(workspaceSlug, projectId, data); - const issueToCycle = await this.addIssueToCycle(workspaceSlug, cycleId, [response.id], false); + await this.addIssueToCycle(workspaceSlug, cycleId, [response.id], false); let _issues = this.issues; if (!_issues) _issues = {}; @@ -211,7 +211,7 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor this.issues = _issues; }); - return issueToCycle; + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId); throw error; @@ -298,7 +298,7 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor const response = await this.createIssue(workspaceSlug, projectId, data, cycleId); - if (this.issues) { + if (this.issues && response) { delete this.issues[cycleId][data.id as keyof IIssue]; let _issues = { ...this.issues }; From afff9790d44ee2f892dfcdc0688bdc91bceb506d Mon Sep 17 00:00:00 2001 From: Erhan Date: Wed, 17 Jan 2024 12:12:11 +0100 Subject: [PATCH 10/17] Fix self-hosting docker-compose doc link (#3389) Fixes the link as it leads to a 404 at the moment in the documentation / readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b96dbf6c..41ebdd169 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Thats it! ## 🍙 Self Hosting -For self hosting environment setup, visit the [Self Hosting](https://docs.plane.so/self-hosting/docker-compose) documentation page +For self hosting environment setup, visit the [Self Hosting](https://docs.plane.so/docker-compose) documentation page ## 🚀 Features From a19598fec1664eb78840c8948c6418f0478855e1 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Wed, 17 Jan 2024 16:44:31 +0530 Subject: [PATCH 11/17] fix: updated env variables at root (#3390) --- .env.example | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 90070de19..71a9074a6 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,12 @@ # Database Settings -PGUSER="plane" -PGPASSWORD="plane" -PGHOST="plane-db" -PGDATABASE="plane" -DATABASE_URL=postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE} +POSTGRES_USER="plane" +POSTGRES_PASSWORD="plane" +POSTGRES_DB="plane" +PGDATA="/var/lib/postgresql/data" # Redis Settings REDIS_HOST="plane-redis" REDIS_PORT="6379" -REDIS_URL="redis://${REDIS_HOST}:6379/" # AWS Settings AWS_REGION="" From ee78b4fe524d228183192a825b8ee6e44d48c84b Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:03:57 +0530 Subject: [PATCH 12/17] dev: self host local build for other arch cpu (#3358) handled x86_64 along with amd64 --- deploy/selfhost/build.yml | 26 +++++++ deploy/selfhost/docker-compose.yml | 27 ++++--- deploy/selfhost/install.sh | 113 ++++++++++++++++++++++++++--- 3 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 deploy/selfhost/build.yml diff --git a/deploy/selfhost/build.yml b/deploy/selfhost/build.yml new file mode 100644 index 000000000..92533a73b --- /dev/null +++ b/deploy/selfhost/build.yml @@ -0,0 +1,26 @@ +version: "3.8" + +services: + web: + image: ${DOCKERHUB_USER:-local}/plane-frontend:${APP_RELEASE:-latest} + build: + context: . + dockerfile: ./web/Dockerfile.web + + space: + image: ${DOCKERHUB_USER:-local}/plane-space:${APP_RELEASE:-latest} + build: + context: ./ + dockerfile: ./space/Dockerfile.space + + api: + image: ${DOCKERHUB_USER:-local}/plane-backend:${APP_RELEASE:-latest} + build: + context: ./apiserver + dockerfile: ./Dockerfile.api + + proxy: + image: ${DOCKERHUB_USER:-local}/plane-proxy:${APP_RELEASE:-latest} + build: + context: ./nginx + dockerfile: ./Dockerfile diff --git a/deploy/selfhost/docker-compose.yml b/deploy/selfhost/docker-compose.yml index 8b4ff77ef..e42f53c7a 100644 --- a/deploy/selfhost/docker-compose.yml +++ b/deploy/selfhost/docker-compose.yml @@ -65,8 +65,8 @@ x-app-env : &app-env services: web: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-frontend:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-frontend:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} restart: unless-stopped command: /usr/local/bin/start.sh web/server.js web deploy: @@ -77,8 +77,8 @@ services: space: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-space:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-space:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} restart: unless-stopped command: /usr/local/bin/start.sh space/server.js space deploy: @@ -90,8 +90,8 @@ services: api: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-backend:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} restart: unless-stopped command: ./bin/takeoff deploy: @@ -102,8 +102,8 @@ services: worker: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-backend:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} restart: unless-stopped command: ./bin/worker depends_on: @@ -113,8 +113,8 @@ services: beat-worker: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-backend:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} restart: unless-stopped command: ./bin/beat depends_on: @@ -125,6 +125,7 @@ services: plane-db: <<: *app-env image: postgres:15.2-alpine + pull_policy: if_not_present restart: unless-stopped command: postgres -c 'max_connections=1000' volumes: @@ -133,6 +134,7 @@ services: plane-redis: <<: *app-env image: redis:6.2.7-alpine + pull_policy: if_not_present restart: unless-stopped volumes: - redisdata:/data @@ -140,6 +142,7 @@ services: plane-minio: <<: *app-env image: minio/minio + pull_policy: if_not_present restart: unless-stopped command: server /export --console-address ":9090" volumes: @@ -148,8 +151,8 @@ services: # Comment this if you already have a reverse proxy running proxy: <<: *app-env - platform: linux/amd64 - image: makeplane/plane-proxy:${APP_RELEASE:-latest} + image: ${DOCKERHUB_USER:-makeplane}/plane-proxy:${APP_RELEASE:-latest} + pull_policy: ${PULL_POLICY:-always} ports: - ${NGINX_PORT}:80 depends_on: diff --git a/deploy/selfhost/install.sh b/deploy/selfhost/install.sh index 15150aa40..b52c9236b 100755 --- a/deploy/selfhost/install.sh +++ b/deploy/selfhost/install.sh @@ -3,13 +3,75 @@ BRANCH=master SCRIPT_DIR=$PWD PLANE_INSTALL_DIR=$PWD/plane-app +export APP_RELEASE=$BRANCH +export DOCKERHUB_USER=makeplane +export PULL_POLICY=always +USE_GLOBAL_IMAGES=1 -function install(){ - echo - echo "Installing on $PLANE_INSTALL_DIR" +RED='\033[0;31m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +function buildLocalImage() { + if [ "$1" == "--force-build" ]; then + DO_BUILD="1" + elif [ "$1" == "--skip-build" ]; then + DO_BUILD="2" + else + printf "\n" >&2 + printf "${YELLOW}You are on ${ARCH} cpu architecture. ${NC}\n" >&2 + printf "${YELLOW}Since the prebuilt ${ARCH} compatible docker images are not available for, we will be running the docker build on this system. ${NC} \n" >&2 + printf "${YELLOW}This might take ${YELLOW}5-30 min based on your system's hardware configuration. \n ${NC} \n" >&2 + printf "\n" >&2 + printf "${GREEN}Select an option to proceed: ${NC}\n" >&2 + printf " 1) Build Fresh Images \n" >&2 + printf " 2) Skip Building Images \n" >&2 + printf " 3) Exit \n" >&2 + printf "\n" >&2 + read -p "Select Option [1]: " DO_BUILD + until [[ -z "$DO_BUILD" || "$DO_BUILD" =~ ^[1-3]$ ]]; do + echo "$DO_BUILD: invalid selection." >&2 + read -p "Select Option [1]: " DO_BUILD + done + echo "" >&2 + fi + + if [ "$DO_BUILD" == "1" ] || [ "$DO_BUILD" == "" ]; + then + REPO=https://github.com/makeplane/plane.git + CURR_DIR=$PWD + PLANE_TEMP_CODE_DIR=$(mktemp -d) + git clone $REPO $PLANE_TEMP_CODE_DIR --branch $BRANCH --single-branch + + cp $PLANE_TEMP_CODE_DIR/deploy/selfhost/build.yml $PLANE_TEMP_CODE_DIR/build.yml + + cd $PLANE_TEMP_CODE_DIR + if [ "$BRANCH" == "master" ]; + then + APP_RELEASE=latest + fi + + docker compose -f build.yml build --no-cache >&2 + # cd $CURR_DIR + # rm -rf $PLANE_TEMP_CODE_DIR + echo "build_completed" + elif [ "$DO_BUILD" == "2" ]; + then + printf "${YELLOW}Build action skipped by you in lieu of using existing images. ${NC} \n" >&2 + echo "build_skipped" + elif [ "$DO_BUILD" == "3" ]; + then + echo "build_exited" + else + printf "INVALID OPTION SUPPLIED" >&2 + fi +} +function install() { + echo "Installing Plane.........." download } -function download(){ +function download() { cd $SCRIPT_DIR TS=$(date +%s) if [ -f "$PLANE_INSTALL_DIR/docker-compose.yaml" ] @@ -35,6 +97,21 @@ function download(){ rm $PLANE_INSTALL_DIR/temp.yaml fi + + if [ $USE_GLOBAL_IMAGES == 0 ]; then + local res=$(buildLocalImage) + # echo $res + + if [ "$res" == "build_exited" ]; + then + echo + echo "Install action cancelled by you. Exiting now." + echo + exit 0 + fi + else + docker compose -f $PLANE_INSTALL_DIR/docker-compose.yaml pull + fi echo "" echo "Latest version is now available for you to use" @@ -43,22 +120,22 @@ function download(){ echo "" } -function startServices(){ +function startServices() { cd $PLANE_INSTALL_DIR - docker compose up -d + docker compose up -d --quiet-pull cd $SCRIPT_DIR } -function stopServices(){ +function stopServices() { cd $PLANE_INSTALL_DIR docker compose down cd $SCRIPT_DIR } -function restartServices(){ +function restartServices() { cd $PLANE_INSTALL_DIR docker compose restart cd $SCRIPT_DIR } -function upgrade(){ +function upgrade() { echo "***** STOPPING SERVICES ****" stopServices @@ -69,10 +146,10 @@ function upgrade(){ echo "***** PLEASE VALIDATE AND START SERVICES ****" } -function askForAction(){ +function askForAction() { echo echo "Select a Action you want to perform:" - echo " 1) Install" + echo " 1) Install (${ARCH})" echo " 2) Start" echo " 3) Stop" echo " 4) Restart" @@ -115,6 +192,20 @@ function askForAction(){ fi } +# CPU ARCHITECHTURE BASED SETTINGS +ARCH=$(uname -m) +if [ $ARCH == "amd64" ] || [ $ARCH == "x86_64" ]; +then + USE_GLOBAL_IMAGES=1 + DOCKERHUB_USER=makeplane + PULL_POLICY=always +else + USE_GLOBAL_IMAGES=0 + DOCKERHUB_USER=myplane + PULL_POLICY=never +fi + +# REMOVE SPECIAL CHARACTERS FROM BRANCH NAME if [ "$BRANCH" != "master" ]; then PLANE_INSTALL_DIR=$PWD/plane-app-$(echo $BRANCH | sed -r 's@(\/|" "|\.)@-@g') From eda1e46a2dc49e08b2a19c878ab01176af5b2142 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:45:52 +0530 Subject: [PATCH 13/17] chore: update issue template to auto assign and update labels (#3404) --- .github/ISSUE_TEMPLATE/--bug-report.yaml | 3 ++- .github/ISSUE_TEMPLATE/--feature-request.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/--bug-report.yaml b/.github/ISSUE_TEMPLATE/--bug-report.yaml index 4240c10c5..5d19be11c 100644 --- a/.github/ISSUE_TEMPLATE/--bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/--bug-report.yaml @@ -1,7 +1,8 @@ name: Bug report description: Create a bug report to help us improve Plane title: "[bug]: " -labels: [bug, need testing] +labels: [🐛bug] +assignees: [srinivaspendem, pushya-plane] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/--feature-request.yaml b/.github/ISSUE_TEMPLATE/--feature-request.yaml index b7ba11679..941fbef87 100644 --- a/.github/ISSUE_TEMPLATE/--feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/--feature-request.yaml @@ -1,7 +1,8 @@ name: Feature request description: Suggest a feature to improve Plane title: "[feature]: " -labels: [feature] +labels: [✨feature] +assignees: [srinivaspendem, pushya-plane] body: - type: markdown attributes: From c5e7c2f6a87b3fe2b799cbf0a6a0fa5ab5f1e9a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:52:53 +0530 Subject: [PATCH 14/17] chore(deps): bump follow-redirects from 1.15.3 to 1.15.4 (#3349) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ad1268469..ba2f01aad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5018,9 +5018,9 @@ flatted@^3.2.9: integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== for-each@^0.3.3: version "0.3.3" From af5057defac541696a31cda3950ade9a98014563 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:04:04 +0530 Subject: [PATCH 15/17] fix: key validation when magic sign in (#3403) --- apiserver/plane/app/views/authentication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/plane/app/views/authentication.py b/apiserver/plane/app/views/authentication.py index 256446313..78e867fae 100644 --- a/apiserver/plane/app/views/authentication.py +++ b/apiserver/plane/app/views/authentication.py @@ -325,7 +325,7 @@ class MagicSignInEndpoint(BaseAPIView): ) user_token = request.data.get("token", "").strip() - key = request.data.get("key", False).strip().lower() + key = request.data.get("key", "").strip().lower() if not key or user_token == "": return Response( From 6c2fecd3222db43ce301af1eea5dc1d0a749d102 Mon Sep 17 00:00:00 2001 From: Facundo Martin Gordillo Date: Fri, 19 Jan 2024 11:40:41 +0100 Subject: [PATCH 16/17] fix: Updated "deployment documentation" link (#3413) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41ebdd169..b509fd6f6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Meet [Plane](https://plane.so). An open-source software development tool to mana > Plane is still in its early days, not everything will be perfect yet, and hiccups may happen. Please let us know of any suggestions, ideas, or bugs that you encounter on our [Discord](https://discord.com/invite/A92xrEGCge) or GitHub issues, and we will use your feedback to improve on our upcoming releases. -The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account. Plane Cloud offers a hosted solution for Plane. If you prefer to self-host Plane, please refer to our [deployment documentation](https://docs.plane.so/self-hosting/docker-compose). +The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account. Plane Cloud offers a hosted solution for Plane. If you prefer to self-host Plane, please refer to our [deployment documentation](https://docs.plane.so/docker-compose). ## ⚡️ Contributors Quick Start From 543636eb4007ea03c225c368a7c9670a794dd00a Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:13:22 +0530 Subject: [PATCH 17/17] dev: update apiserver .env.example (#3412) --- apiserver/.env.example | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apiserver/.env.example b/apiserver/.env.example index 3ac9a3aeb..7e8e11639 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -8,11 +8,11 @@ SENTRY_DSN="" SENTRY_ENVIRONMENT="development" # Database Settings -PGUSER="plane" -PGPASSWORD="plane" -PGHOST="plane-db" -PGDATABASE="plane" -DATABASE_URL=postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE} +POSTGRES_USER="plane" +POSTGRES_PASSWORD="plane" +POSTGRES_HOST="plane-db" +POSTGRES_DB="plane" +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}/${POSTGRES_DB} # Oauth variables GOOGLE_CLIENT_ID=""