mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: enable api logging (#2538)
* dev: enable api logging and control worker count through env * dev: enable logger instead of printing * dev: remove worker counts * dev: enable global level log settings * dev: add rotating logger * fix: logging configuration * dev: api logging and moving the capture exception to utils for logging and then capturing * fix: information leaking through print logs * dev: linting fix * dev: logging configuration for django * fix: linting errors * dev: add logs for migrator * dev: logging cofiguration * dev: add permision for captain user in Plane * dev: add log paths in compose * dev: create directory for logs * dev: fix linting errors
This commit is contained in:
parent
0759666b75
commit
82ba9833f2
1
.gitignore
vendored
1
.gitignore
vendored
@ -51,6 +51,7 @@ staticfiles
|
|||||||
mediafiles
|
mediafiles
|
||||||
.env
|
.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
logs/
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
assets/dist/
|
assets/dist/
|
||||||
|
@ -44,4 +44,3 @@ WEB_URL="http://localhost"
|
|||||||
|
|
||||||
# Gunicorn Workers
|
# Gunicorn Workers
|
||||||
GUNICORN_WORKERS=2
|
GUNICORN_WORKERS=2
|
||||||
|
|
||||||
|
@ -48,8 +48,10 @@ USER root
|
|||||||
RUN apk --no-cache add "bash~=5.2"
|
RUN apk --no-cache add "bash~=5.2"
|
||||||
COPY ./bin ./bin/
|
COPY ./bin ./bin/
|
||||||
|
|
||||||
|
RUN mkdir /code/plane/logs
|
||||||
RUN chmod +x ./bin/takeoff ./bin/worker ./bin/beat
|
RUN chmod +x ./bin/takeoff ./bin/worker ./bin/beat
|
||||||
RUN chmod -R 777 /code
|
RUN chmod -R 777 /code
|
||||||
|
RUN chown -R captain:plane /code
|
||||||
|
|
||||||
USER captain
|
USER captain
|
||||||
|
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import zoneinfo
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import zoneinfo
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
|
from django.db import IntegrityError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
from rest_framework import status
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.api.middleware.api_authentication import APIKeyAuthentication
|
from plane.api.middleware.api_authentication import APIKeyAuthentication
|
||||||
from plane.api.rate_limit import ApiKeyRateThrottle
|
from plane.api.rate_limit import ApiKeyRateThrottle
|
||||||
from plane.utils.paginator import BasePaginator
|
|
||||||
from plane.bgtasks.webhook_task import send_webhook
|
from plane.bgtasks.webhook_task import send_webhook
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
from plane.utils.paginator import BasePaginator
|
||||||
|
|
||||||
|
|
||||||
class TimezoneMixin:
|
class TimezoneMixin:
|
||||||
@ -106,15 +106,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
|||||||
|
|
||||||
if isinstance(e, ValidationError):
|
if isinstance(e, ValidationError):
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{"error": "Please provide valid detail"},
|
||||||
"error": "The provided payload is not valid please try with a valid payload"
|
|
||||||
},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(e, ObjectDoesNotExist):
|
if isinstance(e, ObjectDoesNotExist):
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "The required object does not exist."},
|
{"error": "The requested resource does not exist."},
|
||||||
status=status.HTTP_404_NOT_FOUND,
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,9 +122,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import zoneinfo
|
import zoneinfo
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
|
from django.db import IntegrityError
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.urls import resolve
|
from django.urls import resolve
|
||||||
from django.conf import settings
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db import IntegrityError
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
||||||
|
|
||||||
# Third part imports
|
# Third part imports
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.viewsets import ModelViewSet
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.filters import SearchFilter
|
from rest_framework.filters import SearchFilter
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from sentry_sdk import capture_exception
|
from rest_framework.response import Response
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.utils.paginator import BasePaginator
|
|
||||||
from plane.bgtasks.webhook_task import send_webhook
|
from plane.bgtasks.webhook_task import send_webhook
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
from plane.utils.paginator import BasePaginator
|
||||||
|
|
||||||
|
|
||||||
class TimezoneMixin:
|
class TimezoneMixin:
|
||||||
@ -87,7 +87,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
|||||||
try:
|
try:
|
||||||
return self.model.objects.all()
|
return self.model.objects.all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
raise APIException(
|
raise APIException(
|
||||||
"Please check the view", status.HTTP_400_BAD_REQUEST
|
"Please check the view", status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
@ -121,13 +121,13 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(e, KeyError):
|
if isinstance(e, KeyError):
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "The required key does not exist."},
|
{"error": "The required key does not exist."},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@ -233,9 +233,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
@ -15,10 +15,7 @@ from django.db.models import (
|
|||||||
Value,
|
Value,
|
||||||
CharField,
|
CharField,
|
||||||
)
|
)
|
||||||
from django.core import serializers
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.views.decorators.gzip import gzip_page
|
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.db.models import UUIDField
|
from django.db.models import UUIDField
|
||||||
@ -32,9 +29,7 @@ from rest_framework import status
|
|||||||
from .. import BaseViewSet, BaseAPIView, WebhookMixin
|
from .. import BaseViewSet, BaseAPIView, WebhookMixin
|
||||||
from plane.app.serializers import (
|
from plane.app.serializers import (
|
||||||
CycleSerializer,
|
CycleSerializer,
|
||||||
CycleIssueSerializer,
|
|
||||||
CycleFavoriteSerializer,
|
CycleFavoriteSerializer,
|
||||||
IssueSerializer,
|
|
||||||
CycleWriteSerializer,
|
CycleWriteSerializer,
|
||||||
CycleUserPropertiesSerializer,
|
CycleUserPropertiesSerializer,
|
||||||
)
|
)
|
||||||
@ -48,13 +43,10 @@ from plane.db.models import (
|
|||||||
CycleIssue,
|
CycleIssue,
|
||||||
Issue,
|
Issue,
|
||||||
CycleFavorite,
|
CycleFavorite,
|
||||||
IssueLink,
|
|
||||||
IssueAttachment,
|
|
||||||
Label,
|
Label,
|
||||||
CycleUserProperties,
|
CycleUserProperties,
|
||||||
)
|
)
|
||||||
from plane.bgtasks.issue_activites_task import issue_activity
|
from plane.bgtasks.issue_activites_task import issue_activity
|
||||||
from plane.utils.issue_filters import issue_filters
|
|
||||||
from plane.utils.analytics_plot import burndown_plot
|
from plane.utils.analytics_plot import burndown_plot
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ from plane.db.models import (
|
|||||||
IssueLink,
|
IssueLink,
|
||||||
IssueAttachment,
|
IssueAttachment,
|
||||||
IssueRelation,
|
IssueRelation,
|
||||||
IssueAssignee,
|
|
||||||
User,
|
User,
|
||||||
)
|
)
|
||||||
from plane.app.serializers import (
|
from plane.app.serializers import (
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import json
|
import json
|
||||||
import random
|
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -21,64 +19,38 @@ from django.db.models import (
|
|||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.gzip import gzip_page
|
from django.views.decorators.gzip import gzip_page
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.db.models import Value, UUIDField
|
from django.db.models import UUIDField
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
# Third Party imports
|
# Third Party imports
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.parsers import MultiPartParser, FormParser
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from .. import BaseViewSet, BaseAPIView, WebhookMixin
|
from .. import BaseViewSet, BaseAPIView, WebhookMixin
|
||||||
from plane.app.serializers import (
|
from plane.app.serializers import (
|
||||||
IssueActivitySerializer,
|
|
||||||
IssueCommentSerializer,
|
|
||||||
IssuePropertySerializer,
|
IssuePropertySerializer,
|
||||||
IssueSerializer,
|
IssueSerializer,
|
||||||
IssueCreateSerializer,
|
IssueCreateSerializer,
|
||||||
LabelSerializer,
|
|
||||||
IssueFlatSerializer,
|
|
||||||
IssueLinkSerializer,
|
|
||||||
IssueLiteSerializer,
|
|
||||||
IssueAttachmentSerializer,
|
|
||||||
IssueSubscriberSerializer,
|
|
||||||
ProjectMemberLiteSerializer,
|
|
||||||
IssueReactionSerializer,
|
|
||||||
CommentReactionSerializer,
|
|
||||||
IssueRelationSerializer,
|
|
||||||
RelatedIssueSerializer,
|
|
||||||
IssueDetailSerializer,
|
IssueDetailSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
ProjectEntityPermission,
|
ProjectEntityPermission,
|
||||||
WorkSpaceAdminPermission,
|
|
||||||
ProjectMemberPermission,
|
|
||||||
ProjectLitePermission,
|
ProjectLitePermission,
|
||||||
)
|
)
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
Project,
|
Project,
|
||||||
Issue,
|
Issue,
|
||||||
IssueActivity,
|
|
||||||
IssueComment,
|
|
||||||
IssueProperty,
|
IssueProperty,
|
||||||
Label,
|
|
||||||
IssueLink,
|
IssueLink,
|
||||||
IssueAttachment,
|
IssueAttachment,
|
||||||
IssueSubscriber,
|
IssueSubscriber,
|
||||||
ProjectMember,
|
|
||||||
IssueReaction,
|
IssueReaction,
|
||||||
CommentReaction,
|
|
||||||
IssueRelation,
|
|
||||||
)
|
)
|
||||||
from plane.bgtasks.issue_activites_task import issue_activity
|
from plane.bgtasks.issue_activites_task import issue_activity
|
||||||
from plane.utils.grouper import group_results
|
|
||||||
from plane.utils.issue_filters import issue_filters
|
from plane.utils.issue_filters import issue_filters
|
||||||
from collections import defaultdict
|
|
||||||
from plane.utils.cache import invalidate_cache
|
|
||||||
|
|
||||||
class IssueListEndpoint(BaseAPIView):
|
class IssueListEndpoint(BaseAPIView):
|
||||||
|
|
||||||
|
@ -1,52 +1,54 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Django imports
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.db.models import (
|
|
||||||
Prefetch,
|
|
||||||
OuterRef,
|
|
||||||
Func,
|
|
||||||
F,
|
|
||||||
Q,
|
|
||||||
Case,
|
|
||||||
Value,
|
|
||||||
CharField,
|
|
||||||
When,
|
|
||||||
Exists,
|
|
||||||
Max,
|
|
||||||
UUIDField,
|
|
||||||
)
|
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.views.decorators.gzip import gzip_page
|
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
|
from django.db.models import (
|
||||||
|
Case,
|
||||||
|
CharField,
|
||||||
|
Exists,
|
||||||
|
F,
|
||||||
|
Func,
|
||||||
|
Max,
|
||||||
|
OuterRef,
|
||||||
|
Prefetch,
|
||||||
|
Q,
|
||||||
|
UUIDField,
|
||||||
|
Value,
|
||||||
|
When,
|
||||||
|
)
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
|
# Django imports
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views.decorators.gzip import gzip_page
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
# Third Party imports
|
# Third Party imports
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
|
||||||
|
from plane.app.permissions import ProjectEntityPermission
|
||||||
|
from plane.app.serializers import (
|
||||||
|
IssueCreateSerializer,
|
||||||
|
IssueDetailSerializer,
|
||||||
|
IssueFlatSerializer,
|
||||||
|
IssueSerializer,
|
||||||
|
)
|
||||||
|
from plane.bgtasks.issue_activites_task import issue_activity
|
||||||
|
from plane.db.models import (
|
||||||
|
Issue,
|
||||||
|
IssueAttachment,
|
||||||
|
IssueLink,
|
||||||
|
IssueReaction,
|
||||||
|
IssueSubscriber,
|
||||||
|
Project,
|
||||||
|
)
|
||||||
|
from plane.utils.issue_filters import issue_filters
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from .. import BaseViewSet
|
from .. import BaseViewSet
|
||||||
from plane.app.serializers import (
|
|
||||||
IssueSerializer,
|
|
||||||
IssueCreateSerializer,
|
|
||||||
IssueFlatSerializer,
|
|
||||||
IssueDetailSerializer,
|
|
||||||
)
|
|
||||||
from plane.app.permissions import ProjectEntityPermission
|
|
||||||
from plane.db.models import (
|
|
||||||
Project,
|
|
||||||
Issue,
|
|
||||||
IssueLink,
|
|
||||||
IssueAttachment,
|
|
||||||
IssueSubscriber,
|
|
||||||
IssueReaction,
|
|
||||||
)
|
|
||||||
from plane.bgtasks.issue_activites_task import issue_activity
|
|
||||||
from plane.utils.issue_filters import issue_filters
|
|
||||||
|
|
||||||
|
|
||||||
class IssueDraftViewSet(BaseViewSet):
|
class IssueDraftViewSet(BaseViewSet):
|
||||||
@ -117,11 +119,6 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
filters = issue_filters(request.query_params, "GET")
|
filters = issue_filters(request.query_params, "GET")
|
||||||
fields = [
|
|
||||||
field
|
|
||||||
for field in request.GET.get("fields", "").split(",")
|
|
||||||
if field
|
|
||||||
]
|
|
||||||
|
|
||||||
# Custom ordering for priority and state
|
# Custom ordering for priority and state
|
||||||
priority_order = ["urgent", "high", "medium", "low", "none"]
|
priority_order = ["urgent", "high", "medium", "low", "none"]
|
||||||
|
@ -346,12 +346,12 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
|||||||
{"name": "The project name is already taken"},
|
{"name": "The project name is already taken"},
|
||||||
status=status.HTTP_410_GONE,
|
status=status.HTTP_410_GONE,
|
||||||
)
|
)
|
||||||
except Workspace.DoesNotExist as e:
|
except Workspace.DoesNotExist:
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Workspace does not exist"},
|
{"error": "Workspace does not exist"},
|
||||||
status=status.HTTP_404_NOT_FOUND,
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
except serializers.ValidationError as e:
|
except serializers.ValidationError:
|
||||||
return Response(
|
return Response(
|
||||||
{"identifier": "The project identifier is already taken"},
|
{"identifier": "The project identifier is already taken"},
|
||||||
status=status.HTTP_410_GONE,
|
status=status.HTTP_410_GONE,
|
||||||
@ -410,7 +410,7 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
|||||||
{"error": "Project does not exist"},
|
{"error": "Project does not exist"},
|
||||||
status=status.HTTP_404_NOT_FOUND,
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
except serializers.ValidationError as e:
|
except serializers.ValidationError:
|
||||||
return Response(
|
return Response(
|
||||||
{"identifier": "The project identifier is already taken"},
|
{"identifier": "The project identifier is already taken"},
|
||||||
status=status.HTTP_410_GONE,
|
status=status.HTTP_410_GONE,
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import csv
|
import csv
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Issue
|
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.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.analytics_plot import build_graph_plot
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
from plane.utils.issue_filters import issue_filters
|
||||||
|
|
||||||
row_mapping = {
|
row_mapping = {
|
||||||
"state__name": "State",
|
"state__name": "State",
|
||||||
@ -210,9 +210,9 @@ def generate_segmented_rows(
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if assignee:
|
if assignee:
|
||||||
generated_row[
|
generated_row[0] = (
|
||||||
0
|
f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
||||||
] = f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
)
|
||||||
|
|
||||||
if x_axis == LABEL_ID:
|
if x_axis == LABEL_ID:
|
||||||
label = next(
|
label = next(
|
||||||
@ -279,9 +279,9 @@ def generate_segmented_rows(
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if assignee:
|
if assignee:
|
||||||
row_zero[
|
row_zero[index + 2] = (
|
||||||
index + 2
|
f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
||||||
] = f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
)
|
||||||
|
|
||||||
if segmented == LABEL_ID:
|
if segmented == LABEL_ID:
|
||||||
for index, segm in enumerate(row_zero[2:]):
|
for index, segm in enumerate(row_zero[2:]):
|
||||||
@ -366,9 +366,9 @@ def generate_non_segmented_rows(
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if assignee:
|
if assignee:
|
||||||
row[
|
row[0] = (
|
||||||
0
|
f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
||||||
] = f"{assignee['assignees__first_name']} {assignee['assignees__last_name']}"
|
)
|
||||||
|
|
||||||
if x_axis == LABEL_ID:
|
if x_axis == LABEL_ID:
|
||||||
label = next(
|
label = next(
|
||||||
@ -504,10 +504,8 @@ def analytic_export_task(email, data, slug):
|
|||||||
|
|
||||||
csv_buffer = generate_csv_from_rows(rows)
|
csv_buffer = generate_csv_from_rows(rows)
|
||||||
send_export_email(email, slug, csv_buffer, rows)
|
send_export_email(email, slug, csv_buffer, rows)
|
||||||
|
logging.getLogger("plane").info("Email sent succesfully.")
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from sentry_sdk import capture_exception
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
|
||||||
from django.template.loader import render_to_string
|
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import EmailNotificationLog, User, Issue
|
from plane.db.models import EmailNotificationLog, Issue, User
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
from plane.settings.redis import redis_instance
|
from plane.settings.redis import redis_instance
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
# acquire and delete redis lock
|
# acquire and delete redis lock
|
||||||
@ -69,7 +70,9 @@ def stack_email_notification():
|
|||||||
receiver_notification.get("entity_identifier"), {}
|
receiver_notification.get("entity_identifier"), {}
|
||||||
).setdefault(
|
).setdefault(
|
||||||
str(receiver_notification.get("triggered_by_id")), []
|
str(receiver_notification.get("triggered_by_id")), []
|
||||||
).append(receiver_notification.get("data"))
|
).append(
|
||||||
|
receiver_notification.get("data")
|
||||||
|
)
|
||||||
# append processed notifications
|
# append processed notifications
|
||||||
processed_notifications.append(receiver_notification.get("id"))
|
processed_notifications.append(receiver_notification.get("id"))
|
||||||
email_notification_ids.append(receiver_notification.get("id"))
|
email_notification_ids.append(receiver_notification.get("id"))
|
||||||
@ -296,7 +299,9 @@ def send_email_notification(
|
|||||||
)
|
)
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email Sent Successfully")
|
||||||
|
|
||||||
|
# Update the logs
|
||||||
EmailNotificationLog.objects.filter(
|
EmailNotificationLog.objects.filter(
|
||||||
pk__in=email_notification_ids
|
pk__in=email_notification_ids
|
||||||
).update(sent_at=timezone.now())
|
).update(sent_at=timezone.now())
|
||||||
@ -305,15 +310,20 @@ def send_email_notification(
|
|||||||
release_lock(lock_id=lock_id)
|
release_lock(lock_id=lock_id)
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
# release the lock
|
# release the lock
|
||||||
release_lock(lock_id=lock_id)
|
release_lock(lock_id=lock_id)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
print("Duplicate task recived. Skipping...")
|
logging.getLogger("plane").info(
|
||||||
|
"Duplicate email received skipping"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
except (Issue.DoesNotExist, User.DoesNotExist) as e:
|
except (Issue.DoesNotExist, User.DoesNotExist) as e:
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
release_lock(lock_id=lock_id)
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
log_exception(e)
|
||||||
release_lock(lock_id=lock_id)
|
release_lock(lock_id=lock_id)
|
||||||
return
|
return
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import uuid
|
|
||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
|
|
||||||
# third party imports
|
# third party imports
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
from posthog import Posthog
|
from posthog import Posthog
|
||||||
|
|
||||||
# module imports
|
# module imports
|
||||||
from plane.license.utils.instance_value import get_configuration_value
|
from plane.license.utils.instance_value import get_configuration_value
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
def posthogConfiguration():
|
def posthogConfiguration():
|
||||||
@ -51,7 +51,8 @@ def auth_events(user, email, user_agent, ip, event_name, medium, first_time):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -77,4 +78,5 @@ def workspace_invite_event(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
|
return
|
||||||
|
@ -2,21 +2,22 @@
|
|||||||
import csv
|
import csv
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import boto3
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.client import Config
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
from botocore.client import Config
|
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Issue, ExporterHistory
|
from plane.db.models import ExporterHistory, Issue
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
def dateTimeConverter(time):
|
def dateTimeConverter(time):
|
||||||
@ -403,8 +404,5 @@ def issue_export_task(
|
|||||||
exporter_instance.status = "failed"
|
exporter_instance.status = "failed"
|
||||||
exporter_instance.reason = str(e)
|
exporter_instance.reason = str(e)
|
||||||
exporter_instance.save(update_fields=["status", "reason"])
|
exporter_instance.save(update_fields=["status", "reason"])
|
||||||
# Print logs if in DEBUG mode
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
# Python import
|
# Python imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -60,10 +60,8 @@ def forgot_password(first_name, email, uidb64, token, current_site):
|
|||||||
)
|
)
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email sent successfully")
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Print logs if in DEBUG mode
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,34 +1,36 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
# Third Party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
# Third Party imports
|
from plane.app.serializers import IssueActivitySerializer
|
||||||
from celery import shared_task
|
from plane.bgtasks.notification_task import notifications
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
User,
|
|
||||||
Issue,
|
|
||||||
Project,
|
|
||||||
Label,
|
|
||||||
IssueActivity,
|
|
||||||
State,
|
|
||||||
Cycle,
|
|
||||||
Module,
|
|
||||||
IssueReaction,
|
|
||||||
CommentReaction,
|
CommentReaction,
|
||||||
|
Cycle,
|
||||||
|
Issue,
|
||||||
|
IssueActivity,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
|
IssueReaction,
|
||||||
IssueSubscriber,
|
IssueSubscriber,
|
||||||
|
Label,
|
||||||
|
Module,
|
||||||
|
Project,
|
||||||
|
State,
|
||||||
|
User,
|
||||||
)
|
)
|
||||||
from plane.app.serializers import IssueActivitySerializer
|
|
||||||
from plane.bgtasks.notification_task import notifications
|
|
||||||
from plane.settings.redis import redis_instance
|
from plane.settings.redis import redis_instance
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
# Track Changes in name
|
# Track Changes in name
|
||||||
@ -1647,7 +1649,7 @@ def issue_activity(
|
|||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
|
|
||||||
if notification:
|
if notification:
|
||||||
notifications.delay(
|
notifications.delay(
|
||||||
@ -1668,8 +1670,5 @@ def issue_activity(
|
|||||||
|
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Print logs if in DEBUG mode
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -2,18 +2,17 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
# Django imports
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.db.models import Q
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from sentry_sdk import capture_exception
|
from django.db.models import Q
|
||||||
|
|
||||||
|
# Django imports
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Issue, Project, State
|
|
||||||
from plane.bgtasks.issue_activites_task import issue_activity
|
from plane.bgtasks.issue_activites_task import issue_activity
|
||||||
|
from plane.db.models import Issue, Project, State
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -96,9 +95,7 @@ def archive_old_issues():
|
|||||||
]
|
]
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@ -179,7 +176,5 @@ def close_old_issues():
|
|||||||
]
|
]
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -52,11 +52,8 @@ def magic_link(email, key, token, current_site):
|
|||||||
)
|
)
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email sent successfully.")
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log_exception(e)
|
||||||
capture_exception(e)
|
|
||||||
# Print logs if in DEBUG mode
|
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
# Python import
|
# Python imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Project, User, ProjectMemberInvite
|
from plane.db.models import Project, ProjectMemberInvite, User
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -73,12 +73,10 @@ def project_invitation(email, project_id, token, current_site, invitor):
|
|||||||
|
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email sent successfully.")
|
||||||
return
|
return
|
||||||
except (Project.DoesNotExist, ProjectMemberInvite.DoesNotExist):
|
except (Project.DoesNotExist, ProjectMemberInvite.DoesNotExist):
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Print logs if in DEBUG mode
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,44 +1,45 @@
|
|||||||
import requests
|
|
||||||
import uuid
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
import hmac
|
import hmac
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
# Django imports
|
import requests
|
||||||
from django.conf import settings
|
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
|
||||||
from django.template.loader import render_to_string
|
|
||||||
from django.utils.html import strip_tags
|
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
from plane.db.models import (
|
# Django imports
|
||||||
Webhook,
|
from django.conf import settings
|
||||||
WebhookLog,
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
Project,
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
Issue,
|
from django.template.loader import render_to_string
|
||||||
Cycle,
|
from django.utils.html import strip_tags
|
||||||
Module,
|
|
||||||
ModuleIssue,
|
|
||||||
CycleIssue,
|
|
||||||
IssueComment,
|
|
||||||
User,
|
|
||||||
)
|
|
||||||
from plane.api.serializers import (
|
|
||||||
ProjectSerializer,
|
|
||||||
CycleSerializer,
|
|
||||||
ModuleSerializer,
|
|
||||||
CycleIssueSerializer,
|
|
||||||
ModuleIssueSerializer,
|
|
||||||
IssueCommentSerializer,
|
|
||||||
IssueExpandSerializer,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
|
from plane.api.serializers import (
|
||||||
|
CycleIssueSerializer,
|
||||||
|
CycleSerializer,
|
||||||
|
IssueCommentSerializer,
|
||||||
|
IssueExpandSerializer,
|
||||||
|
ModuleIssueSerializer,
|
||||||
|
ModuleSerializer,
|
||||||
|
ProjectSerializer,
|
||||||
|
)
|
||||||
|
from plane.db.models import (
|
||||||
|
Cycle,
|
||||||
|
CycleIssue,
|
||||||
|
Issue,
|
||||||
|
IssueComment,
|
||||||
|
Module,
|
||||||
|
ModuleIssue,
|
||||||
|
Project,
|
||||||
|
User,
|
||||||
|
Webhook,
|
||||||
|
WebhookLog,
|
||||||
|
)
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
SERIALIZER_MAPPER = {
|
SERIALIZER_MAPPER = {
|
||||||
"project": ProjectSerializer,
|
"project": ProjectSerializer,
|
||||||
@ -174,7 +175,7 @@ def webhook_task(self, webhook, slug, event, event_data, action, current_site):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
print(e)
|
print(e)
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@ -241,7 +242,7 @@ def send_webhook(event, payload, kw, action, slug, bulk, current_site):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
print(e)
|
print(e)
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@ -295,8 +296,8 @@ def send_webhook_deactivation_email(
|
|||||||
)
|
)
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email sent successfully.")
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log_exception(e)
|
||||||
return
|
return
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from celery import shared_task
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Workspace, WorkspaceMemberInvite, User
|
from plane.db.models import User, Workspace, WorkspaceMemberInvite
|
||||||
from plane.license.utils.instance_value import get_email_configuration
|
from plane.license.utils.instance_value import get_email_configuration
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -76,14 +76,12 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor):
|
|||||||
)
|
)
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send()
|
msg.send()
|
||||||
|
logging.getLogger("plane").info("Email sent succesfully")
|
||||||
|
|
||||||
return
|
return
|
||||||
except (Workspace.DoesNotExist, WorkspaceMemberInvite.DoesNotExist):
|
except (Workspace.DoesNotExist, WorkspaceMemberInvite.DoesNotExist) as e:
|
||||||
print("Workspace or WorkspaceMember Invite Does not exists")
|
log_exception(e)
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Print logs if in DEBUG mode
|
log_exception(e)
|
||||||
if settings.DEBUG:
|
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return
|
return
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import uuid
|
|
||||||
import string
|
|
||||||
import random
|
import random
|
||||||
|
import string
|
||||||
|
import uuid
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
from django.contrib.auth.models import (
|
||||||
|
AbstractBaseUser,
|
||||||
|
PermissionsMixin,
|
||||||
|
UserManager,
|
||||||
|
)
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import (
|
|
||||||
AbstractBaseUser,
|
|
||||||
UserManager,
|
|
||||||
PermissionsMixin,
|
|
||||||
)
|
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -3,19 +3,20 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import os
|
import os
|
||||||
import ssl
|
import ssl
|
||||||
import certifi
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
# Django imports
|
import certifi
|
||||||
from django.core.management.utils import get_random_secret_key
|
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
import dj_database_url
|
import dj_database_url
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
|
||||||
|
# Django imports
|
||||||
|
from django.core.management.utils import get_random_secret_key
|
||||||
|
from sentry_sdk.integrations.celery import CeleryIntegration
|
||||||
from sentry_sdk.integrations.django import DjangoIntegration
|
from sentry_sdk.integrations.django import DjangoIntegration
|
||||||
from sentry_sdk.integrations.redis import RedisIntegration
|
from sentry_sdk.integrations.redis import RedisIntegration
|
||||||
from sentry_sdk.integrations.celery import CeleryIntegration
|
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
SECRET_KEY = os.environ.get("SECRET_KEY", get_random_secret_key())
|
SECRET_KEY = os.environ.get("SECRET_KEY", get_random_secret_key())
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = False
|
DEBUG = int(os.environ.get("DEBUG", "0"))
|
||||||
|
|
||||||
# Allowed Hosts
|
# Allowed Hosts
|
||||||
ALLOWED_HOSTS = ["*"]
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
@ -7,8 +7,8 @@ from .common import * # noqa
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
# Debug Toolbar settings
|
# Debug Toolbar settings
|
||||||
INSTALLED_APPS += ("debug_toolbar",)
|
INSTALLED_APPS += ("debug_toolbar",) # noqa
|
||||||
MIDDLEWARE += ("debug_toolbar.middleware.DebugToolbarMiddleware",)
|
MIDDLEWARE += ("debug_toolbar.middleware.DebugToolbarMiddleware",) # noqa
|
||||||
|
|
||||||
DEBUG_TOOLBAR_PATCH_SETTINGS = False
|
DEBUG_TOOLBAR_PATCH_SETTINGS = False
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
|||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django_redis.cache.RedisCache",
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
"LOCATION": REDIS_URL,
|
"LOCATION": REDIS_URL, # noqa
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
},
|
},
|
||||||
@ -28,7 +28,7 @@ CACHES = {
|
|||||||
INTERNAL_IPS = ("127.0.0.1",)
|
INTERNAL_IPS = ("127.0.0.1",)
|
||||||
|
|
||||||
MEDIA_URL = "/uploads/"
|
MEDIA_URL = "/uploads/"
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "uploads")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "uploads") # noqa
|
||||||
|
|
||||||
CORS_ALLOWED_ORIGINS = [
|
CORS_ALLOWED_ORIGINS = [
|
||||||
"http://localhost:3000",
|
"http://localhost:3000",
|
||||||
@ -36,3 +36,38 @@ CORS_ALLOWED_ORIGINS = [
|
|||||||
"http://localhost:4000",
|
"http://localhost:4000",
|
||||||
"http://127.0.0.1:4000",
|
"http://127.0.0.1:4000",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
LOG_DIR = os.path.join(BASE_DIR, "logs") # noqa
|
||||||
|
|
||||||
|
if not os.path.exists(LOG_DIR):
|
||||||
|
os.makedirs(LOG_DIR)
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
|
||||||
|
"style": "{",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django.request": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"plane": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
"""Production settings"""
|
"""Production settings"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .common import * # noqa
|
from .common import * # noqa
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = int(os.environ.get("DEBUG", 0)) == 1
|
DEBUG = int(os.environ.get("DEBUG", 0)) == 1
|
||||||
|
DEBUG = True
|
||||||
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
||||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
INSTALLED_APPS += ("scout_apm.django",)
|
INSTALLED_APPS += ("scout_apm.django",) # noqa
|
||||||
|
|
||||||
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
||||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
@ -18,3 +19,62 @@ SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|||||||
SCOUT_MONITOR = os.environ.get("SCOUT_MONITOR", False)
|
SCOUT_MONITOR = os.environ.get("SCOUT_MONITOR", False)
|
||||||
SCOUT_KEY = os.environ.get("SCOUT_KEY", "")
|
SCOUT_KEY = os.environ.get("SCOUT_KEY", "")
|
||||||
SCOUT_NAME = "Plane"
|
SCOUT_NAME = "Plane"
|
||||||
|
|
||||||
|
LOG_DIR = os.path.join(BASE_DIR, "logs") # noqa
|
||||||
|
|
||||||
|
if not os.path.exists(LOG_DIR):
|
||||||
|
os.makedirs(LOG_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
|
||||||
|
"style": "{",
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
|
||||||
|
"fmt": "%(levelname)s %(asctime)s %(module)s %(name)s %(message)s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"class": "plane.utils.logging.SizedTimedRotatingFileHandler",
|
||||||
|
"filename": (
|
||||||
|
os.path.join(BASE_DIR, "logs", "plane-debug.log") # noqa
|
||||||
|
if DEBUG
|
||||||
|
else os.path.join(BASE_DIR, "logs", "plane-error.log") # noqa
|
||||||
|
),
|
||||||
|
"when": "s",
|
||||||
|
"maxBytes": 1024 * 1024 * 1,
|
||||||
|
"interval": 1,
|
||||||
|
"backupCount": 5,
|
||||||
|
"formatter": "json",
|
||||||
|
"level": "DEBUG" if DEBUG else "ERROR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": True,
|
||||||
|
},
|
||||||
|
"django.request": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"plane": {
|
||||||
|
"level": "DEBUG" if DEBUG else "ERROR",
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -7,6 +7,6 @@ DEBUG = True
|
|||||||
# Send it in a dummy outbox
|
# Send it in a dummy outbox
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||||
|
|
||||||
INSTALLED_APPS.append(
|
INSTALLED_APPS.append( # noqa
|
||||||
"plane.tests",
|
"plane.tests",
|
||||||
)
|
)
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
import zoneinfo
|
import zoneinfo
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
|
from django.db import IntegrityError
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.urls import resolve
|
from django.urls import resolve
|
||||||
from django.conf import settings
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db import IntegrityError
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
||||||
|
|
||||||
# Third part imports
|
# Third part imports
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.viewsets import ModelViewSet
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.filters import SearchFilter
|
from rest_framework.filters import SearchFilter
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from sentry_sdk import capture_exception
|
from rest_framework.response import Response
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
|
from plane.utils.exception_logger import log_exception
|
||||||
from plane.utils.paginator import BasePaginator
|
from plane.utils.paginator import BasePaginator
|
||||||
|
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
|||||||
try:
|
try:
|
||||||
return self.model.objects.all()
|
return self.model.objects.all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
raise APIException(
|
raise APIException(
|
||||||
"Please check the view", status.HTTP_400_BAD_REQUEST
|
"Please check the view", status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
@ -90,14 +90,13 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(e, KeyError):
|
if isinstance(e, KeyError):
|
||||||
capture_exception(e)
|
log_exception(e)
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "The required key does not exist."},
|
{"error": "The required key does not exist."},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(e) if settings.DEBUG else print("Server Error")
|
log_exception(e)
|
||||||
capture_exception(e)
|
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@ -185,9 +184,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
|||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
log_exception(e)
|
||||||
print(e)
|
|
||||||
capture_exception(e)
|
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
15
apiserver/plane/utils/exception_logger.py
Normal file
15
apiserver/plane/utils/exception_logger.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Python imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Third party imports
|
||||||
|
from sentry_sdk import capture_exception
|
||||||
|
|
||||||
|
|
||||||
|
def log_exception(e):
|
||||||
|
# Log the error
|
||||||
|
logger = logging.getLogger("plane")
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
|
# Capture in sentry if configured
|
||||||
|
capture_exception(e)
|
||||||
|
return
|
46
apiserver/plane/utils/logging.py
Normal file
46
apiserver/plane/utils/logging.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import logging.handlers as handlers
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class SizedTimedRotatingFileHandler(handlers.TimedRotatingFileHandler):
|
||||||
|
"""
|
||||||
|
Handler for logging to a set of files, which switches from one file
|
||||||
|
to the next when the current file reaches a certain size, or at certain
|
||||||
|
timed intervals
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
filename,
|
||||||
|
maxBytes=0,
|
||||||
|
backupCount=0,
|
||||||
|
encoding=None,
|
||||||
|
delay=0,
|
||||||
|
when="h",
|
||||||
|
interval=1,
|
||||||
|
utc=False,
|
||||||
|
):
|
||||||
|
handlers.TimedRotatingFileHandler.__init__(
|
||||||
|
self, filename, when, interval, backupCount, encoding, delay, utc
|
||||||
|
)
|
||||||
|
self.maxBytes = maxBytes
|
||||||
|
|
||||||
|
def shouldRollover(self, record):
|
||||||
|
"""
|
||||||
|
Determine if rollover should occur.
|
||||||
|
|
||||||
|
Basically, see if the supplied record would cause the file to exceed
|
||||||
|
the size limit we have.
|
||||||
|
"""
|
||||||
|
if self.stream is None: # delay was set...
|
||||||
|
self.stream = self._open()
|
||||||
|
if self.maxBytes > 0: # are we rolling over?
|
||||||
|
msg = "%s\n" % self.format(record)
|
||||||
|
# due to non-posix-compliant Windows feature
|
||||||
|
self.stream.seek(0, 2)
|
||||||
|
if self.stream.tell() + len(msg) >= self.maxBytes:
|
||||||
|
return 1
|
||||||
|
t = int(time.time())
|
||||||
|
if t >= self.rolloverAt:
|
||||||
|
return 1
|
||||||
|
return 0
|
@ -27,6 +27,7 @@ psycopg-binary==3.1.12
|
|||||||
psycopg-c==3.1.12
|
psycopg-c==3.1.12
|
||||||
scout-apm==2.26.1
|
scout-apm==2.26.1
|
||||||
openpyxl==3.1.2
|
openpyxl==3.1.2
|
||||||
|
python-json-logger==2.0.7
|
||||||
beautifulsoup4==4.12.2
|
beautifulsoup4==4.12.2
|
||||||
dj-database-url==2.1.0
|
dj-database-url==2.1.0
|
||||||
posthog==3.0.2
|
posthog==3.0.2
|
||||||
|
@ -70,6 +70,8 @@ services:
|
|||||||
command: ./bin/takeoff
|
command: ./bin/takeoff
|
||||||
deploy:
|
deploy:
|
||||||
replicas: ${API_REPLICAS:-1}
|
replicas: ${API_REPLICAS:-1}
|
||||||
|
volumes:
|
||||||
|
- logs_api:/code/plane/logs
|
||||||
depends_on:
|
depends_on:
|
||||||
- plane-db
|
- plane-db
|
||||||
- plane-redis
|
- plane-redis
|
||||||
@ -80,6 +82,8 @@ services:
|
|||||||
pull_policy: ${PULL_POLICY:-always}
|
pull_policy: ${PULL_POLICY:-always}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: ./bin/worker
|
command: ./bin/worker
|
||||||
|
volumes:
|
||||||
|
- logs_worker:/code/plane/logs
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
- plane-db
|
- plane-db
|
||||||
@ -91,6 +95,8 @@ services:
|
|||||||
pull_policy: ${PULL_POLICY:-always}
|
pull_policy: ${PULL_POLICY:-always}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: ./bin/beat
|
command: ./bin/beat
|
||||||
|
volumes:
|
||||||
|
- logs_beat-worker:/code/plane/logs
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
- plane-db
|
- plane-db
|
||||||
@ -104,6 +110,8 @@ services:
|
|||||||
command: >
|
command: >
|
||||||
sh -c "python manage.py wait_for_db &&
|
sh -c "python manage.py wait_for_db &&
|
||||||
python manage.py migrate"
|
python manage.py migrate"
|
||||||
|
volumes:
|
||||||
|
- logs_migrator:/code/plane/logs
|
||||||
depends_on:
|
depends_on:
|
||||||
- plane-db
|
- plane-db
|
||||||
- plane-redis
|
- plane-redis
|
||||||
@ -149,3 +157,7 @@ volumes:
|
|||||||
pgdata:
|
pgdata:
|
||||||
redisdata:
|
redisdata:
|
||||||
uploads:
|
uploads:
|
||||||
|
logs_api:
|
||||||
|
logs_worker:
|
||||||
|
logs_beat-worker:
|
||||||
|
logs_migrator:
|
||||||
|
Loading…
Reference in New Issue
Block a user