2026-01-27 08:24:22 +00:00
|
|
|
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
# See the LICENSE file for details.
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
"""Global Settings"""
|
2024-02-23 16:17:25 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Python imports
|
2026-04-20 09:58:33 +00:00
|
|
|
import ipaddress
|
|
|
|
|
import logging
|
2022-11-29 21:17:42 +00:00
|
|
|
import os
|
2023-11-15 07:01:52 +00:00
|
|
|
from urllib.parse import urlparse
|
2025-05-05 13:28:24 +00:00
|
|
|
from urllib.parse import urljoin
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Third party imports
|
|
|
|
|
import dj_database_url
|
2024-03-18 08:57:02 +00:00
|
|
|
|
|
|
|
|
# Django imports
|
|
|
|
|
from django.core.management.utils import get_random_secret_key
|
2024-07-02 12:39:14 +00:00
|
|
|
from corsheaders.defaults import default_headers
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2024-10-03 19:44:29 +00:00
|
|
|
|
2025-05-05 13:28:24 +00:00
|
|
|
# Module imports
|
|
|
|
|
from plane.utils.url import is_valid_url
|
|
|
|
|
|
|
|
|
|
|
2022-11-29 21:17:42 +00:00
|
|
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
2026-06-23 12:29:19 +00:00
|
|
|
_logger = logging.getLogger("plane")
|
|
|
|
|
|
|
|
|
|
# Secret Key — use `or` so an explicitly empty env var is treated the same as unset,
|
|
|
|
|
# falling back to a random key rather than passing "" to Django (GHSA-cmwv-pjmw-8483).
|
|
|
|
|
SECRET_KEY = os.environ.get("SECRET_KEY") or get_random_secret_key()
|
|
|
|
|
# Refuse to run silently with a publicly-known or placeholder SECRET_KEY
|
|
|
|
|
# (GHSA-cmwv-pjmw-8483). Emit a critical log so operators notice immediately.
|
|
|
|
|
# The `or get_random_secret_key()` above means the only way to reach this branch
|
|
|
|
|
# is if the environment explicitly passes one of the flagged values.
|
|
|
|
|
_INSECURE_SECRET_KEYS = {
|
|
|
|
|
"60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5", # old publicly-known default
|
|
|
|
|
"change-this-key-on-deployment", # placeholder shipped in community templates
|
|
|
|
|
}
|
|
|
|
|
if SECRET_KEY in _INSECURE_SECRET_KEYS:
|
|
|
|
|
_logger.critical(
|
|
|
|
|
"SECURITY: SECRET_KEY is set to a known insecure or placeholder value. "
|
|
|
|
|
"This makes your installation vulnerable to session forgery, CSRF bypass, and "
|
|
|
|
|
"password-reset token forging. Set a unique SECRET_KEY before deploying to production. "
|
|
|
|
|
"Generate one with: "
|
|
|
|
|
"python3 -c \"from django.utils.crypto import get_random_secret_key; print(get_random_secret_key())\""
|
|
|
|
|
)
|
2022-11-29 21:17:42 +00:00
|
|
|
|
|
|
|
|
# SECURITY WARNING: don't run with debug turned on in production!
|
2024-03-18 08:57:02 +00:00
|
|
|
DEBUG = int(os.environ.get("DEBUG", "0"))
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2025-12-03 10:23:42 +00:00
|
|
|
# Self-hosted mode
|
|
|
|
|
IS_SELF_MANAGED = True
|
|
|
|
|
|
2026-04-20 09:58:33 +00:00
|
|
|
# Webhook IP allowlist — comma-separated IPs or CIDR ranges that are allowed as
|
|
|
|
|
# webhook targets even if they resolve to private networks.
|
|
|
|
|
# Example: "10.0.0.0/8,192.168.1.0/24,172.16.0.5"
|
|
|
|
|
_webhook_allowed_ips_raw = os.environ.get("WEBHOOK_ALLOWED_IPS", "")
|
|
|
|
|
WEBHOOK_ALLOWED_IPS = []
|
|
|
|
|
for _cidr in _webhook_allowed_ips_raw.split(","):
|
|
|
|
|
_cidr = _cidr.strip()
|
|
|
|
|
if not _cidr:
|
|
|
|
|
continue
|
|
|
|
|
try:
|
|
|
|
|
WEBHOOK_ALLOWED_IPS.append(ipaddress.ip_network(_cidr, strict=False))
|
|
|
|
|
except ValueError:
|
|
|
|
|
_logger.warning("WEBHOOK_ALLOWED_IPS: skipping invalid entry %r", _cidr)
|
|
|
|
|
|
2026-05-14 19:27:39 +00:00
|
|
|
# Webhook hostname allowlist — comma-separated hostnames that bypass the
|
|
|
|
|
# private-IP SSRF check. Useful for trusted internal services whose IPs are
|
|
|
|
|
# dynamic in containerised deployments (e.g. docker-compose service DNS,
|
|
|
|
|
# kubernetes service hostnames).
|
|
|
|
|
# Example: "silo,silo.namespace.svc.cluster.local,internal-api.lan"
|
|
|
|
|
_webhook_allowed_hosts_raw = os.environ.get("WEBHOOK_ALLOWED_HOSTS", "")
|
|
|
|
|
WEBHOOK_ALLOWED_HOSTS = [
|
|
|
|
|
_host.strip().rstrip(".").lower()
|
|
|
|
|
for _host in _webhook_allowed_hosts_raw.split(",")
|
|
|
|
|
if _host.strip()
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Webhook disallowed domains — comma-separated hostnames. Webhooks targeting
|
|
|
|
|
# these domains or any of their subdomains are rejected (the request host is
|
|
|
|
|
# always appended at validation time as a loop-back guard). Empty by default
|
|
|
|
|
# for self-hosted deployments; set to e.g. "plane.so" to block specific domains.
|
|
|
|
|
_webhook_disallowed_domains_raw = os.environ.get("WEBHOOK_DISALLOWED_DOMAINS", "")
|
|
|
|
|
WEBHOOK_DISALLOWED_DOMAINS = [
|
|
|
|
|
_d.strip().rstrip(".").lower()
|
|
|
|
|
for _d in _webhook_disallowed_domains_raw.split(",")
|
|
|
|
|
if _d.strip()
|
|
|
|
|
]
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Allowed Hosts
|
2025-04-02 17:39:27 +00:00
|
|
|
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "*").split(",")
|
2022-11-29 21:17:42 +00:00
|
|
|
|
|
|
|
|
# Application definition
|
|
|
|
|
INSTALLED_APPS = [
|
|
|
|
|
"django.contrib.auth",
|
|
|
|
|
"django.contrib.contenttypes",
|
|
|
|
|
"django.contrib.sessions",
|
2025-12-09 15:35:26 +00:00
|
|
|
"django.contrib.staticfiles",
|
2022-11-29 21:17:42 +00:00
|
|
|
# Inhouse apps
|
|
|
|
|
"plane.analytics",
|
2023-11-20 06:29:20 +00:00
|
|
|
"plane.app",
|
|
|
|
|
"plane.space",
|
2022-11-29 21:17:42 +00:00
|
|
|
"plane.bgtasks",
|
|
|
|
|
"plane.db",
|
|
|
|
|
"plane.utils",
|
|
|
|
|
"plane.web",
|
|
|
|
|
"plane.middleware",
|
2023-11-18 10:47:01 +00:00
|
|
|
"plane.license",
|
2023-11-20 10:28:17 +00:00
|
|
|
"plane.api",
|
2024-05-08 17:31:20 +00:00
|
|
|
"plane.authentication",
|
2022-11-29 21:17:42 +00:00
|
|
|
# Third-party things
|
|
|
|
|
"rest_framework",
|
|
|
|
|
"corsheaders",
|
2023-07-11 09:05:20 +00:00
|
|
|
"django_celery_beat",
|
2022-11-29 21:17:42 +00:00
|
|
|
]
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Middlewares
|
2022-11-29 21:17:42 +00:00
|
|
|
MIDDLEWARE = [
|
|
|
|
|
"corsheaders.middleware.CorsMiddleware",
|
|
|
|
|
"django.middleware.security.SecurityMiddleware",
|
2025-12-09 15:35:26 +00:00
|
|
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
2024-05-08 17:31:20 +00:00
|
|
|
"plane.authentication.middleware.session.SessionMiddleware",
|
2022-11-29 21:17:42 +00:00
|
|
|
"django.middleware.common.CommonMiddleware",
|
|
|
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
|
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
|
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
|
|
|
"crum.CurrentRequestUserMiddleware",
|
2023-03-27 18:06:13 +00:00
|
|
|
"django.middleware.gzip.GZipMiddleware",
|
2025-10-14 11:51:11 +00:00
|
|
|
"plane.middleware.request_body_size.RequestBodySizeLimitMiddleware",
|
2025-04-22 12:11:58 +00:00
|
|
|
"plane.middleware.logger.APITokenLogMiddleware",
|
2025-04-11 12:29:19 +00:00
|
|
|
"plane.middleware.logger.RequestLoggerMiddleware",
|
2023-11-15 07:01:52 +00:00
|
|
|
]
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Rest Framework settings
|
2022-11-29 21:17:42 +00:00
|
|
|
REST_FRAMEWORK = {
|
2025-12-03 10:23:42 +00:00
|
|
|
"DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework.authentication.SessionAuthentication",),
|
2025-11-20 09:35:01 +00:00
|
|
|
"DEFAULT_THROTTLE_CLASSES": ("rest_framework.throttling.AnonRateThrottle",),
|
|
|
|
|
"DEFAULT_THROTTLE_RATES": {
|
|
|
|
|
"anon": "30/minute",
|
|
|
|
|
"asset_id": "5/minute",
|
|
|
|
|
},
|
2024-11-21 12:12:44 +00:00
|
|
|
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
2022-11-29 21:17:42 +00:00
|
|
|
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
|
2024-11-21 12:12:44 +00:00
|
|
|
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
|
2024-05-08 17:31:20 +00:00
|
|
|
"EXCEPTION_HANDLER": "plane.authentication.adapter.exception.auth_exception_handler",
|
[WEB-4045] feat: restructuring of the external APIs for better maintainability (#7477)
* Basic setup for drf-spectacular
* Updated to only handle /api/v1 endpoints
* feat: add asset and user endpoints with URL routing
- Introduced new asset-related endpoints for user assets and server assets, allowing for asset uploads and management.
- Added user endpoint to retrieve current user information.
- Updated URL routing to include new asset and user patterns.
- Enhanced issue handling with a new search endpoint for issues across multiple fields.
- Expanded member management with a new endpoint for workspace members.
* Group endpoints by tags
* Detailed schema definitions and examples for asset endpoints
* Removed unnecessary extension
* Specify avatar_url field separately
* chore: add project docs
* chore: correct all errors
* chore: added open spec in work items
* feat: enhance cycle API endpoints with detailed OpenAPI specifications
- Updated CycleAPIEndpoint and CycleIssueAPIEndpoint to include detailed OpenAPI schema definitions for GET, POST, PATCH, and DELETE operations.
- Specified allowed HTTP methods for each endpoint in the URL routing.
- Improved documentation for cycle creation, updating, and deletion, including request and response examples.
* chore: added open spec in labels
* chore: work item properties
* feat: enhance API endpoints with OpenAPI specifications and HTTP method definitions
- Added detailed OpenAPI schema definitions for various API endpoints including Intake, Module, and State.
- Specified allowed HTTP methods for each endpoint in the URL routing for better clarity and documentation.
- Improved request and response examples for better understanding of API usage.
- Introduced unarchive functionality for cycles and modules with appropriate endpoint definitions.
* chore: run formatter
* Removed unnecessary settings for authentication
* Refactors OpenAPI documentation structure
Improves the organization and maintainability of the OpenAPI documentation by modularizing the `openapi_spec_helpers.py` file.
The changes include:
- Migrates common parameters, responses, examples, and authentication extensions to separate modules.
- Introduces helper decorators for different endpoint types.
- Updates view imports to use the new module paths.
- Removes the legacy `openapi_spec_helpers.py` file.
This refactoring results in a more structured and easier-to-maintain OpenAPI documentation setup.
* Refactor OpenAPI endpoint specifications
- Removed unnecessary parameters from the OpenAPI documentation for various endpoints in the asset, cycle, and project views.
- Updated request structures to improve clarity and consistency across the API documentation.
- Enhanced response formatting for better readability and maintainability.
* Enhance API documentation with detailed endpoint descriptions
Updated various API endpoints across the application to include comprehensive docstrings that clarify their functionality. Each endpoint now features a summary and detailed description, improving the overall understanding of their purpose and usage. This change enhances the OpenAPI specifications for better developer experience and documentation clarity.
* Enhance API serializers and views with new request structures
- Added new serializers for handling cycle and module issue requests, including `CycleIssueRequestSerializer`, `TransferCycleIssueRequestSerializer`, `ModuleIssueRequestSerializer`, and intake issue creation/updating serializers.
- Updated existing serializers to improve clarity and maintainability, including the `UserAssetUploadSerializer` and `IssueAttachmentUploadSerializer`.
- Refactored API views to utilize the new serializers, enhancing the request handling for cycle and intake issue endpoints.
- Improved OpenAPI documentation by replacing inline request definitions with serializer references for better consistency and readability.
* Refactor OpenAPI documentation and endpoint specifications
- Replaced inline schema definitions with dedicated decorators for various endpoint types, enhancing clarity and maintainability.
- Updated API views to utilize new decorators for user, cycle, intake, module, and project endpoints, improving consistency in OpenAPI documentation.
- Removed unnecessary parameters and responses from endpoint specifications, streamlining the documentation for better readability.
- Enhanced the organization of OpenAPI documentation by modularizing endpoint-specific decorators and parameters.
* chore: correct formatting
* chore: correct formatting for all api folder files
* refactor: clean up serializer imports and test setup
- Removed unused `StateLiteSerializer` import from the serializer module.
- Updated test setup to include a noqa comment for the `django_db_setup` fixture, ensuring clarity in the code.
- Added missing commas in user data dictionary for consistency.
* feat: add project creation and update serializers with validation
- Introduced `ProjectCreateSerializer` and `ProjectUpdateSerializer` to handle project creation and updates, respectively.
- Implemented validation to ensure project leads and default assignees are members of the workspace.
- Updated API views to utilize the new serializers for creating and updating projects, enhancing request handling.
- Added OpenAPI documentation references for the new serializers in the project API endpoints.
* feat: update serializers to include additional read-only fields
* refactor: rename intake issue serializers and enhance structure
- Renamed `CreateIntakeIssueRequestSerializer` to `IntakeIssueCreateSerializer` and `UpdateIntakeIssueRequestSerializer` to `IntakeIssueUpdateSerializer` for clarity.
- Introduced `IssueSerializer` for nested issue data in intake requests, improving the organization of serializer logic.
- Updated API views to utilize the new serializer names, ensuring consistency across the codebase.
* refactor: rename issue serializer for intake and enhance API documentation
- Renamed `IssueSerializer` to `IssueForIntakeSerializer` for better clarity in the context of intake issues.
- Updated references in `IntakeIssueCreateSerializer` and `IntakeIssueUpdateSerializer` to use the new `IssueForIntakeSerializer`.
- Added OpenAPI documentation for the `get_workspace_work_item` endpoint, detailing parameters and responses for improved clarity.
* chore: modules and cycles serializers
* feat: add new serializers for label and issue link management
- Introduced `LabelCreateUpdateSerializer`, `IssueLinkCreateSerializer`, `IssueLinkUpdateSerializer`, and `IssueCommentCreateSerializer` to enhance the handling of label and issue link data.
- Updated existing API views to utilize the new serializers for creating and updating labels, issue links, and comments, improving request handling and validation.
- Added `IssueSearchSerializer` for searching issues, streamlining the search functionality in the API.
* Don't consider read only fields as required
* Add setting to separate request and response definitions
* Fixed avatar_url warning on openapi spec generation
* Made spectacular disabled by default
* Moved spectacular settings into separate file and added detailed descriptions to tags
* Specify methods for asset urls
* Better server names
* Enhance API documentation with summaries for various endpoints
- Added summary descriptions for user asset, cycle, intake, issue, member, module, project, state, and user API endpoints to improve clarity and usability of the API documentation.
- Updated the OpenAPI specifications to reflect these changes, ensuring better understanding for developers interacting with the API.
* Add contact information to OpenAPI settings
- Included contact details for Plane in the OpenAPI settings to enhance API documentation and provide developers with a direct point of contact for support.
- This addition aims to improve the overall usability and accessibility of the API documentation.
* Reordered tags and improved description relavancy
* Enhance OpenAPI documentation for cycle and issue endpoints
- Added response definitions for the `get_cycle_issues` and `delete_cycle_issue` methods in the CycleIssueAPIEndpoint to clarify expected outcomes.
- Included additional response codes for the IssueSearchEndpoint to handle various error scenarios, improving the overall API documentation and usability.
* Enhance serializer documentation across multiple files
- Updated docstrings for various serializers including UserAssetUploadSerializer, AssetUpdateSerializer, and others to provide clearer descriptions of their functionality and usage.
- Improved consistency in formatting and language across serializer classes to enhance readability and maintainability.
- Added detailed explanations for new serializers related to project, module, and cycle management, ensuring comprehensive documentation for developers.
* Refactor API endpoints for cycles, intake, modules, projects, and states
- Replaced existing API endpoint classes with more descriptive names such as CycleListCreateAPIEndpoint, CycleDetailAPIEndpoint, IntakeIssueListCreateAPIEndpoint, and others to enhance clarity.
- Updated URL patterns to reflect the new endpoint names, ensuring consistency across the API.
- Improved documentation and method summaries for better understanding of endpoint functionalities.
- Enhanced query handling in the new endpoint classes to streamline data retrieval and improve performance.
* Refactor issue and label API endpoints for clarity and functionality
- Renamed existing API endpoint classes to more descriptive names such as IssueListCreateAPIEndpoint, IssueDetailAPIEndpoint, LabelListCreateAPIEndpoint, and LabelDetailAPIEndpoint to enhance clarity.
- Updated URL patterns to reflect the new endpoint names, ensuring consistency across the API.
- Improved method summaries and documentation for better understanding of endpoint functionalities.
- Streamlined query handling in the new endpoint classes to enhance data retrieval and performance.
* Refactor asset API endpoint methods and introduce new status enums
- Updated the GenericAssetEndpoint to only allow POST requests for asset creation, removing the GET method.
- Modified the get method to require asset_id, ensuring that asset retrieval is always tied to a specific asset.
- Added new IntakeIssueStatus and ModuleStatus enums to improve clarity and management of asset and module states.
- Enhanced OpenAPI settings to include these new enums for better documentation and usability.
* enforce naming convention
* Added LICENSE to openapi spec
* Enhance OpenAPI documentation for various API endpoints
- Updated API endpoints in asset, cycle, intake, issue, module, project, and state views to include OpenApiRequest and OpenApiExample for better request documentation.
- Added example requests for creating and updating resources, improving clarity for API consumers.
- Ensured consistent use of OpenApi utilities across all relevant endpoints to enhance overall API documentation quality.
* Enhance OpenAPI documentation for various API endpoints
- Added detailed descriptions to multiple API endpoints across asset, cycle, intake, issue, module, project, state, and user views to improve clarity for API consumers.
- Ensured consistent documentation practices by including descriptions that outline the purpose and functionality of each endpoint.
- This update aims to enhance the overall usability and understanding of the API documentation.
* Update OpenAPI examples and enhance project queryset logic
- Changed example fields in OpenAPI documentation for issue comments from "content" to "comment_html" to reflect the correct structure.
- Introduced a new `get_queryset` method in the ProjectDetailAPIEndpoint to filter projects based on user membership and workspace, while also annotating additional project-related data such as total members, cycles, and modules.
- Updated permission checks to use the correct attribute name for project identifiers, ensuring accurate permission handling.
* Enhance OpenAPI documentation and add response examples
- Updated multiple API endpoints across asset, cycle, intake, issue, module, project, state, and user views to include new OpenApiResponse examples for better clarity on expected outcomes.
- Introduced new parameters for project and issue identifiers to improve request handling and documentation consistency.
- Enhanced existing responses with detailed examples to aid API consumers in understanding the expected data structure and error handling.
- This update aims to improve the overall usability and clarity of the API documentation.
* refactor: update terminology from 'issues' to 'work items' across multiple API endpoints for consistency and clarity
* use common timezones from pytz for choices
* Moved the openapi utils to the new folder structure
* Added exception logging in GenericAssetEndpoint to improve error handling
* Fixed code rabbit suggestions
* Refactored IssueDetailAPIEndpoint to streamline issue retrieval and response handling, removing redundant external ID checks and custom ordering logic.
---------
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2025-07-24 18:47:05 +00:00
|
|
|
# Preserve original Django URL parameter names (pk) instead of converting to 'id'
|
|
|
|
|
"SCHEMA_COERCE_PATH_PK": False,
|
2022-11-29 21:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 18:43:41 +00:00
|
|
|
# API key throttle rate (DRF SimpleRateThrottle format, e.g. "60/minute")
|
|
|
|
|
API_KEY_RATE_LIMIT = os.environ.get("API_KEY_RATE_LIMIT", "60/minute")
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Django Auth Backend
|
2024-11-21 12:12:44 +00:00
|
|
|
AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",) # default
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Root Urls
|
2022-11-29 21:17:42 +00:00
|
|
|
ROOT_URLCONF = "plane.urls"
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Templates
|
2022-11-29 21:17:42 +00:00
|
|
|
TEMPLATES = [
|
|
|
|
|
{
|
|
|
|
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
2024-11-21 12:12:44 +00:00
|
|
|
"DIRS": ["templates"],
|
2022-11-29 21:17:42 +00:00
|
|
|
"APP_DIRS": True,
|
|
|
|
|
"OPTIONS": {
|
|
|
|
|
"context_processors": [
|
|
|
|
|
"django.template.context_processors.debug",
|
|
|
|
|
"django.template.context_processors.request",
|
|
|
|
|
"django.contrib.auth.context_processors.auth",
|
|
|
|
|
"django.contrib.messages.context_processors.messages",
|
2024-11-21 12:12:44 +00:00
|
|
|
]
|
2022-11-29 21:17:42 +00:00
|
|
|
},
|
2024-11-21 12:12:44 +00:00
|
|
|
}
|
2022-11-29 21:17:42 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# CORS Settings
|
|
|
|
|
CORS_ALLOW_CREDENTIALS = True
|
2023-11-18 10:47:01 +00:00
|
|
|
cors_origins_raw = os.environ.get("CORS_ALLOWED_ORIGINS", "")
|
|
|
|
|
# filter out empty strings
|
2025-09-29 13:45:32 +00:00
|
|
|
cors_allowed_origins = [origin.strip() for origin in cors_origins_raw.split(",") if origin.strip()]
|
2023-11-18 10:47:01 +00:00
|
|
|
if cors_allowed_origins:
|
|
|
|
|
CORS_ALLOWED_ORIGINS = cors_allowed_origins
|
2025-09-29 13:45:32 +00:00
|
|
|
secure_origins = False if [origin for origin in cors_allowed_origins if "http:" in origin] else True
|
2023-11-18 10:47:01 +00:00
|
|
|
else:
|
|
|
|
|
CORS_ALLOW_ALL_ORIGINS = True
|
2024-05-08 17:31:20 +00:00
|
|
|
secure_origins = False
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2024-07-02 12:39:14 +00:00
|
|
|
CORS_ALLOW_HEADERS = [*default_headers, "X-API-Key"]
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Application Settings
|
2022-11-29 21:17:42 +00:00
|
|
|
WSGI_APPLICATION = "plane.wsgi.application"
|
dev: promote stage release to production (#155)
* refractor: removed modules from user.context
* refractor: removed cycles from user context
* refractor: removed state from user context
* feat: implement channel protocol for tracking issue-activites
* refactor: remove blocking code and add todo
* refactor: refactor the consumer with function modules
* feat: add columns for identifiers for easier redirection
* style: minor padding, coloring and consistency changes
* feat: track blocker issues
* feat: track issue after creation
* feat: add runworker in procfile
* refractor: moved all context provider to _app for more clarity
* dev: added our icons
* refractor: removed issues from user context
* refactor: rename db names to plural and remove admin register file
* refactor: integrate permission layer in endpoints
* feat: create product email html templates
* refractor: changed to getServerSide from getInitialProps, removed unused component imports and minor refractoring
* feat: remirror added
* feat: workspace member user details endpoint
* fix: resolved build issue
* refactor: remove www
* feat: workspace details on user endpoint
* feat: added authorization in project settings
refractor: improved code readability
* fix: removed hard-coded workspace slug value, and added workspace in user interface
* refactor: invitation workflow for already existing users
* feat: modified remirror, fix: issue details sidebar
* fix: merge conflicts
* fix: merge conflicts
* fix: added missing dependencies
* refactor: remove user dependency from invitations
* refactor: issue description context is updated with manager
* dev: redis instance rewrite for ssl settings and remove REDIS_TLS env variable
* chore: upgrade python package requirements
* dev: added new migrations for changes
* dev: ssl config for django channels redis connection
* chore: upgrade channels requirements
* refactor: better function for connecting with redis ssl django channels
* chore: cleanup on manifest file
* revert: user endpoint changes
* build: setup asgi
* refactor: update invitation endpoint to do bulk operations
* style: cycles page, custom listbox, issue details page
* refractor: removed folder that were moved to workspaceSlug
* dev: uvicorn in requirements
* Update index.tsx
* refactor: get workspace slug on user endpoint
* fix: workspace slug redirections and slug value in user context
* fix: user context bugs, drag and drop in cycles and modules
* fix: merge conflicts
* fix: user context and create issue modal
* refactor: add extra columns for json and html description and script for back migrating old issues
* refactor: move all 500 errors to 400
* refractor: removed active project, active workspace, projects, and workspaces from user context
* refractor: change from /home to /, added home page redirection logic
added explict GET method on fetch request, and fixed invitation page not fetching all invitations
* fix: passing project id in command palette
* style: home page, feat: image in remirror
* fix: bugs
* chore: remove test_runner workflow from github actions
* dev: update Procfile worker count and python runtime upgrade
* refactor: update response from 404 to 403
* feat: filtering using both name and issue identifier in command palette
showing my issues instead of project issue in command palette, hiding again according to route in command palette
* fix: mutation on different CRUD operations
* fix: redirection in my issues pages
* feat: added authorization in workspace settings, moved command palette to app-layout
* feat: endpoint and column to store my issue props
* style: authorization new design,
fix: made whole button on authorization page clickable, lib/auth on unsuccessful api call redirecting to error page
* feat: return project details on modules and cycles
* fix: create cycle and state coming below issue modal, showing loader for rich text editor
refractor: changed from sprint to cycle in issue type
* fix: issue delete mustation
and some code refractor
* fix: mutation bugs, remirror bugs, style: consistent droopdowns and buttons
* feat: user role in model
* dev: added new migrations
* fix: add url for workspace availability check
* feat: onboarding screens
* fix: update url for workspace name check and add authentication layer and
fix invitation endpoint
* refactor: bulk invitations message
* refactor: response on workspace invitarions
* refactor: update identifier endpoint
* refactor: invitations endpoint
* feat: onboarding logic and validations
* fix: email striep
* dev: added workspace space member unique_together
* chore: back populate neccesary data for description field
* feat: emoji-picker gets close on select, public will be default option in create project
* fix: update error in project creation
* fix: mutation error on issue count in kanban view
some minor code refractoring
* fix: module bugs
* fix: issue activities and issue comments mutation handled at issue detail
* fix: error message for creating updates without permissions
* fix: showing no user left to invite in project invite
fix: - mutation in project settings control, style: - showing loader in project settings controller, - showing request pending for user that hasn't accepted invitation
* refactor: file asset upload directory
* fix: update last workspace id on user invitation accept
* style: onboarding screens
* style: cycles, issue activity
* feat: add json and html column in issue comments
* fix: submitting create issue modal on enter click, project not getting deselected
* feat: file size validator
* fix: emoji picker not closing on all emoji select
* feat: added validation in identifier such that it only accept uppercase text
* dev: commenting is now richer
* fix: shortcuts not getting opened in settings layouts
* style: showing sidebar on unauthorized pages
* fix: error code on exception
* fix: add issue button is working on my issues pages
* feat: new way of assets
* fix: updated activity content for description field
* fix: mutation on project settings control
style: blocker and blocked changed to outline button
* fix: description activity logging
* refactor: check for workspace slug on workspace creation
* fix: typo on workspace url check
* fix: workspace name uniqueness
* fix: remove workspace from read only field
* fix: file upload endpoint, workspace slug check
* chore: drop unique_together constraint for name and workspace
* chore: settings files cleanup and use PubSub backend on django channels
* chore: change in channels backend
* refactor: issue activity api to combine comments
* fix: instance created at key
* fix: result list
* style: create project, cycle modal, view dropdown
* feat: merged issue activities and issue comments into a single section
* fix: remirror dynamic update of issue description
* fix: removed commented code
* fix: issue acitivties mutation
* fix: empty comments cant be submitted
* fix: workspace avatar has been updated while loading
* refactor: update docker-compose to run redis and database in heroku and docker environment
* refactor: removesingle docker file configuration
* refactor: update take off script to run in asgi
* docs: added workspace, quickstart documentation
* fix: reading editor values on focus out
* refactor: cleanup environment variables and create .env.example
* refactor: add extra variables in example env
* fix: warning and erros on console
lazy loading images with low priority, added validation on onboarding for user to either join or create workspace, on onboarding user can't click button while form is getting submitted, profile page going into loading state when updated, refractor: made some state local, removed unnecessary console logs and comments, changed some variable and function name to make more sence
* feat: env examples
* fix: workspace member does not exist
* fi: remove pagination from issue list api
* refactor: remove env example from root
* feat: documentation for projects on plane
* feat: create code of conduct and contributing guidelines
* fix: update docker setup to check handle redis
* revert: bring back pagination to avoid breaking
* feat: made image uploader modal, used it in profile page and workspace page,
delete project from project settings page, join project modal in project list page
* feat: create workspace page, style: made ui consistent
* style: updated onboarding and create workspace page design
* style: responsive sidebar
* fix: updated ui imports
2023-01-10 18:25:47 +00:00
|
|
|
ASGI_APPLICATION = "plane.asgi.application"
|
2022-11-29 21:17:42 +00:00
|
|
|
|
|
|
|
|
# Django Sites
|
|
|
|
|
SITE_ID = 1
|
|
|
|
|
|
|
|
|
|
# User Model
|
|
|
|
|
AUTH_USER_MODEL = "db.User"
|
|
|
|
|
|
|
|
|
|
# Database
|
2023-11-15 07:01:52 +00:00
|
|
|
if bool(os.environ.get("DATABASE_URL")):
|
|
|
|
|
# Parse database configuration from $DATABASE_URL
|
2024-11-21 12:12:44 +00:00
|
|
|
DATABASES = {"default": dj_database_url.config()}
|
2023-11-15 07:01:52 +00:00
|
|
|
else:
|
|
|
|
|
DATABASES = {
|
|
|
|
|
"default": {
|
|
|
|
|
"ENGINE": "django.db.backends.postgresql",
|
|
|
|
|
"NAME": os.environ.get("POSTGRES_DB"),
|
|
|
|
|
"USER": os.environ.get("POSTGRES_USER"),
|
|
|
|
|
"PASSWORD": os.environ.get("POSTGRES_PASSWORD"),
|
|
|
|
|
"HOST": os.environ.get("POSTGRES_HOST"),
|
2024-05-20 06:59:03 +00:00
|
|
|
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
|
2023-11-15 07:01:52 +00:00
|
|
|
}
|
2022-11-29 21:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-28 12:11:02 +00:00
|
|
|
|
|
|
|
|
if os.environ.get("ENABLE_READ_REPLICA", "0") == "1":
|
|
|
|
|
if bool(os.environ.get("DATABASE_READ_REPLICA_URL")):
|
|
|
|
|
# Parse database configuration from $DATABASE_URL
|
2025-09-29 13:45:32 +00:00
|
|
|
DATABASES["replica"] = dj_database_url.parse(os.environ.get("DATABASE_READ_REPLICA_URL"))
|
2025-07-28 12:11:02 +00:00
|
|
|
else:
|
|
|
|
|
DATABASES["replica"] = {
|
|
|
|
|
"ENGINE": "django.db.backends.postgresql",
|
|
|
|
|
"NAME": os.environ.get("POSTGRES_READ_REPLICA_DB"),
|
|
|
|
|
"USER": os.environ.get("POSTGRES_READ_REPLICA_USER"),
|
|
|
|
|
"PASSWORD": os.environ.get("POSTGRES_READ_REPLICA_PASSWORD"),
|
|
|
|
|
"HOST": os.environ.get("POSTGRES_READ_REPLICA_HOST"),
|
|
|
|
|
"PORT": os.environ.get("POSTGRES_READ_REPLICA_PORT", "5432"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Database Routers
|
|
|
|
|
DATABASE_ROUTERS = ["plane.utils.core.dbrouters.ReadReplicaRouter"]
|
|
|
|
|
# Add middleware at the end for read replica routing
|
|
|
|
|
MIDDLEWARE.append("plane.middleware.db_routing.ReadReplicaRoutingMiddleware")
|
|
|
|
|
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Redis Config
|
|
|
|
|
REDIS_URL = os.environ.get("REDIS_URL")
|
2023-11-20 16:02:00 +00:00
|
|
|
REDIS_SSL = REDIS_URL and "rediss" in REDIS_URL
|
2023-11-15 07:01:52 +00:00
|
|
|
|
|
|
|
|
if REDIS_SSL:
|
|
|
|
|
CACHES = {
|
|
|
|
|
"default": {
|
|
|
|
|
"BACKEND": "django_redis.cache.RedisCache",
|
|
|
|
|
"LOCATION": REDIS_URL,
|
|
|
|
|
"OPTIONS": {
|
|
|
|
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
|
|
|
"CONNECTION_POOL_KWARGS": {"ssl_cert_reqs": False},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
CACHES = {
|
|
|
|
|
"default": {
|
|
|
|
|
"BACKEND": "django_redis.cache.RedisCache",
|
|
|
|
|
"LOCATION": REDIS_URL,
|
2024-11-21 12:12:44 +00:00
|
|
|
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
2023-11-15 07:01:52 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Password validations
|
2022-11-29 21:17:42 +00:00
|
|
|
AUTH_PASSWORD_VALIDATORS = [
|
2025-09-29 13:45:32 +00:00
|
|
|
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
2024-11-21 12:12:44 +00:00
|
|
|
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
|
|
|
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|
|
|
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
2022-11-29 21:17:42 +00:00
|
|
|
]
|
|
|
|
|
|
2023-11-29 15:03:37 +00:00
|
|
|
# Password reset time the number of seconds the uniquely generated uid will be valid
|
|
|
|
|
PASSWORD_RESET_TIMEOUT = 3600
|
|
|
|
|
|
2022-11-29 21:17:42 +00:00
|
|
|
# Static files (CSS, JavaScript, Images)
|
|
|
|
|
STATIC_URL = "/static/"
|
|
|
|
|
STATIC_ROOT = os.path.join(BASE_DIR, "static-assets", "collected-static")
|
|
|
|
|
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
|
|
|
|
|
|
|
|
|
|
# Media Settings
|
|
|
|
|
MEDIA_ROOT = "mediafiles"
|
|
|
|
|
MEDIA_URL = "/media/"
|
|
|
|
|
|
|
|
|
|
# Internationalization
|
|
|
|
|
LANGUAGE_CODE = "en-us"
|
|
|
|
|
USE_I18N = True
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Timezones
|
2022-11-29 21:17:42 +00:00
|
|
|
USE_TZ = True
|
2023-11-15 07:01:52 +00:00
|
|
|
TIME_ZONE = "UTC"
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Default Auto Field
|
2022-11-29 21:17:42 +00:00
|
|
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Email settings
|
2022-11-29 21:17:42 +00:00
|
|
|
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
|
|
|
|
|
2023-11-15 07:01:52 +00:00
|
|
|
# Storage Settings
|
2024-05-30 10:52:47 +00:00
|
|
|
# Use Minio settings
|
|
|
|
|
USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1
|
|
|
|
|
|
2025-09-29 13:45:32 +00:00
|
|
|
STORAGES = {"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"}}
|
2024-11-21 12:12:44 +00:00
|
|
|
STORAGES["default"] = {"BACKEND": "plane.settings.storage.S3Storage"}
|
2023-11-15 07:01:52 +00:00
|
|
|
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "access-key")
|
|
|
|
|
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "secret-key")
|
2023-11-21 12:22:19 +00:00
|
|
|
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_S3_BUCKET_NAME", "uploads")
|
2023-11-20 11:04:57 +00:00
|
|
|
AWS_REGION = os.environ.get("AWS_REGION", "")
|
2023-11-15 07:01:52 +00:00
|
|
|
AWS_DEFAULT_ACL = "public-read"
|
|
|
|
|
AWS_QUERYSTRING_AUTH = False
|
|
|
|
|
AWS_S3_FILE_OVERWRITE = False
|
2025-09-29 13:45:32 +00:00
|
|
|
AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", None) or os.environ.get("MINIO_ENDPOINT_URL", None)
|
2024-05-30 10:52:47 +00:00
|
|
|
if AWS_S3_ENDPOINT_URL and USE_MINIO:
|
2023-11-15 07:01:52 +00:00
|
|
|
parsed_url = urlparse(os.environ.get("WEB_URL", "http://localhost"))
|
2023-11-21 12:22:19 +00:00
|
|
|
AWS_S3_CUSTOM_DOMAIN = f"{parsed_url.netloc}/{AWS_STORAGE_BUCKET_NAME}"
|
2023-11-15 07:01:52 +00:00
|
|
|
AWS_S3_URL_PROTOCOL = f"{parsed_url.scheme}:"
|
2022-11-29 21:17:42 +00:00
|
|
|
|
2024-09-02 12:10:17 +00:00
|
|
|
# RabbitMQ connection settings
|
|
|
|
|
RABBITMQ_HOST = os.environ.get("RABBITMQ_HOST", "localhost")
|
|
|
|
|
RABBITMQ_PORT = os.environ.get("RABBITMQ_PORT", "5672")
|
|
|
|
|
RABBITMQ_USER = os.environ.get("RABBITMQ_USER", "guest")
|
|
|
|
|
RABBITMQ_PASSWORD = os.environ.get("RABBITMQ_PASSWORD", "guest")
|
|
|
|
|
RABBITMQ_VHOST = os.environ.get("RABBITMQ_VHOST", "/")
|
|
|
|
|
AMQP_URL = os.environ.get("AMQP_URL")
|
2023-11-15 07:01:52 +00:00
|
|
|
|
|
|
|
|
# Celery Configuration
|
2024-09-02 12:10:17 +00:00
|
|
|
if AMQP_URL:
|
|
|
|
|
CELERY_BROKER_URL = AMQP_URL
|
|
|
|
|
else:
|
|
|
|
|
CELERY_BROKER_URL = f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASSWORD}@{RABBITMQ_HOST}:{RABBITMQ_PORT}/{RABBITMQ_VHOST}"
|
2024-09-13 11:29:47 +00:00
|
|
|
|
2023-04-08 10:16:05 +00:00
|
|
|
CELERY_TIMEZONE = TIME_ZONE
|
2023-11-15 07:01:52 +00:00
|
|
|
CELERY_TASK_SERIALIZER = "json"
|
2024-09-02 12:10:17 +00:00
|
|
|
CELERY_RESULT_SERIALIZER = "json"
|
2023-11-15 07:01:52 +00:00
|
|
|
CELERY_ACCEPT_CONTENT = ["application/json"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CELERY_IMPORTS = (
|
2024-04-11 14:57:05 +00:00
|
|
|
# scheduled tasks
|
2023-11-15 07:01:52 +00:00
|
|
|
"plane.bgtasks.issue_automation_task",
|
|
|
|
|
"plane.bgtasks.exporter_expired_task",
|
2023-11-20 09:23:06 +00:00
|
|
|
"plane.bgtasks.file_asset_task",
|
2024-01-23 12:19:22 +00:00
|
|
|
"plane.bgtasks.email_notification_task",
|
2025-08-24 09:43:49 +00:00
|
|
|
"plane.bgtasks.cleanup_task",
|
2026-05-28 13:04:27 +00:00
|
|
|
"plane.license.bgtasks.telemetry_metrics",
|
2024-04-11 14:57:05 +00:00
|
|
|
# management tasks
|
|
|
|
|
"plane.bgtasks.dummy_data_task",
|
2024-12-13 17:00:29 +00:00
|
|
|
# issue version tasks
|
|
|
|
|
"plane.bgtasks.issue_version_sync",
|
|
|
|
|
"plane.bgtasks.issue_description_version_sync",
|
2023-11-15 07:01:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
FILE_SIZE_LIMIT = int(os.environ.get("FILE_SIZE_LIMIT", 5242880))
|
|
|
|
|
|
|
|
|
|
# Unsplash Access key
|
|
|
|
|
UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY")
|
|
|
|
|
# Github Access Token
|
|
|
|
|
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False)
|
|
|
|
|
|
|
|
|
|
# Analytics
|
|
|
|
|
ANALYTICS_SECRET_KEY = os.environ.get("ANALYTICS_SECRET_KEY", False)
|
|
|
|
|
ANALYTICS_BASE_API = os.environ.get("ANALYTICS_BASE_API", False)
|
|
|
|
|
|
2023-12-07 09:21:27 +00:00
|
|
|
# Skip environment variable configuration
|
|
|
|
|
SKIP_ENV_VAR = os.environ.get("SKIP_ENV_VAR", "1") == "1"
|
2023-12-21 08:56:44 +00:00
|
|
|
|
|
|
|
|
DATA_UPLOAD_MAX_MEMORY_SIZE = int(os.environ.get("FILE_SIZE_LIMIT", 5242880))
|
2024-05-08 17:31:20 +00:00
|
|
|
|
|
|
|
|
# Cookie Settings
|
|
|
|
|
SESSION_COOKIE_SECURE = secure_origins
|
|
|
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
|
|
|
SESSION_ENGINE = "plane.db.models.session"
|
2025-10-16 10:06:48 +00:00
|
|
|
SESSION_COOKIE_AGE = int(os.environ.get("SESSION_COOKIE_AGE", 604800))
|
2024-09-13 11:29:47 +00:00
|
|
|
SESSION_COOKIE_NAME = os.environ.get("SESSION_COOKIE_NAME", "session-id")
|
2024-05-08 17:31:20 +00:00
|
|
|
SESSION_COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", None)
|
2024-11-21 12:12:44 +00:00
|
|
|
SESSION_SAVE_EVERY_REQUEST = os.environ.get("SESSION_SAVE_EVERY_REQUEST", "0") == "1"
|
2024-05-08 17:31:20 +00:00
|
|
|
|
|
|
|
|
# Admin Cookie
|
2024-09-13 11:29:47 +00:00
|
|
|
ADMIN_SESSION_COOKIE_NAME = "admin-session-id"
|
2025-10-16 10:06:48 +00:00
|
|
|
ADMIN_SESSION_COOKIE_AGE = int(os.environ.get("ADMIN_SESSION_COOKIE_AGE", 3600))
|
2024-05-08 17:31:20 +00:00
|
|
|
|
|
|
|
|
# CSRF cookies
|
|
|
|
|
CSRF_COOKIE_SECURE = secure_origins
|
|
|
|
|
CSRF_COOKIE_HTTPONLY = True
|
|
|
|
|
CSRF_TRUSTED_ORIGINS = cors_allowed_origins
|
|
|
|
|
CSRF_COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", None)
|
2024-05-21 09:34:10 +00:00
|
|
|
CSRF_FAILURE_VIEW = "plane.authentication.views.common.csrf_failure"
|
2024-05-09 21:02:42 +00:00
|
|
|
|
2025-05-05 13:28:24 +00:00
|
|
|
###### Base URLs ######
|
|
|
|
|
|
|
|
|
|
# Admin Base URL
|
2024-05-09 21:02:42 +00:00
|
|
|
ADMIN_BASE_URL = os.environ.get("ADMIN_BASE_URL", None)
|
2025-05-05 13:28:24 +00:00
|
|
|
if ADMIN_BASE_URL and not is_valid_url(ADMIN_BASE_URL):
|
|
|
|
|
ADMIN_BASE_URL = None
|
|
|
|
|
ADMIN_BASE_PATH = os.environ.get("ADMIN_BASE_PATH", "/god-mode/")
|
|
|
|
|
|
|
|
|
|
# Space Base URL
|
2024-05-09 21:02:42 +00:00
|
|
|
SPACE_BASE_URL = os.environ.get("SPACE_BASE_URL", None)
|
2025-05-05 13:28:24 +00:00
|
|
|
if SPACE_BASE_URL and not is_valid_url(SPACE_BASE_URL):
|
|
|
|
|
SPACE_BASE_URL = None
|
|
|
|
|
SPACE_BASE_PATH = os.environ.get("SPACE_BASE_PATH", "/spaces/")
|
|
|
|
|
|
|
|
|
|
# App Base URL
|
|
|
|
|
APP_BASE_URL = os.environ.get("APP_BASE_URL", None)
|
|
|
|
|
if APP_BASE_URL and not is_valid_url(APP_BASE_URL):
|
|
|
|
|
APP_BASE_URL = None
|
|
|
|
|
APP_BASE_PATH = os.environ.get("APP_BASE_PATH", "/")
|
|
|
|
|
|
|
|
|
|
# Live Base URL
|
|
|
|
|
LIVE_BASE_URL = os.environ.get("LIVE_BASE_URL", None)
|
|
|
|
|
if LIVE_BASE_URL and not is_valid_url(LIVE_BASE_URL):
|
|
|
|
|
LIVE_BASE_URL = None
|
|
|
|
|
LIVE_BASE_PATH = os.environ.get("LIVE_BASE_PATH", "/live/")
|
|
|
|
|
|
|
|
|
|
LIVE_URL = urljoin(LIVE_BASE_URL, LIVE_BASE_PATH) if LIVE_BASE_URL else None
|
|
|
|
|
|
|
|
|
|
# WEB URL
|
2025-04-02 17:39:27 +00:00
|
|
|
WEB_URL = os.environ.get("WEB_URL")
|
2024-07-30 14:35:08 +00:00
|
|
|
|
|
|
|
|
HARD_DELETE_AFTER_DAYS = int(os.environ.get("HARD_DELETE_AFTER_DAYS", 60))
|
2024-10-11 14:43:38 +00:00
|
|
|
|
refactor: logging with retention + API token hardening (#9148)
* fix: harden API token handling against rate-limit tampering and plaintext logging
- Make `allowed_rate_limit` read-only on APITokenSerializer so users can no
longer raise their own API token rate limit via PATCH (GHSA-xfgr-2x3f-g2cf).
- Stop persisting API keys in plaintext in APITokenLogMiddleware: store a
SHA-256 hash as the token identifier and redact sensitive request headers
(X-Api-Key, Authorization, Cookie) before logging (GHSA-r5p8-cj3q-38cc).
* refactor: remove MongoDB log sink and add per-log-type retention
Logs are now written to and cleared from PostgreSQL only; MongoDB is no
longer used as a log sink or archive.
- Drop the MongoDB write/archival paths from the API request logger, the
webhook log writer, and the cleanup tasks; Postgres is the sole sink.
- Cleanup tasks now hard-delete expired rows in batches via `all_objects`
(rows are removed immediately, not soft-deleted).
- Add env-backed, per-log-type retention settings: API activity logs
(API_ACTIVITY_LOG_RETENTION_DAYS, default 14), webhook logs
(WEBHOOK_LOG_RETENTION_DAYS, default 14), email logs
(EMAIL_LOG_RETENTION_DAYS, default 7). HARD_DELETE_AFTER_DAYS no longer
drives any log cleanup.
- Delete settings/mongo.py, remove MONGO_DB_* settings and the plane.mongo
loggers, and drop the pymongo dependency.
* chore: gitignore local advisories.md notes file
* fix: use keyed HMAC-SHA256 for API token log identifier
Address CodeQL "weak hashing of sensitive data" by hashing the API key with
a SECRET_KEY-keyed HMAC instead of a bare SHA-256. The identifier is a
non-reversible tokenization of a high-entropy key (not password storage);
keying it also prevents precomputing the digest from a known key value.
* chore: address review feedback on log cleanup and request logging
- process_logs accepts extra kwargs so jobs enqueued by an older release
(with a mongo_log arg) don't fail during a rolling deploy.
- Log-cleanup batch delete failures are logged and skipped rather than
aborting the run, so a single bad batch can't block the rest.
- Extend logger middleware test to assert Authorization and Cookie headers
are redacted; add a test that a failing cleanup batch is swallowed.
* fix: fall back to default when a log retention env value is invalid
Negative (or unparseable) retention values would compute a future cutoff and
delete every log row. The retention settings now fall back to their defaults
in that case via a shared `_retention_days` helper.
2026-05-27 10:30:05 +00:00
|
|
|
|
|
|
|
|
def _retention_days(env_var, default):
|
|
|
|
|
"""
|
|
|
|
|
Read a retention window (in days) from the environment, falling back to the
|
|
|
|
|
default when the variable is unset, unparseable, or negative — a negative
|
|
|
|
|
window would otherwise select rows with a future cutoff and delete everything.
|
|
|
|
|
"""
|
|
|
|
|
raw = os.environ.get(env_var)
|
|
|
|
|
if raw is None:
|
|
|
|
|
return default
|
|
|
|
|
try:
|
|
|
|
|
days = int(raw)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return default
|
|
|
|
|
return days if days >= 0 else default
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# API activity logs hold request/response payloads, so they are retained for a
|
|
|
|
|
# shorter window than other logs.
|
|
|
|
|
API_ACTIVITY_LOG_RETENTION_DAYS = _retention_days("API_ACTIVITY_LOG_RETENTION_DAYS", 14)
|
|
|
|
|
|
|
|
|
|
# Webhook delivery logs are retained on their own window, independent of the
|
|
|
|
|
# generic HARD_DELETE_AFTER_DAYS.
|
|
|
|
|
WEBHOOK_LOG_RETENTION_DAYS = _retention_days("WEBHOOK_LOG_RETENTION_DAYS", 14)
|
|
|
|
|
|
|
|
|
|
# Email notification logs are retained on their own window.
|
|
|
|
|
EMAIL_LOG_RETENTION_DAYS = _retention_days("EMAIL_LOG_RETENTION_DAYS", 7)
|
|
|
|
|
|
2024-10-29 13:56:00 +00:00
|
|
|
# Instance Changelog URL
|
|
|
|
|
INSTANCE_CHANGELOG_URL = os.environ.get("INSTANCE_CHANGELOG_URL", "")
|
|
|
|
|
|
2024-10-11 14:43:38 +00:00
|
|
|
ATTACHMENT_MIME_TYPES = [
|
|
|
|
|
# Images
|
|
|
|
|
"image/jpeg",
|
|
|
|
|
"image/png",
|
|
|
|
|
"image/gif",
|
|
|
|
|
"image/svg+xml",
|
|
|
|
|
"image/webp",
|
|
|
|
|
"image/tiff",
|
|
|
|
|
"image/bmp",
|
|
|
|
|
# Documents
|
|
|
|
|
"application/pdf",
|
|
|
|
|
"application/msword",
|
|
|
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
|
|
|
"application/vnd.ms-excel",
|
|
|
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
|
|
|
"application/vnd.ms-powerpoint",
|
|
|
|
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
|
|
|
"text/plain",
|
2026-01-23 08:08:47 +00:00
|
|
|
"text/markdown",
|
2024-10-11 14:43:38 +00:00
|
|
|
"application/rtf",
|
2025-01-10 04:50:38 +00:00
|
|
|
"application/vnd.oasis.opendocument.spreadsheet",
|
|
|
|
|
"application/vnd.oasis.opendocument.text",
|
|
|
|
|
"application/vnd.oasis.opendocument.presentation",
|
|
|
|
|
"application/vnd.oasis.opendocument.graphics",
|
|
|
|
|
# Microsoft Visio
|
|
|
|
|
"application/vnd.visio",
|
|
|
|
|
# Netpbm format
|
|
|
|
|
"image/x-portable-graymap",
|
|
|
|
|
"image/x-portable-bitmap",
|
|
|
|
|
"image/x-portable-pixmap",
|
|
|
|
|
# Open Office Bae
|
|
|
|
|
"application/vnd.oasis.opendocument.database",
|
2024-10-11 14:43:38 +00:00
|
|
|
# Audio
|
|
|
|
|
"audio/mpeg",
|
|
|
|
|
"audio/wav",
|
|
|
|
|
"audio/ogg",
|
|
|
|
|
"audio/midi",
|
|
|
|
|
"audio/x-midi",
|
|
|
|
|
"audio/aac",
|
|
|
|
|
"audio/flac",
|
|
|
|
|
"audio/x-m4a",
|
|
|
|
|
# Video
|
|
|
|
|
"video/mp4",
|
|
|
|
|
"video/mpeg",
|
|
|
|
|
"video/ogg",
|
|
|
|
|
"video/webm",
|
|
|
|
|
"video/quicktime",
|
|
|
|
|
"video/x-msvideo",
|
|
|
|
|
"video/x-ms-wmv",
|
|
|
|
|
# Archives
|
|
|
|
|
"application/zip",
|
2025-05-02 11:23:06 +00:00
|
|
|
"application/x-rar",
|
2024-10-11 14:43:38 +00:00
|
|
|
"application/x-rar-compressed",
|
|
|
|
|
"application/x-tar",
|
|
|
|
|
"application/gzip",
|
2025-05-02 11:23:06 +00:00
|
|
|
"application/x-zip",
|
|
|
|
|
"application/x-zip-compressed",
|
|
|
|
|
"application/x-7z-compressed",
|
|
|
|
|
"application/x-compressed",
|
|
|
|
|
"application/x-compressed-tar",
|
|
|
|
|
"application/x-compressed-tar-gz",
|
|
|
|
|
"application/x-compressed-tar-bz2",
|
|
|
|
|
"application/x-compressed-tar-zip",
|
|
|
|
|
"application/x-compressed-tar-7z",
|
|
|
|
|
"application/x-compressed-tar-rar",
|
|
|
|
|
"application/x-compressed-tar-zip",
|
2024-10-11 14:43:38 +00:00
|
|
|
# 3D Models
|
|
|
|
|
"model/gltf-binary",
|
|
|
|
|
"model/gltf+json",
|
|
|
|
|
"application/octet-stream", # for .obj files, but be cautious
|
|
|
|
|
# Fonts
|
|
|
|
|
"font/ttf",
|
|
|
|
|
"font/otf",
|
|
|
|
|
"font/woff",
|
|
|
|
|
"font/woff2",
|
|
|
|
|
# Other
|
|
|
|
|
"text/css",
|
|
|
|
|
"text/javascript",
|
|
|
|
|
"application/json",
|
|
|
|
|
"text/xml",
|
2024-10-24 15:05:27 +00:00
|
|
|
"text/csv",
|
2024-10-11 14:43:38 +00:00
|
|
|
"application/xml",
|
2025-04-24 12:09:16 +00:00
|
|
|
# SQL
|
|
|
|
|
"application/x-sql",
|
2025-04-24 12:20:37 +00:00
|
|
|
# Gzip
|
|
|
|
|
"application/x-gzip",
|
2026-01-23 08:03:20 +00:00
|
|
|
# Markdown
|
|
|
|
|
"text/markdown",
|
2024-10-11 14:43:38 +00:00
|
|
|
]
|
2025-04-29 08:31:22 +00:00
|
|
|
|
2026-07-13 15:03:46 +00:00
|
|
|
# MIME types that browsers can execute as scripts when served inline.
|
|
|
|
|
# These must always be served with Content-Disposition: attachment, even if they
|
|
|
|
|
# somehow end up stored (e.g. uploaded before this restriction was added).
|
|
|
|
|
SCRIPT_CAPABLE_MIME_TYPES: frozenset[str] = frozenset(
|
|
|
|
|
[
|
|
|
|
|
"image/svg+xml", # SVG with onload / embedded <script> tags
|
|
|
|
|
"text/javascript",
|
|
|
|
|
"application/javascript",
|
|
|
|
|
"text/html",
|
|
|
|
|
"application/xhtml+xml",
|
|
|
|
|
"text/xml",
|
|
|
|
|
"application/xml",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2025-04-29 08:31:22 +00:00
|
|
|
# Seed directory path
|
|
|
|
|
SEED_DIR = os.path.join(BASE_DIR, "seeds")
|
[WEB-4045] feat: restructuring of the external APIs for better maintainability (#7477)
* Basic setup for drf-spectacular
* Updated to only handle /api/v1 endpoints
* feat: add asset and user endpoints with URL routing
- Introduced new asset-related endpoints for user assets and server assets, allowing for asset uploads and management.
- Added user endpoint to retrieve current user information.
- Updated URL routing to include new asset and user patterns.
- Enhanced issue handling with a new search endpoint for issues across multiple fields.
- Expanded member management with a new endpoint for workspace members.
* Group endpoints by tags
* Detailed schema definitions and examples for asset endpoints
* Removed unnecessary extension
* Specify avatar_url field separately
* chore: add project docs
* chore: correct all errors
* chore: added open spec in work items
* feat: enhance cycle API endpoints with detailed OpenAPI specifications
- Updated CycleAPIEndpoint and CycleIssueAPIEndpoint to include detailed OpenAPI schema definitions for GET, POST, PATCH, and DELETE operations.
- Specified allowed HTTP methods for each endpoint in the URL routing.
- Improved documentation for cycle creation, updating, and deletion, including request and response examples.
* chore: added open spec in labels
* chore: work item properties
* feat: enhance API endpoints with OpenAPI specifications and HTTP method definitions
- Added detailed OpenAPI schema definitions for various API endpoints including Intake, Module, and State.
- Specified allowed HTTP methods for each endpoint in the URL routing for better clarity and documentation.
- Improved request and response examples for better understanding of API usage.
- Introduced unarchive functionality for cycles and modules with appropriate endpoint definitions.
* chore: run formatter
* Removed unnecessary settings for authentication
* Refactors OpenAPI documentation structure
Improves the organization and maintainability of the OpenAPI documentation by modularizing the `openapi_spec_helpers.py` file.
The changes include:
- Migrates common parameters, responses, examples, and authentication extensions to separate modules.
- Introduces helper decorators for different endpoint types.
- Updates view imports to use the new module paths.
- Removes the legacy `openapi_spec_helpers.py` file.
This refactoring results in a more structured and easier-to-maintain OpenAPI documentation setup.
* Refactor OpenAPI endpoint specifications
- Removed unnecessary parameters from the OpenAPI documentation for various endpoints in the asset, cycle, and project views.
- Updated request structures to improve clarity and consistency across the API documentation.
- Enhanced response formatting for better readability and maintainability.
* Enhance API documentation with detailed endpoint descriptions
Updated various API endpoints across the application to include comprehensive docstrings that clarify their functionality. Each endpoint now features a summary and detailed description, improving the overall understanding of their purpose and usage. This change enhances the OpenAPI specifications for better developer experience and documentation clarity.
* Enhance API serializers and views with new request structures
- Added new serializers for handling cycle and module issue requests, including `CycleIssueRequestSerializer`, `TransferCycleIssueRequestSerializer`, `ModuleIssueRequestSerializer`, and intake issue creation/updating serializers.
- Updated existing serializers to improve clarity and maintainability, including the `UserAssetUploadSerializer` and `IssueAttachmentUploadSerializer`.
- Refactored API views to utilize the new serializers, enhancing the request handling for cycle and intake issue endpoints.
- Improved OpenAPI documentation by replacing inline request definitions with serializer references for better consistency and readability.
* Refactor OpenAPI documentation and endpoint specifications
- Replaced inline schema definitions with dedicated decorators for various endpoint types, enhancing clarity and maintainability.
- Updated API views to utilize new decorators for user, cycle, intake, module, and project endpoints, improving consistency in OpenAPI documentation.
- Removed unnecessary parameters and responses from endpoint specifications, streamlining the documentation for better readability.
- Enhanced the organization of OpenAPI documentation by modularizing endpoint-specific decorators and parameters.
* chore: correct formatting
* chore: correct formatting for all api folder files
* refactor: clean up serializer imports and test setup
- Removed unused `StateLiteSerializer` import from the serializer module.
- Updated test setup to include a noqa comment for the `django_db_setup` fixture, ensuring clarity in the code.
- Added missing commas in user data dictionary for consistency.
* feat: add project creation and update serializers with validation
- Introduced `ProjectCreateSerializer` and `ProjectUpdateSerializer` to handle project creation and updates, respectively.
- Implemented validation to ensure project leads and default assignees are members of the workspace.
- Updated API views to utilize the new serializers for creating and updating projects, enhancing request handling.
- Added OpenAPI documentation references for the new serializers in the project API endpoints.
* feat: update serializers to include additional read-only fields
* refactor: rename intake issue serializers and enhance structure
- Renamed `CreateIntakeIssueRequestSerializer` to `IntakeIssueCreateSerializer` and `UpdateIntakeIssueRequestSerializer` to `IntakeIssueUpdateSerializer` for clarity.
- Introduced `IssueSerializer` for nested issue data in intake requests, improving the organization of serializer logic.
- Updated API views to utilize the new serializer names, ensuring consistency across the codebase.
* refactor: rename issue serializer for intake and enhance API documentation
- Renamed `IssueSerializer` to `IssueForIntakeSerializer` for better clarity in the context of intake issues.
- Updated references in `IntakeIssueCreateSerializer` and `IntakeIssueUpdateSerializer` to use the new `IssueForIntakeSerializer`.
- Added OpenAPI documentation for the `get_workspace_work_item` endpoint, detailing parameters and responses for improved clarity.
* chore: modules and cycles serializers
* feat: add new serializers for label and issue link management
- Introduced `LabelCreateUpdateSerializer`, `IssueLinkCreateSerializer`, `IssueLinkUpdateSerializer`, and `IssueCommentCreateSerializer` to enhance the handling of label and issue link data.
- Updated existing API views to utilize the new serializers for creating and updating labels, issue links, and comments, improving request handling and validation.
- Added `IssueSearchSerializer` for searching issues, streamlining the search functionality in the API.
* Don't consider read only fields as required
* Add setting to separate request and response definitions
* Fixed avatar_url warning on openapi spec generation
* Made spectacular disabled by default
* Moved spectacular settings into separate file and added detailed descriptions to tags
* Specify methods for asset urls
* Better server names
* Enhance API documentation with summaries for various endpoints
- Added summary descriptions for user asset, cycle, intake, issue, member, module, project, state, and user API endpoints to improve clarity and usability of the API documentation.
- Updated the OpenAPI specifications to reflect these changes, ensuring better understanding for developers interacting with the API.
* Add contact information to OpenAPI settings
- Included contact details for Plane in the OpenAPI settings to enhance API documentation and provide developers with a direct point of contact for support.
- This addition aims to improve the overall usability and accessibility of the API documentation.
* Reordered tags and improved description relavancy
* Enhance OpenAPI documentation for cycle and issue endpoints
- Added response definitions for the `get_cycle_issues` and `delete_cycle_issue` methods in the CycleIssueAPIEndpoint to clarify expected outcomes.
- Included additional response codes for the IssueSearchEndpoint to handle various error scenarios, improving the overall API documentation and usability.
* Enhance serializer documentation across multiple files
- Updated docstrings for various serializers including UserAssetUploadSerializer, AssetUpdateSerializer, and others to provide clearer descriptions of their functionality and usage.
- Improved consistency in formatting and language across serializer classes to enhance readability and maintainability.
- Added detailed explanations for new serializers related to project, module, and cycle management, ensuring comprehensive documentation for developers.
* Refactor API endpoints for cycles, intake, modules, projects, and states
- Replaced existing API endpoint classes with more descriptive names such as CycleListCreateAPIEndpoint, CycleDetailAPIEndpoint, IntakeIssueListCreateAPIEndpoint, and others to enhance clarity.
- Updated URL patterns to reflect the new endpoint names, ensuring consistency across the API.
- Improved documentation and method summaries for better understanding of endpoint functionalities.
- Enhanced query handling in the new endpoint classes to streamline data retrieval and improve performance.
* Refactor issue and label API endpoints for clarity and functionality
- Renamed existing API endpoint classes to more descriptive names such as IssueListCreateAPIEndpoint, IssueDetailAPIEndpoint, LabelListCreateAPIEndpoint, and LabelDetailAPIEndpoint to enhance clarity.
- Updated URL patterns to reflect the new endpoint names, ensuring consistency across the API.
- Improved method summaries and documentation for better understanding of endpoint functionalities.
- Streamlined query handling in the new endpoint classes to enhance data retrieval and performance.
* Refactor asset API endpoint methods and introduce new status enums
- Updated the GenericAssetEndpoint to only allow POST requests for asset creation, removing the GET method.
- Modified the get method to require asset_id, ensuring that asset retrieval is always tied to a specific asset.
- Added new IntakeIssueStatus and ModuleStatus enums to improve clarity and management of asset and module states.
- Enhanced OpenAPI settings to include these new enums for better documentation and usability.
* enforce naming convention
* Added LICENSE to openapi spec
* Enhance OpenAPI documentation for various API endpoints
- Updated API endpoints in asset, cycle, intake, issue, module, project, and state views to include OpenApiRequest and OpenApiExample for better request documentation.
- Added example requests for creating and updating resources, improving clarity for API consumers.
- Ensured consistent use of OpenApi utilities across all relevant endpoints to enhance overall API documentation quality.
* Enhance OpenAPI documentation for various API endpoints
- Added detailed descriptions to multiple API endpoints across asset, cycle, intake, issue, module, project, state, and user views to improve clarity for API consumers.
- Ensured consistent documentation practices by including descriptions that outline the purpose and functionality of each endpoint.
- This update aims to enhance the overall usability and understanding of the API documentation.
* Update OpenAPI examples and enhance project queryset logic
- Changed example fields in OpenAPI documentation for issue comments from "content" to "comment_html" to reflect the correct structure.
- Introduced a new `get_queryset` method in the ProjectDetailAPIEndpoint to filter projects based on user membership and workspace, while also annotating additional project-related data such as total members, cycles, and modules.
- Updated permission checks to use the correct attribute name for project identifiers, ensuring accurate permission handling.
* Enhance OpenAPI documentation and add response examples
- Updated multiple API endpoints across asset, cycle, intake, issue, module, project, state, and user views to include new OpenApiResponse examples for better clarity on expected outcomes.
- Introduced new parameters for project and issue identifiers to improve request handling and documentation consistency.
- Enhanced existing responses with detailed examples to aid API consumers in understanding the expected data structure and error handling.
- This update aims to improve the overall usability and clarity of the API documentation.
* refactor: update terminology from 'issues' to 'work items' across multiple API endpoints for consistency and clarity
* use common timezones from pytz for choices
* Moved the openapi utils to the new folder structure
* Added exception logging in GenericAssetEndpoint to improve error handling
* Fixed code rabbit suggestions
* Refactored IssueDetailAPIEndpoint to streamline issue retrieval and response handling, removing redundant external ID checks and custom ordering logic.
---------
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2025-07-24 18:47:05 +00:00
|
|
|
|
|
|
|
|
ENABLE_DRF_SPECTACULAR = os.environ.get("ENABLE_DRF_SPECTACULAR", "0") == "1"
|
|
|
|
|
|
|
|
|
|
if ENABLE_DRF_SPECTACULAR:
|
|
|
|
|
REST_FRAMEWORK["DEFAULT_SCHEMA_CLASS"] = "drf_spectacular.openapi.AutoSchema"
|
|
|
|
|
INSTALLED_APPS.append("drf_spectacular")
|
|
|
|
|
from .openapi import SPECTACULAR_SETTINGS # noqa: F401
|