Compare commits

..
Author SHA1 Message Date
sriram veeraghanta 48c34e068b chore: code refactor 2025-09-03 23:41:10 +05:30
sriram veeraghanta fc6701fc4a fix: merge conflicts 2025-09-03 23:13:01 +05:30
Bavisetti NarayanandGitHub 9965fc75a6 [WEB-4806] chore: improved the html validation (#7676)
* chore: improved the html validation

* chore: removed the models changes

* chore: removed extra filters

* chore: changed the protocols
2025-09-03 19:56:20 +05:30
Dheeraj Kumar KetireddyandGitHub cb1e93f83d [WEB-4831] schema: add rich_filters field to multiple models for enhanced filtering capabilities (#7700)
* chore: add rich_filters field to multiple models for enhanced filtering capabilities

* Set the default  value to empty dict
2025-09-03 18:37:56 +05:30
SangeethaandGitHub 2e673026d4 [WEB-4129] fix: work item filter assignees missing fields #7698 2025-09-03 18:36:04 +05:30
NikhilandGitHub 49a40031a4 [WEB-4795] chore: remove caching decorator from WorkspaceStatesEndpoint #7714 2025-09-03 18:28:11 +05:30
54e4a0f6dd fix: build error due to exports in propel package (#7715)
* fix: build error due to exports in propel package

* fix: editor css exports

* fix: editor css build

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2025-09-03 18:11:31 +05:30
sriram veeraghantaandGitHub 473535fcfa [WEB-4844] fix: export prople package dist (#7712)
* fix: export prople package dist

* fix: code refactor
2025-09-03 17:25:31 +05:30
Akshita GoyalandGitHub 59ace4f411 [WEB-4787] fix: changed issue to work item in cycles dropdown (#7706) 2025-09-03 14:11:30 +05:30
b99ddc24e7 [WEB-4810] feat: migrate to tsdown from tsup (#7679)
* feat: migrat to tsdown to tsup

* fix: build scripts

* fix: lock file fixes

* fix: adding build process to i18n and propel packages

* fix: lint warning

* chore: update services module entry points

* fix: lock file

* fix: lock file

* fix: remove tsc from build

* fix: tsdown configs

* fix: remove tsc step from build process

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
Co-authored-by: Aaron Reisman <aaron.reisman@plane.so>
2025-09-03 14:01:57 +05:30
Anmol Singh BhatiaandGitHub 91f0228b5f [WEB-4838] fix: github logo theme #7709 2025-09-02 22:39:42 +05:30
sriram veeraghantaandGitHub 4b74751e4e [WEB-4837] feat: adding more packages to catalog (#7693)
* feat: adding more packages to catalog

* fix: lock file

* fix: sharp pacakge catalog
2025-09-02 21:03:03 +05:30
Anmol Singh BhatiaandGitHub 1c28bd5887 [WEB-4724] fix: editor tooltip (#7708)
* fix: editor tooltip code refactoring

* chore: code refactor
2025-09-02 20:40:14 +05:30
Aaryan KhandelwalandGitHub 292264ba96 [WIKI-574] fix: disable copy comment link option for intake work item #7707 2025-09-02 20:39:09 +05:30
Prateek ShouryaandGitHub 28d368d502 [WEB-4836] fix: work item retrieval in peek overview (#7705) 2025-09-02 19:26:35 +05:30
Anmol Singh BhatiaandGitHub 652a6cc885 [WEB-4734] feat: replace emoji picker with frimousse (#7639) 2025-09-02 19:00:15 +05:30
26b48bfcf0 [WEB-4724] feat: migrate tooltips from blueprintjs to base-ui (#7646)
* feat: add card component to propel package and update tooltip imports

* refactor: remove @plane/ui dependency and update tooltip imports to use local card component

* fix: lint

* refactor: update import from @plane/ui to @plane/utils in command component

* chore: removed blueprintjs/core and blueprintjs/popover2 dependencies

* chore: updated tooltip instances across platform and performed related code refactoring

* chore: code refactor

* chore: code refactor

* fix: lint and build error

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* fix: format issue

* fix: build fix

---------

Co-authored-by: Jayash Tripathy <76092296+JayashTripathy@users.noreply.github.com>
2025-09-02 18:19:56 +05:30
Vamsi KrishnaandGitHub 569b2fba83 [WEB-4196]fix: toast message for sub work item quick actions (#7688)
* fix: toast message for sub work item quick actions

* chore: updated translation files
2025-09-02 18:02:30 +05:30
Anmol Singh BhatiaandGitHub 779a96acb8 [WEB-4834] fix: range date picker weekStartsOn (#7699)
* fix: range date picker weekStartsOn

* chore: code refactor
2025-09-02 18:02:09 +05:30
guru_sainathandGitHub 2d31b562d8 [WEB-4823] chore: Add compound indexing for notification fields to improve query performance (#7691)
* dev: handled indexing for the notification fields

* dev: removing indexing related to workspaces

* dev: handled indexing for file asset, user favorite, and page log

* dev: indexing concurrently
2025-09-02 14:18:13 +05:30
50b4aca726 [WEB-4829] refactor: star us on GitHub link component (#7697)
* [WEB-4829] refactor: star us on github link

* fix: review comment

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-09-02 13:54:13 +05:30
Prateek ShouryaandGitHub d960d7ce88 [WEB-4828] refactor: remove legacy project-level draft work items components (#7694) 2025-09-02 12:29:00 +05:30
M. PalanikannanandGitHub fd5ba6c7b8 [WIKI-511] [WIKI-572] fix: extended page root and editor body (#7661)
* fix: better refactoring of page root and editor body

* fix: editor sideeffects

* fix: add comments json field

* fix: props name

* fix: review changes

* fix: extra checks

* fix: type changes

* fix: remove editor ref current

* fix: renaming

* fix: link check
2025-09-01 22:17:53 +05:30
f42eeec2c0 [WEB-4686] feat: propel tabs (#7620)
* chore: global css file added to tailwind config package

* chore: tailwind config updated

* chore: cn utility function added to propel package

* chore: storybook init

* fix: format error

* feat: added base ui tabs

* fix: add missing newline at end of package.json in propel package

* fix: reorder import statement for Tabs component in propel package

* feat: refactor Tabs component to support compound structure with forward refs

* fix: lint

* chore: code refactor

* chore: code refactor

* fix: lock file

* chore: added stories for tabs

* refactor: clean up

* fix: lint

* fix: lint

* fix: Remove duplicate storybook ESLint config

* fix: lint

* fix: update classname import path in Tabs component

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-09-01 19:58:40 +05:30
Vamsi KrishnaandGitHub e679dc3d12 [WEB-4814] chore: enabled reordering in list when group is none #7684 2025-09-01 19:46:17 +05:30
Jayash TripathyandGitHub f2edf637de [WEB-4809] chore: added common libs to pnpm workspace catalog (#7611)
* chore: added common libs to pnpm workspace catalog

* chore: update pnpm lockfile to use exact versions for React and TypeScript dependencies

* refactor: removed string from the versions

* fix: lint

* refactor: cleanup

* fix: lint

* chore: updated lock file
2025-09-01 19:42:56 +05:30
Vamsi KrishnaandGitHub 7437deaa86 [WEB-4689]chore: added accordion to propel (#7641)
* chore: added accordion to propel

* fix: lint errors

* fix: updated export path

* fix: lint errors

* chore: made accordion into compound component

* fix: coderabbit suggestions
2025-09-01 19:42:12 +05:30
64b95daff4 [WEB-4740] feat: add propel seperator (#7637)
* chore: global css file added to tailwind config package

* chore: tailwind config updated

* chore: cn utility function added to propel package

* chore: storybook init

* fix: format error

* chore: code refactor

* chore: code refactor

* fix: format error

* feat: add propel seperator component

* 🔒 chore: updated lock file

* ✏️ fix: typo in separator filename and some linting issues

* ♻️ refactor: replace clsx with cn utility in Separator component for class name management

* 🐛 fix: re-added twMerge

* 🧹 cleanup: remove unnecessary blank line in Separator component

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2025-09-01 19:40:01 +05:30
sriramveeraghanta 34181fba80 chore: version bump 2025-09-01 19:38:21 +05:30
Vipin ChaudharyandGitHub 0358e9b965 [WIKI-589] fix: update project page creation flow (#7685)
* fix : update project page creation flow

* chore: update translations

* chore: remove unused changes
2025-09-01 16:43:07 +05:30
Vipin ChaudharyandGitHub ee471c772a [WIKI-622] fix: update space work item description styles #7686 2025-09-01 15:13:52 +05:30
e2c0d0f23c [WEB-4813] fix: ensure all identifiers in log transformations are converted to strings (#7682)
* fix: ensure all identifiers in log transformations are converted to strings

* Update apps/api/plane/bgtasks/cleanup_task.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-01 14:12:34 +05:30
Anmol Singh BhatiaandGitHub 3ecebc02ae [WEB-4811] chore: plane logo asset updated #7683 2025-09-01 13:52:05 +05:30
Anmol Singh BhatiaandGitHub 16d531cc7a [WEB-4808] fix: joinUrlPath utility fn #7678 2025-09-01 13:51:26 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ab283c7c78 chore(deps): bump the npm_and_yarn group across 4 directories with 1 update (#7681)
Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js).
Bumps the npm_and_yarn group with 1 update in the /apps/admin directory: [next](https://github.com/vercel/next.js).
Bumps the npm_and_yarn group with 1 update in the /apps/space directory: [next](https://github.com/vercel/next.js).
Bumps the npm_and_yarn group with 1 update in the /apps/web directory: [next](https://github.com/vercel/next.js).


Updates `next` from 14.2.30 to 14.2.32
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.30...v14.2.32)

Updates `next` from 14.2.30 to 14.2.32
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.30...v14.2.32)

Updates `next` from 14.2.30 to 14.2.32
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.30...v14.2.32)

Updates `next` from 14.2.30 to 14.2.32
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.30...v14.2.32)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 14.2.32
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.32
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.32
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.32
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-01 13:05:18 +05:30
Anmol Singh BhatiaandGitHub 7c1bbf4a6f [WEB-4807] fix: workspace menu item highlight #7677 2025-08-29 20:23:31 +05:30
sriram veeraghantaandGitHub 4293892178 [WEB-4803] fix: eslint errors in code editors (#7675)
* fix: eslint errors in code editors

* fix: removed workspaces from package.json
2025-08-29 19:44:46 +05:30
Bavisetti NarayanandGitHub ebd517bb7d [WIKI-619] chore: added sort order migration for page model #7673 2025-08-29 19:44:29 +05:30
Henit ChobisaandGitHub 4042af9f32 feat: added support for expanding updated_by in work item (#7667)
* feat: added support for expanding `updated_by` and `type` in work item

* fix: moved type to dictionary for expansion

* fix: refactored unnecessary fields
2025-08-29 16:41:54 +05:30
258d24bf06 [SILO-454] chore: refactor decorator, logger packages (#7618)
* [SILO-454] chore: refactor decorator, logger packages

- add registerControllers function abstracting both rest, ws controllers
- update logger to a simple json based logger

* fix: logger instance and middleware

* fix: type and module resolutions

* fix: lodash type package update

* fix: bypass lint errors in decorators

* chore: format changes

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-08-29 14:29:16 +05:30
NikhilandGitHub 489a6e1e94 [WEB-4796] fix: update MongoDB collection check to use 'is not None' for better clarity #7671 2025-08-29 14:25:46 +05:30
sriram veeraghantaandGitHub 4f349807be chore: docker image builds by removing node version in workspace.yml file (#7672)
* fix: docker image builts by removing nodeversion in workspace.yml file

* chore: remve .nvmrc file
2025-08-29 14:25:07 +05:30
Sriram Veeraghanta 88914fec7c fix: adding new logger and docorator changes 2025-08-29 01:23:36 +05:30
Sriram Veeraghanta 0692dd9ef5 chore: merge conflicts 2025-08-29 01:19:55 +05:30
Vipin ChaudharyandGitHub 7a43137620 [WIKI-556] fix : invert tracking logic #7668 2025-08-28 20:37:20 +05:30
e144ce8cf2 [WIKI-556] chore: disable tracking of page hover (#7650)
* chore: disable tracking of page hover

* chore: add track check for page feth

* chore: make track check mandatory

* chore: update track format

---------

Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
2025-08-28 20:02:44 +05:30
Anmol Singh BhatiaandGitHub ba7303b7af [WEB-4559] fix: workspace menu item active state (#7666)
* fix: highlight active workspace menu item correctly

* chore: code refactor
2025-08-28 20:01:28 +05:30
Prateek ShouryaandGitHub e0912ccefc [WEB-4040] fix: minor changes in base plan names (#7656) 2025-08-28 18:51:10 +05:30
Anmol Singh BhatiaandGitHub aef465415b [WEB-4748] chore: propel utils (#7662)
* chore: tailwind merge added to propel

* chore: classname utils updated

* chore: utils import alias added

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-08-28 18:39:27 +05:30
Vamsi KrishnaandGitHub 3b3bd3e54e [WEB-4722] fix: fixed draft state update #7663 2025-08-28 18:38:30 +05:30
SangeethaandGitHub f2fabff10a [WEB-4521] fix: attachments #7665 2025-08-28 18:36:50 +05:30
NikhilandGitHub ddeabeeeb1 [WEB-4720] fix: mongo connection class to initialize mongo db #7652 2025-08-28 13:57:16 +05:30
sriram veeraghantaandGitHub 0e6fbaee3a [WEB-4790] fix: moved typescript parser to the base eslint config (#7658)
* fix: moved typescript parser to the base eslint config

* fix: eslint warning

* fix: type config setting

* fix: convert live eslint to cjs
2025-08-27 21:03:20 +05:30
Anmol Singh BhatiaandGitHub cfe710d492 [WEB-4779] chore: app sidebar wrapper component for consistency and reusability (#7655)
* chore: app sidebar ui enhancements and code refactor

* chore: code refactor

* chore: code refactor
2025-08-27 20:46:43 +05:30
AaronandGitHub 4a3c172992 feat(repo): add diff checks in CI flows (#7657) 2025-08-27 16:31:23 +05:30
Vipin ChaudharyandGitHub 5d1ad8a183 [WIKI-550] fix: emoji modal for touch device (#7651)
* fix: emoji modal for touch device

* refactor: editor from props

* fix : update is touch device plugin
2025-08-27 00:39:47 +05:30
Vipin ChaudharyandGitHub f95a07d8c8 [WIKI-436] fix: check for code in alignment #7649 2025-08-27 00:39:26 +05:30
Bavisetti NarayanandGitHub 0af75897f5 [WEB-4780] chore: changed the html validation (#7648)
* chore: changed the html validation

* chore: added requirements for nh3

* chore: removed the json validations
2025-08-27 00:38:25 +05:30
sriramveeraghanta f56d39d7bb fix: lodash type package update 2025-08-26 19:52:49 +05:30
sriramveeraghanta b77da77d58 fix: type and module resolutions 2025-08-26 19:47:49 +05:30
sriramveeraghanta e2fa03d66c fix: logger instance and middleware 2025-08-26 18:56:58 +05:30
sriramveeraghanta 89a94fe056 Merge branch 'preview' of github.com:makeplane/plane into chore/refactor-decorators-pkg 2025-08-26 18:48:55 +05:30
Vamsi KrishnaandGitHub 3602ff6930 [WEB-4781] fix: add peek view get to store #7654 2025-08-26 18:24:08 +05:30
Prateek ShouryaandGitHub 6d1275d58c [WEB-4761] fix: resolve circular import between IssueService and local db (#7653)
- Move local db import to dynamic import to break circular dependency
2025-08-26 16:48:32 +05:30
Vipin ChaudharyandGitHub cf7b288f93 [WIKI-345] regression: update block menu options (#7647)
* chore: update flagged and disabled extension

* chore :build fix
2025-08-26 14:18:26 +05:30
Vipin ChaudharyandGitHub 0fe7da6265 [WIKI-345] chore: pass disabled and flagged extensions to block menu (#7152)
* chore: refactor editor

* sync changes

* feat: api service update

* refactor : update sync

* fix : package sync

* fix: requested changes

* fix : embedhandler type

* fix : remove commands

* refactor : space

* refactor : rich lite editors

* refactor : minor ce changes

* chore : minor ui fix

* package: tldjs

* refactor : remove tldjs

* refactor: flagged

* refactor: flagged

* chore : remove disbaled check in menu

* refactor: fix space

* refactor: NodeViewProps

* refactor: type

* refactor : update community types

* refactor : remove external embed CE

* remove : external embed config from ce

* refactor : update disabled

* chore: pass disabled

* chore : update utils
2025-08-26 02:23:50 +05:30
Jayash TripathyandGitHub 8801ab0081 [WEB-4727] feat: propel cards (#7630)
* feat: add card component to propel package and update tooltip imports

* refactor: remove @plane/ui dependency and update tooltip imports to use local card component

* fix: lint

* refactor: update import from @plane/ui to @plane/utils in command component

* refactor: extend CardProps interface to include HTML attributes for better flexibility
2025-08-26 02:14:24 +05:30
Anmol Singh BhatiaandGitHub c2464939fc [WEB-4725] chore: storybook setup & tailwind config package improvements (#7614)
* chore: global css file added to tailwind config package

* chore: tailwind config updated

* chore: cn utility function added to propel package

* chore: storybook init

* fix: format error

* chore: code refactor

* chore: code refactor

* fix: format error

* fix: build error
2025-08-26 02:14:00 +05:30
Aaryan KhandelwalandGitHub 34e231230f [WIKI-498] regression: table bugs #7631 2025-08-26 02:13:27 +05:30
Lakhan BahetiandGitHub eb5ac2fc2d [WIKI-602] chore: disable image alignment tooltip for touch devices (#7642)
* chore: disable image alignment tooltip for touch devices

* chore: added touch-select-none style
2025-08-26 02:12:04 +05:30
Jayash TripathyandGitHub b6cf3a5a8b [WEB-4438] fix: epics label in y axis of epics(analytics-modal) #7644 2025-08-25 19:25:42 +05:30
Anmol Singh BhatiaandGitHub bbc465a3b2 [WEB-4761] fix: old url redirection method #7638 2025-08-25 15:28:58 +05:30
sriramveeraghanta 78526ff852 fix: lock file 2025-08-25 15:00:46 +05:30
sriramveeraghanta a56606e070 chore: restructured without ce folder 2025-08-25 14:58:30 +05:30
Jayash TripathyandGitHub 9a77e383cd [WEB-4751] chore: enhance URL utility functions with IP address validation and cleaned up url extraction (#7636)
* feat: enhance URL utility functions with IP address validation and cleaned up the extraction utilities

* fix: remove unnecessary type assertion in isLocalhost function
2025-08-25 13:38:09 +05:30
sriram veeraghanta a2d9e70a83 fix: requirments.txt 2025-08-25 02:40:06 +05:30
c7763dd431 refactor: remove few barrel exports (#7633)
* refactor: remove few barrel exports

* fix: pnpm lock file update

* fix: build errors

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-25 02:13:20 +05:30
Prateek ShouryaandGitHub 599ff2eec4 [WEB-4746] fix: position peek view relative to app layout (#7635) 2025-08-25 01:32:15 +05:30
Prateek ShouryaandGitHub 568a1bb228 [WEB-4757] fix: remove project view from workspace level group by options (#7634) 2025-08-24 15:16:08 +05:30
NikhilandGitHub 935e4b5c33 [WEB-4720] chore: refactor and extend cleanup tasks for logs and versions (#7604)
* Refactor and extend cleanup tasks for logs and versions

- Consolidate API log deletion into cleanup_task.py - Add tasks to
delete old email logs, page versions, and issue description versions -
Update Celery schedule and imports for new tasks

* chore: update cleanup task with mongo changes

* fix: update log deletion task name for clarity

* fix: enhance MongoDB archival error handling in cleanup task

- Added a parameter to check MongoDB availability in the flush_to_mongo_and_delete function.
- Implemented error logging for MongoDB archival failures.
- Updated calls to flush_to_mongo_and_delete to include the new parameter.

* fix: correct parameter name in cleanup task function call

- Updated the parameter name from 'mode' to 'model' in the process_cleanup_task function to ensure consistency and clarity in the code.

* fix: improve MongoDB connection parameter handling in MongoConnection class

- Replaced direct access to settings with getattr for MONGO_DB_URL and MONGO_DB_DATABASE to enhance robustness.
- Added warning logging for missing MongoDB connection parameters.
2025-08-24 15:13:49 +05:30
841388e437 [WEB-4751] refactor: added tld validation for urls (#7622)
* refactor: added tld validation for urls

* refactor: improve TLD validation and update parameter naming in URL utility functions

* refactor: enhance URL component extraction and validation logic

* fix: lint

* chore: remove unused lodash filter import in existing issues list modal

---------

Co-authored-by: Sriram Veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-23 01:07:35 +05:30
9ecea15d74 [WIKI-498] [WIKI-567] feat: ability to rearrange columns and rows in table (#7624)
* feat: ability to rearrange columns and rows

* chore: update delete icon

* refactor: table utilities and plugins

* chore: handle edge cases

* chore: safe pseudo element inserts

---------

Co-authored-by: Sriram Veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-23 00:54:03 +05:30
4ad88c969c [WEB-4747]chore: rendering cycle progress from snapshot (#7626)
* chore: rendering progress from snaposhot

* chore: removed unncessary memoization

---------

Co-authored-by: Sriram Veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-23 00:46:06 +05:30
706085395e [WEB-4748] chore: placement helper fn added and code refactor #7627
Co-authored-by: Sriram Veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-23 00:42:44 +05:30
Sriram Veeraghanta a0f7acae42 chore: format files 2025-08-23 00:33:38 +05:30
SangeethaandGitHub 6e5549c439 [WEB-4187] fix: related search issues #7628 2025-08-23 00:28:08 +05:30
Jayash TripathyandGitHub cf8eeee03a [WEB-4687] feat: propel switch (#7629)
* feat: added switch

* fix: lint

* fix: lock file

* fix: improve accessibility and refactor switch component styles

* feat: add switch component to propel package

* fix: update imports in command component for consistency

* refactor: styles
2025-08-23 00:27:31 +05:30
sriramveeraghanta e3e5a1f1e7 fix: controllers setup 2025-08-22 21:59:54 +05:30
sriramveeraghanta 7b10918084 fix: async method handled properly 2025-08-22 19:00:19 +05:30
sriramveeraghanta 269d045185 fix: merge conflicts 2025-08-22 18:50:27 +05:30
sriram veeraghantaandGitHub d3b26996dd fix: replace .npmrc node version with engines in package.json (#7623) 2025-08-22 14:13:08 +05:30
d0f26f8734 [WEB-4726] fix: intake work item redirection (#7619)
* chore: added is intake for email notifications

* fix: intake work item redirection

* chore: code refactor

* chore: code refactor

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2025-08-22 12:54:46 +05:30
e86b40ac82 [WEB-4682 | WEB-4685] feat: propel comobobox and command component (#7615)
* feat: comobobox and command component added to propel package

* fix: format error

* chore: code refactor

* chore: code refactor

* fix: format error

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-21 15:32:29 +05:30
Surya Prashanth 2f281323a3 [SILO-454] chore: refactor decorator, logger packages
- add registerControllers function abstracting both rest, ws controllers
- update logger to a simple json based logger
2025-08-21 14:58:47 +05:30
Saurabh KumarandGitHub c209a713d8 [SILO-449] fix: add missing methods in external APIs (#7601)
* add missing fields and methods in endpoints

* add POST method for project members

* make project_id as uuid in url pattern

* remove post method

* fix method reordering
2025-08-21 13:15:15 +05:30
Anmol Singh BhatiaandGitHub f10cd92610 [WEB-4724] feat: tooltip component added to propel package (#7613)
* feat: tooltip component added to propel package

* chore: code refactor

* chore: code refactor

* fix: format error
2025-08-21 13:03:18 +05:30
Anmol Singh BhatiaandGitHub 03479cf6b3 [WEB-4684] chore: dialog component enhancements (#7606)
* chore: z-index tokens added

* chore: dialog component code refactor

* chore: dialog component improvements

* fix: lint error

* fix: lint error

* fix: format error
2025-08-20 22:17:26 +05:30
Bavisetti NarayanandGitHub b8a88fe89c [WIKI-599] chore: removed the regex tags calculation in description (#7608) 2025-08-20 21:26:21 +05:30
Anmol Singh BhatiaandGitHub 409ac30c91 [WEB-4683] feat: popover component added to propel package #7607 2025-08-20 20:50:27 +05:30
Bavisetti NarayanandGitHub a59ebadd34 [WEB-4712] chore: work item attachment patch endpoint (#7595) 2025-08-20 18:56:15 +05:30
Akshita GoyalandGitHub 174ebfad56 [WEB-4637] fix: scrolling issue fixed #7600 2025-08-20 18:55:22 +05:30
SangeethaandGitHub 008e048968 [WEB-4430] fix: incorrect WI count while scrolling (#7596)
* fix: wrong WI count while scrolling

* chore: optimize issue queryset

* fix: use separate query for total_count_queryset

* fix: guest visibility constraint

* fix: use separate query for total_count_queryset in external api

* fix: use queryset.count()
2025-08-20 18:54:32 +05:30
sriram veeraghantaandGitHub 7e15fcc080 fix: docker node_modeles symlink path matching with pnpm path (#7605) 2025-08-20 16:17:35 +05:30
sriram veeraghantaandGitHub 6636b8882f fix: package cleanup (#7602) 2025-08-20 02:24:12 +05:30
sriram veeraghanta e169a0187d fix: package dependecies 2025-08-20 01:44:54 +05:30
sriram veeraghanta e001d6c605 fix: merge conflicts 2025-08-20 01:27:38 +05:30
Bavisetti NarayanandGitHub 6398fc3cba [WEB-4716] chore: created new description model (#7597)
* chore: created new description model

* chore: added project field

* chore: removed the duplicate workspace

* chore: updated the comment
2025-08-20 01:07:23 +05:30
Vamsi KrishnaandGitHub fc698bd9b4 [WEB-4710]feat: added module filters to local storage (#7598)
* feat: added module filters to local storage

* chore: removed debounce
2025-08-20 01:04:17 +05:30
Jayash TripathyandGitHub cd61e8dd44 [WEB-4705] chore: url utilities (#7589)
* feat: add truncated link export and URL utility to respective modules

* refactor: replace Link2 with ExternalLink in TruncatedUrl component

* feat: add TruncatedUrl component and update link exports

* fix: export ParsedURL interface for better accessibility in URL utilities

* refactor: remove TruncatedUrl component and update link exports

* fix: update parseURL function to return undefined for invalid URLs

* refactor: rename ParsedURL interface to IParsedURL for consistency

* refactor: rename IParsedURL to IURLComponents and update parsing functions for improved clarity

* refactor: update URL utility functions and improve documentation for clarity

* refactor: add full URL property to IURLComponents interface and update extractURLComponents function

* refactor: rename createURL function to isUrlValid and update its implementation to validate URL strings

* refactor: rename isUrlValid function to getValidURL and update its implementation to return URL object or undefined
2025-08-19 20:09:03 +05:30
Vamsi KrishnaandGitHub cbcdd86569 [WEB-4698]fix: work items modal select/deselect button #7599 2025-08-19 20:07:14 +05:30
553f01fde1 feat: migrate to pnpm from yarn (#7593)
* chore(repo): migrate to pnpm

* chore(repo): cleanup pnpm integration with turbo

* chore(repo): run lint

* chore(repo): cleanup tsconfigs

* chore: align TypeScript to 5.8.3 across monorepo; update pnpm override and catalog; pnpm install to update lockfile

* chore(repo): revert logger.ts changes

* fix: type errors

* fix: build errors

* fix: pnpm home setup in dockerfiles

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-08-19 20:06:42 +05:30
d8f58d28ed fix: CI to include lint and format along with build (#7482)
* fix(lint): get ci passing again

* chore(ci): run lint before build

* chore(ci): exclude web app from build check for now

The web app takes too long and causes CI to timeout. Once we
improve we will reintroduce.

* fix: formating of files

* fix: adding format to ci

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-08-18 21:27:16 +05:30
b194089fec [WEB-3855] fix: Turkish language support (#7383)
* Türkçe dil desteği , İgilizce okunuşlardan gerçek karşılığına çevirildi.

* "sidebar.intake"="Talep" olarak değiştirildi.

---------

Co-authored-by: Bektaş IŞIK <bektas.isik@aurorabilisim.com>
2025-08-18 21:17:06 +05:30
sriram veeraghantaandGitHub 927da438c7 [PRIME-17] fix: enable github api to fetch latest version information (#7548)
* fix: enable github api to fetch latest version information

* chore: typo fixes

* chore: add timeout to request
2025-08-18 20:12:48 +05:30
sriram veeraghantaandGitHub 9c21fd320c feat: adding baseui components to propel package (#7585)
* feat: adding baseui components

* fix: export from the package.json
2025-08-18 19:35:34 +05:30
Anmol Singh BhatiaandGitHub f142266bed [WEB-4699] chore: loading spinner theme #7587 2025-08-18 18:30:51 +05:30
sriram veeraghantaandGitHub 4b06bc4d2d fix: remove page title hook (#7583)
* fix: page title hook removed

* fix: build errors
2025-08-16 14:10:10 +05:30
Aaryan KhandelwalandGitHub d692db47b2 refactor: space app barrel exports (#7573)
* refactor: space app barrel files

* chore: rename views layout
2025-08-15 13:12:36 +05:30
3391e8580c refactor: remove barrel exports from web app (#7577)
* refactor: remove barrel exports from some compoennt modules

* refactor: remove barrel exports from issue components

* refactor: remove barrel exports from page components

* chore: update type improts

* refactor: remove barrel exports from cycle components

* refactor: remove barrel exports from dropdown components

* refactor: remove barrel exports from ce  components

* refactor: remove barrel exports from some more components

* refactor: remove barrel exports from profile and sidebar components

* chore: update type imports

* refactor: remove barrel exports from store hooks

* chore: dynamically load sticky editor

* fix: lint

* chore: revert sticky dynamic import

* refactor: remove barrel exports from ce issue components

* refactor: remove barrel exports from ce issue components

* refactor: remove barrel exports from ce issue components

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-08-15 13:10:26 +05:30
Prateek ShouryaandGitHub 9cf564caae [WEB-4693] fix: remove initial load flicker on auto-archive and auto-close automation page (#7578)
* [WEB-4693] fix: remove initial load flicker on auto-archive and auto-close automation page

* refactor: optimize auto-archive and auto-close status calculations using useMemo

* chore: add requested changes
2025-08-13 19:17:20 +05:30
Prateek ShouryaandGitHub 34c6047d80 [WEB-4677] improvement: add defaultOpen property to CustomSearchSelect (#7576)
* [WEB-4677] improvement: add defaultOpen property to CustomSearchSelect

* improvement: add utils to format time duration

* improvement: added initializing state for project store

* improvement: minor changes in automations page
2025-08-12 19:37:53 +05:30
Anmol Singh BhatiaandGitHub 5629a4d4b6 [WEB-4674] fix: update broken email preference links in notification emails #7574 2025-08-12 15:55:26 +05:30
NikhilandGitHub 545507fa97 [WEB-4668] fix: LabelDetailAPIEndpoint from LabelListCreateAPIEndpoint (#7571) 2025-08-12 14:43:14 +05:30
Anmol Singh BhatiaandGitHub d317755ab9 [WEB-4542] feat: god mode auth revamp and code refactor (#7563)
* chore: auth color updated and remove unused tokens

* chore: god-mode brand revamp

* fix: space app spinner logo
2025-08-11 18:46:23 +05:30
sriram veeraghantaandGitHub 047080a66f [WEB-4661] fix: move helpers file into utils #7568 2025-08-11 18:42:51 +05:30
sriram veeraghantaandGitHub a085c0ec62 [WEB-4660] chore: replace jsx element with react node (#7567)
* chore: replace jsx element with react node

* fix: review comments

* fix: tooltip types update

* fix: propel pacakge fix
2025-08-11 18:42:23 +05:30
SangeethaandGitHub 1ef30746a2 [WEB-4657] refactor: optimize project v2 endpoint and issue detail endpoint #7558 2025-08-11 00:56:15 +05:30
Sriram Veeraghanta e3deeb2b43 fix: minor wrapper order in provider 2025-08-11 00:55:11 +05:30
Anmol Singh BhatiaandGitHub 736296090e [WEB-4654] refactor: replace nprogress with bprogress and clean up unused code (#7559)
* refactor: replace nprogress with bprogress and clean up unused code

* chore: code refactor

* chore: code refactor
2025-08-11 00:37:35 +05:30
Vamsi KrishnaandGitHub 9de5b1a009 [WEB-4634]chore: refactor for work items store (#7538)
* chore: refactor for work items store

* chore: updated conditions order
2025-08-06 22:44:33 +05:30
21c59692f9 [WEB-4635] fix: space auth screen re-loading issue (#7551)
* fix: prevent auth redirect on window focus

* fix: space auth screen re-loading issue.

---------

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-06 22:32:52 +05:30
Vamsi KrishnaandGitHub 806619d73f [WEB-4645]chore: added event trackers for labels #7552 2025-08-06 22:29:55 +05:30
c5e5454265 [WEB-4650] fix: email template logo updated #7553
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-08-06 22:27:59 +05:30
Akshita GoyalandGitHub 3972b6639f [WEB-4632] fix: sidebar item refactored #7539 2025-08-06 22:26:47 +05:30
51e146f8ca [WEB-4488] feat: brand revamp (#7544)
* chore: empty state asset and theme improvement (#7542)

* chore: empty state asset and theme improvement

* chore: upgrade modal improvement and code refactor

* feat: onboarding revamp and theme changes (#7541)

* refactor: consolidate password strength indicator into shared UI package

* chore: remove old password strength meter implementations

* chore: update package dependencies for password strength refactor

* chore: code refactor

* chore: brand logo added

* chore:  terms and conditions refactor

* chore: auth form refactor

* chore: oauth enhancements and refactor

* chore: plane new logos added

* chore: auth input form field added to ui package

* chore: password input component added

* chore: web auth refactor

* chore: update brand colors and remove onboarding-specific styles

* chore: clean up unused assets

* chore: profile menu text overflow

* chore: theme related changes

* chore: logo spinner updated

* chore: onboarding constant and types updated

* chore: theme changes and code refactor

* feat: onboarding flow revamp

* fix:  build error and code refactoring

* chore: code refactor

* fix: build error

* chore: consent option added to onboarding and code refactor

* fix: build fix

* chore: code refactor

* chore: auth screen revamp and code refactor

* chore: onboarding enhancements

* chore: code refactor

* chore: onboarding logic improvement

* chore: code refactor

* fix: onboarding pre release improvements

* chore: color token updated

* chore: color token updated

* chore: auth screen line height and size improvements

* chore: input height updated

* chore: n-progress theme updated

* chore: theme and logo enhancements

* chore: space auth and code refactor

* chore: update new brand empty states (#7543)

* [WEB-4585]chore: branding updates (#7540)

* chore: updated logo, og image, and loaders

* chore: updated branding colors

* chore: tour modal logo

* chore: updated logo spinner size

* chore: updated email templates logos and colors

* chore: code refactor

* fix: removed conditional hook render

* fix: space app loader

---------

Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
Co-authored-by: vamsikrishnamathala <matalav55@gmail.com>
2025-08-06 22:24:47 +05:30
DanciaandGitHub 6450793d72 docs: updated README images (#7547) 2025-08-05 17:24:33 +05:30
Akshita GoyalandGitHub dffe3a844f [WEB-4632] chore: pinned your work to the sidebar #7537 2025-08-04 20:06:03 +05:30
Aaryan KhandelwalandGitHub 7ead606798 [WIKI-578] refactor: editor types structure #7536 2025-08-04 18:01:51 +05:30
fa150c2b47 [WIKI-576] fix: trail node (#7527)
* fix : trail node

* remove flagged

* refactor : add disable flagging

* refactor:update disabled extension

* refactor: additional disabled

* refactor: update enum

* chore: add description key to page response type

* chore: update base page instance

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2025-08-04 16:12:46 +05:30
c3273b1a85 [WIKI-568] refactor: add touch device support to editor (#7439)
* refactor: add isTouchDevice prop

* chore: handle event propagation in touch devices

* refactor: isTouchDevice implementation

* chore: misc editor updates and utility functions (#7455)

* chore: misc editor updated and utility functions

* fix: code review

* passed isTouchDevice prop to editor-wrapper

* added more props to editor-wrapper.

* chore: update types

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>

* fix: remove unnecessary deps

---------

Co-authored-by: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com>
2025-08-04 16:04:09 +05:30
Bavisetti NarayanandGitHub 7cec92113f [WEB-4628] chore: return 200 response for work item comment #7532 2025-08-04 15:27:05 +05:30
Vipin ChaudharyandGitHub 8cc513ba5e [WIKI-577] fix: unexpected emoji input (#7534)
* fix: unexpected emoji input

* update array
2025-08-04 15:21:10 +05:30
P BandGitHub 784b8da9be docs: update README.md (#7531)
changed tagline
2025-08-01 18:19:02 +05:30
1d443310ce feat: update new logo on readme (#7530)
* feat: update new logo on readme

Changed logo on Readme

* fix: image url

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-08-01 17:32:34 +05:30
Akshat JainandGitHub cc49a2ca4f [INFRA-219] fix: update Dockerfile and docker-compose for proxy service (#7523)
* fix: update Dockerfile and docker-compose for version v0.28.0 and improve curl commands in install script

* fix: update docker-compose to use 'stable' tag for all services

* fix: improve curl command options in install script for better reliability
2025-07-31 13:27:34 +05:30
sriram veeraghantaGitHubCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
ee53ee33d0 Potential fix for code scanning alert no. 631: Incomplete URL scheme check (#7514)
* Potential fix for code scanning alert no. 631: Incomplete URL scheme check

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix: ignore warning in this file

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-07-31 13:23:59 +05:30
sriram veeraghantaandGitHub 99f9337f35 fix: enable email notification by default for new users (#7521) 2025-07-31 13:02:41 +05:30
sriram veeraghantaandGitHub 1458c758a3 fix: adding proxy command in compose file #7518
fix: adding proxy command in compose file
2025-07-30 21:01:34 +05:30
sriramveeraghanta 2d9988f584 fix: adding proxy command 2025-07-30 21:00:16 +05:30
SangeethaandGitHub 27fa439c8d [WEB-4602] fix: 500 error on draft wi labels update #7515 2025-07-30 20:18:48 +05:30
sriramveeraghanta 57935a94cc fix: header observer for state changes 2025-07-30 17:24:40 +05:30
Vamsi KrishnaandGitHub 876ccce86b [WEB-4597] fix: project icon render in switcher #7504 2025-07-30 17:18:18 +05:30
NikhilandGitHub a67dba45f8 [WEB-4599] feat: add marketing email consent field to Profile model and timezone migration #7510 2025-07-30 17:17:16 +05:30
Prateek ShouryaandGitHub ef8e613358 [WEB-4603] feat: add missing event trackers (#7513)
* feat: add event trackers for password creation

* feat: add event trackers for project views

* feat: add event trackers for product updates and changelogs

* chore: use element name instead of event name for changelog redirect link
2025-07-30 17:15:59 +05:30
sriramveeraghanta cdb488f864 fix: error handling 2025-07-30 17:10:08 +05:30
sriramveeraghanta c4d2c5b1bb sync: canary changes to preview 2025-07-30 15:44:41 +05:30
Bavisetti Narayanandsriramveeraghanta d4705e1649 [WEB-4544] chore: added field validations in serializer (#7460)
* chore: added field validations in serializer

* chore: added enum for roles
2025-07-30 15:41:44 +05:30
sriramveeraghanta 6a54a07cee chore: cleanup 2025-07-30 15:14:39 +05:30
RishiandGitHub 8228ecc087 fix(cli): improve API service readiness check in install script (#7468)
* fix: improve API service readiness check in install script

* fix(cli): correct python indentation in api health check

* fix(cli): prevent false positive api ready message on timeout
2025-07-30 15:08:30 +05:30
Aaryan KhandelwalandGitHub ce08c9193c [WIKI-573] regression: comment card padding #7512 2025-07-30 15:02:55 +05:30
sriramveeraghanta 78169fec66 fix: redis and hocuspocus as singletons 2025-07-30 14:56:21 +05:30
69d5cd183f chore: added validation for description (#7507)
* added PageBinaryUpdateSerializer for binary data validation and update

* chore: added validation for description

* chore: removed the duplicated file

* Fixed coderabbit comments

- Improve content validation by consolidating patterns and enhancing recursion checks

- Updated `PageBinaryUpdateSerializer` to simplify assignment of validated data.
- Enhanced `content_validator.py` with consolidated dangerous patterns and added recursion depth checks to prevent stack overflow during validation.
- Improved readability and maintainability of validation functions by using constants for patterns.

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
2025-07-30 14:19:49 +05:30
Aaryan KhandelwalandGitHub 6e1734c86e [WIKI-570] fix: image alignment for read only editor #7509 2025-07-30 14:12:33 +05:30
sriramveeraghanta b2ed07a778 Merge branch 'preview' of github.com:makeplane/plane into fix-live-sync-changes 2025-07-30 13:32:28 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
05f56d04ba chore(deps): bump linkifyjs in the npm_and_yarn group across 1 directory (#7506)
Bumps the npm_and_yarn group with 1 update in the / directory: [linkifyjs](https://github.com/nfrasser/linkifyjs/tree/HEAD/packages/linkifyjs).


Updates `linkifyjs` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/nfrasser/linkifyjs/releases)
- [Changelog](https://github.com/nfrasser/linkifyjs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nfrasser/linkifyjs/commits/v4.3.2/packages/linkifyjs)

---
updated-dependencies:
- dependency-name: linkifyjs
  dependency-version: 4.3.2
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 13:12:00 +05:30
Aaryan KhandelwalandGitHub 3f60f14bb1 [WIKI-539] regression: toolbar visible in comments card #7508 2025-07-30 13:11:56 +05:30
Aaryan KhandelwalandGitHub e0b0aafc2b [WIKI-539] regression: editor build errors #7503 2025-07-29 19:10:39 +05:30
Aaryan KhandelwalandGitHub e0fa6553ae [WIKI-539] refactor: remove lite text read only editor (#7481)
* refactor: remove lite text read only editor

* chore: update types
2025-07-29 18:09:39 +05:30
Vamsi KrishnaandGitHub 55f06cf546 [WEB-4563]fix: image full screen modal #7491 2025-07-28 19:55:13 +05:30
84879ee3bd [WEB-4533] feat: read replica functionality (#7453)
* feat: read replica functionality

* fix: set use_read_replica to false

* chore: add use_read_replica to external APIs

* chore: remove use_read_replica on read endpoints

* chore: remove md files

* Updated all the necessary endpoints to use read replica

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
2025-07-28 17:41:02 +05:30
Sriram Veeraghanta b1162395ed sync: canary into preview 2025-07-28 12:53:16 +05:30
Sriram Veeraghanta b93883fc14 chore: version upgrade 2025-07-28 12:51:19 +05:30
NikhilandGitHub 4913c116d1 chore: correct migration file 0098 (#7486)
* feat: restore background color and goals fields in Profile and Workspace models in migration 0099

* chore: update migration dependency to reflect recent changes in profile model migrations
2025-07-28 12:32:47 +05:30
NikhilandGitHub 4044ce25ce [WEB-4566] feat: add background color and goals field to Profile and Workspace models #7485 2025-07-27 21:10:31 +05:30
sriram veeraghanta 1f7ef1865c Merge branch 'preview' of github.com:makeplane/plane into fix-live-sync-changes 2025-07-26 15:29:53 +05:30
a5f3bd15b1 [WEB-4513] refactor: consolidate password strength meter into shared ui package (#7462)
* refactor: consolidate password strength indicator into shared UI package

* chore: remove old password strength meter implementations

* chore: update package dependencies for password strength refactor

* chore: code refactor

* fix: lock file

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-07-25 16:56:46 +05:30
Bavisetti NarayanandGitHub 63d025cbf4 [WEB-4544] chore: added field validations in serializer (#7460)
* chore: added field validations in serializer

* chore: added enum for roles
2025-07-25 16:50:35 +05:30
Aaryan KhandelwalandGitHub 27f74206a3 [WIKI-537] refactor: document editor (#7384)
* refactor: document editor

* chore: update user prop

* fix: type warning

* chore: update value prop name

* chore: remove unnecessary exports

* hore: update initialValue type

* chore: revert initialValue type

* refactor: unnecessary string handlers
2025-07-25 13:57:45 +05:30
Aaron HeckmannandGitHub e20bfa55d6 chore: add ci job names (#7478)
* chore: add ci job names

This makes them easier to find in the Github UI.

* chore: increase ci timeout
2025-07-25 13:41:03 +05:30
Aaryan KhandelwalandGitHub f8353d3468 fix: eslint warnings and errors (#7479) 2025-07-25 13:40:25 +05:30
57479f4554 fix: lint (#7433)
* chore: fix lint

* fix: constants check:lint command

* chore(lint): permit unused vars which begin w/ _

* chore: rm dead code

* fix(lint): more lint fixes to constants pkg

* fix(lint): lint the live server

- fix lint issues

* chore: improve clean script

* fix(lint): more lint

* chore: set live server process title

* chore(deps): update to turbo@2.5.5

* chore(live): target node22

* fix(dev): add missing ui pkg dependency

* fix(dev): lint decorators

* fix(dev): lint space app

* fix(dev): address lint issues in types pkg

* fix(dev): lint editor pkg

* chore(dev): moar lint

* fix(dev): live server exit code

* chore: address PR feedback

* fix(lint): better TPageExtended type

* chore: refactor

* chore: revert most live server changes

* fix: few more lint issues

* chore: enable ci checks

Ensure we can build + confirm that lint is not getting worse.

* chore: address PR feedback

* fix: web lint warning added to package.json

* fix: ci:lint command

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-07-25 01:44:51 +05:30
514686d9d5 [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-25 00:17:05 +05:30
Vamsi KrishnaandGitHub 98a00f5bde [WEB-4523]fix: sub issues loader #7474 2025-07-24 19:25:37 +05:30
sriram veeraghantaandGitHub ed4ee3ad7e fix: remove commands from the docker compose file (#7476) 2025-07-24 18:28:12 +05:30
sriramveeraghanta 18b206952b chore: moving file strucutre 2025-07-24 16:07:03 +05:30
Prateek ShouryaandGitHub 18e4c60b42 [WEB-4536] fix: remove inactive workspace members from lists and dropdowns (#7473) 2025-07-24 15:29:18 +05:30
sriram veeraghantaandGitHub 849b7b7bf3 [WIKI-565] chore: handle 404 messages in live server (#7472)
* chore: handle 404 messages in live server

* chore: removed error stack from response
2025-07-24 15:13:44 +05:30
2746bad86b [WEB-4561] fix: SVG attributes in react components (#7470)
* chore: svg attribute fix (#7446)

* fix: replace invalid --env-file with dotenv-cli for tsup onSuccess

* fix: replace invalid --env-file usage in start script and move dotenv-cli to dependencies

* chor: update SVG attributes to camelCase for JSX compatibility

* chor: update SVG attributes to camelCase

* fix: removed dontenv-cli

* fix: svg attibute fixes

* chore: minor attribute customizations

---------

Co-authored-by: Donal Noye <68372408+Donal-Noye@users.noreply.github.com>
2025-07-24 14:46:55 +05:30
Vamsi KrishnaandGitHub 008727d393 [WEB-4547]fix: archived work item redirection #7471 2025-07-24 14:43:31 +05:30
merrime-nandGitHub 56e4d2c6f1 docs: add file paths to the new language guide (#7454)
* improve the comprehensiblity of translation contribution docs

* Update CONTRIBUTING.md to fix the code blocks appeareance
2025-07-24 14:12:49 +05:30
Vipin ChaudharyandGitHub d7e58a60fa [WIKI-563] refactor: subscription styles (#7444)
* refactor: billing UI

* refactor : fix typo
2025-07-24 14:11:23 +05:30
SangeethaandGitHub af81064961 [WEB-4558] fix: project test cases #7464 2025-07-24 14:07:37 +05:30
Akshita GoyalandGitHub 3c6e2b4447 [WEB-4552] fix: stickies layout + minified layout filter (#7466) 2025-07-24 14:06:46 +05:30
sriram veeraghantaandGitHub b7be45d08a chore: upgrade axios version to 1.11.0 (#7469) 2025-07-24 13:50:37 +05:30
Akshita GoyalandGitHub d2629d723c [WEB-4552] chore: minor design changes for home #7459 2025-07-23 14:30:25 +05:30
Akshita GoyalandGitHub 9007ec3709 [WEB-4546] chore: home update #7456 2025-07-23 10:11:18 +05:30
Akshita GoyalandGitHub 763a28ab60 [WEB-4546] chore: header enhancements + quickstart guide ui update + breadcrumb #7451 2025-07-22 16:47:14 +05:30
Aaryan KhandelwalandGitHub 5c22a6cecc [WIKI-484] chore: update editor packages (#7265) 2025-07-21 19:25:49 +05:30
Vamsi KrishnaandGitHub 4c3af7f8a1 [WEB-4531]chore: refactor for timeline chart (#7440) 2025-07-21 19:22:58 +05:30
Aaryan KhandelwalandGitHub cc673a17a0 [WIKI-357] fix: created by filter for pages list #7445 2025-07-21 19:22:27 +05:30
Prateek ShouryaandGitHub 2377474823 fix: build errors in work item modal (#7450) 2025-07-21 16:18:51 +05:30
Prateek ShouryaandGitHub f3daac6f95 [WEB-4457] refactor: decouple work item properties from mobx store (#7363) 2025-07-18 20:38:21 +05:30
Anmol Singh BhatiaandGitHub 5660b28574 [WEB-4507] fix: webhooks translation #7424 2025-07-18 20:09:39 +05:30
Vamsi KrishnaandGitHub d7d1545801 [WEB-4192]fix: inactive member is hidden in created by #7435 2025-07-18 20:08:59 +05:30
Vamsi KrishnaandGitHub 3ab1f0de84 [WEB-4522]fix: removed redundant state indicator (#7434)
* fix: removed redundant state indicator

* fix: removed redundant state indicator for relation list item
2025-07-18 20:08:36 +05:30
Bavisetti NarayanandGitHub a75ae71ff0 [WEB-4442] fix: removed the deleted labels from webhook payload (#7337)
* chore: removed the deleted label from payload

* chore: optimised the query

* chore: optimised the logic

* chore: defined function for prefetch
2025-07-18 20:08:01 +05:30
Aaryan KhandelwalandGitHub d5eb374217 [WEB-561] refactor: editor build config (#7442)
* refactor: editor build config

* fix: type errors
2025-07-18 20:06:48 +05:30
sriram veeraghantaandGitHub df4ea1f7ac fix: update tsup build packages (#7438) 2025-07-18 15:04:31 +05:30
Anmol Singh BhatiaandGitHub 07c80bb02c [WEB-4525] fix: project create/update error handling (#7429)
* chore: project create/update error handling

* chore: code refactor
2025-07-18 14:25:28 +05:30
Prateek ShouryaandGitHub 1ad792b4bb [WEB-4528] fix: calendar options interactions (#7437) 2025-07-18 14:25:08 +05:30
sriram veeraghantaandGitHub 0eb4af9d19 fix: on-headers vulnerability (#7436) 2025-07-18 13:27:53 +05:30
Aaryan KhandelwalandGitHub 7136b3129b [WIKI-543] fix: unable to delete last cell of a table (#7394)
* fix: delete last cell of a table

* refactor: last cell selection logic
2025-07-17 21:19:54 +05:30
Aaryan KhandelwalandGitHub 48f1999c95 [WIKI-557] fix: keyboard navigation in tables (#7428) 2025-07-17 15:15:18 +05:30
Akshat JainandGitHub 3783e34ae8 [INFRA-213] Fix aio dockerfile with new file structure (#7427)
* fix: update paths in Dockerfile and supervisor.conf for application directories

* fix: update live command in supervisor.conf to use server.js
2025-07-17 14:59:13 +05:30
SangeethaandGitHub e313aee3df [WEB-4346] chore: refactor copy_s3_object bg task (#7298) 2025-07-17 13:10:34 +05:30
Vipin ChaudharyandGitHub 6bb79df0eb [WIKI-547] fix: update find suggestion logic for emoji extension (#7411)
* fix: update find suggestion logic

* refactor: remove logs

* refactor : make logic simpler

* feat: check for one char to show suggestion

* refactor : import types from extension

* refactor: add early return

* refactor : put custom suggestion in helper

* fix : char

* fix: types
2025-07-17 13:07:12 +05:30
SangeethaandGitHub ec0ef98c1b [WEB-4281] chore: error code on project updation endpoint (#7218) 2025-07-17 13:05:24 +05:30
Vamsi KrishnaandGitHub 9523c28c3e [WEB-4514]chore: moved EIssueLayoutTypes enum to types #7416 2025-07-17 13:01:24 +05:30
Anmol Singh BhatiaandGitHub 156ed329ac [WEB-4510] fix: peek view ui layout (#7422) 2025-07-17 12:53:37 +05:30
Bavisetti NarayanandGitHub ef0e3dca12 [WEB-4520] chore: changed the serializer for cycle issues #7423 2025-07-17 12:53:04 +05:30
Aaryan KhandelwalandGitHub d8f2c97810 [WIKI-554] feat: delete multiple rows/columns from a table (#7426) 2025-07-17 12:52:33 +05:30
Anmol Singh BhatiaandGitHub 89983b06d2 [WEB-4515] fix: duplicate work item quick action #7421 2025-07-16 15:19:55 +05:30
Bavisetti NarayanandGitHub 3224122df0 [WEB-1643] chore: support cyrillic characters in subject #7336 2025-07-16 01:04:59 +05:30
99127ff8e4 [WEB-4479] feat: enable/disable SMTP configuration (#7393)
* feat: api update instance configuration

* chore: add enable_smtp key

* fix: empty string for enable_smtp key

* chore: update email_port and email_from

* fix: handled smtp enable disable

* fix: error handling

* fix: refactor

* fix: removed enabled toast

* fix: refactor

---------

Co-authored-by: gakshita <akshitagoyal1516@gmail.com>
Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com>
2025-07-16 01:04:18 +05:30
Anmol Singh BhatiaandGitHub da5390fa03 [WEB-4497] chore: reduce breadcrumb height (#7413)
* chore: reduce breadcrumb height from 3.75rem to 3.25rem

* chore: code refactor

* chore: code refactor
2025-07-16 01:03:31 +05:30
Anmol Singh BhatiaandGitHub ac22df3f88 [WEB-4451] chore: work item header quick action enhancements (#7414)
* chore: work item header quick action enhancements

* chore: code refactor
2025-07-16 00:52:30 +05:30
Aaryan KhandelwalandGitHub df762afaef [WIKI-551] refactor: work item activity logic #7417 2025-07-15 20:49:20 +05:30
Aaryan KhandelwalandGitHub 2058f06b8a [WIKI-549] fix: comment scroll logic #7412 2025-07-15 15:12:27 +05:30
Anmol Singh BhatiaandGitHub bfd4bd5e75 [WEB-4506] fix: date range modal z-index #7410 2025-07-15 13:51:16 +05:30
NikhilandGitHub ac928f263a [WIKI-520] fix: update content disposition handling in asset download endpoints (#7380) 2025-07-15 13:24:17 +05:30
Bavisetti NarayanandGitHub 62065a6ebb [WEB-4504] chore: added migration for app rail and reactions #7408 2025-07-14 20:57:29 +05:30
67b62dcbe3 [WEB-4498] improvement: remove workspace details from workspace members list API (#7407)
* [WEB-4498] improvement: remove workspace details from workspace members list API

* refactor: update select_related usage in workspace invitation and member views

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
2025-07-14 20:45:23 +05:30
Aaryan KhandelwalandGitHub a427367720 [WIKI-277] fix: unable to delete last table column/row in editor (#7389)
* fix: delete column not working

* refactor: delete column commad

* fix: delete last row logic

* refactor: row count logic

* refactor: isCellSelection utility function
2025-07-14 17:27:09 +05:30
Aaryan KhandelwalandGitHub c067eaa1ed [WIKI-542] refactor: editor extensions (#7345)
* refactor: editor extensions

* fix: placeholder extension
2025-07-14 17:10:32 +05:30
Aaryan KhandelwalandGitHub 2c70c1aaa8 [WIKI-509] feat: comment copy link option (#7385)
* feat: comment copy link option

* chore: add translations

* chore: update block position

* chore: rename use id scroll hook

* refactor: setTimeout function

* refactor: use-hash-scroll hook
2025-07-14 17:07:44 +05:30
Akshat JainandGitHub f90e553881 [INFRA-209] Remove nginx related configurations from plane community (#7406)
* Remove deprecated Nginx configuration files and scripts, including Dockerfiles, environment scripts, and configuration templates, to streamline the project structure.

* Update environment configuration and Docker setup for proxy services

- Added LISTEN_PORT and LISTEN_SSL_PORT variables to .env.example and related files.
- Updated Docker Compose files to reference new port variables instead of deprecated NGINX_PORT.
- Adjusted README and variable documentation to reflect changes in port configuration.
- Changed build context for proxy services to use the new directory structure.

* Refactor port configuration in environment and Docker files

- Renamed LISTEN_PORT and LISTEN_SSL_PORT to LISTEN_HTTP_PORT and LISTEN_HTTPS_PORT in .env.example and related files.
- Updated Docker Compose configurations to reflect the new port variable names.
- Adjusted documentation in README and variables.env to ensure consistency with the new naming conventions.
2025-07-14 16:38:27 +05:30
Aaryan KhandelwalandGitHub 0af0e52275 [WIKI-545] fix: shrinking headings in page table of contents #7404 2025-07-14 16:30:19 +05:30
Anmol Singh BhatiaandGitHub 7829e3adf5 [WEB-4494] fix: kanban delete modal ux #7405 2025-07-14 16:29:26 +05:30
Anmol Singh BhatiaandGitHub 47354f0e91 [WEB-4468] fix: setting sidebar truncate #7403 2025-07-14 16:26:04 +05:30
M. PalanikannanandGitHub db2f783d33 [WEB-4407] chore: add project page creation event tracking #7378 2025-07-14 15:30:45 +05:30
6d01622663 [INFRA-208] Reorganize deployment structure and update build workflows (#7391)
* refactor: reorganize deployment structure and update build workflows

- Restructure deployment directories from deploy/ to deployments/
- Move selfhost files to deployments/cli/community/
- Add new AIO community deployment setup
- Update GitHub Actions workflows for new directory structure
- Add Caddy proxy configuration for CE deployment
- Remove deprecated AIO build files and workflows
- Update build context paths in install scripts

* chore: update Dockerfile and supervisor configuration

- Changed `apk add` command in Dockerfile to use `--no-cache` for better image size management.
- Updated `build.sh` to ensure proper directory navigation with quotes around `dirname "$0"`.
- Modified `supervisor.conf` to set `stderr_logfile_maxbytes` to 50MB and added `stderr_logfile_backups` for better log management across multiple services.

* chore: consistent node and python version

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-07-14 14:38:43 +05:30
Anmol Singh BhatiaandGitHub dff2f8ae12 [WEB-4482] fix: kanban drag and delete #7402 2025-07-14 14:18:38 +05:30
sriram veeraghantaandGitHub 4c57ed4336 fix: local dev environment setup using yarn v4 (#7400)
* chore: adding typescript dependency to constants.

* chore: package deps update
2025-07-14 13:08:41 +05:30
NikhilandGitHub c8dab1cc9c [WEB-4484]chore: streamline issue saving process with advisory locks for sequence management (#7396)
* chore: streamline issue saving process with advisory locks for sequence management

* fix: update advisory lock usage in issue model for improved concurrency management
2025-07-14 13:08:26 +05:30
Aaron HeckmannandGitHub 17c90a9d93 [WEB-4485] - fix(dev): get local dev migrator working again #7397 2025-07-12 19:45:45 +05:30
Masoom WahidandGitHub a3714c8e3e feat : added Kabul to the list of timezones (#7370) 2025-07-12 19:30:39 +05:30
NikhilandGitHub 71cd36865b [WEB-4484]chore: streamline issue saving process with advisory locks for sequence management #7395 2025-07-11 21:18:31 +05:30
Dheeraj Kumar KetireddyandGitHub 4501e44702 [WEB-4481] optimize workspace member view by prefetching related assets (#7390) 2025-07-11 21:17:33 +05:30
Vamsi KrishnaandGitHub f661e06582 [WEB-4480]fix: full screen modals (#7392) 2025-07-11 16:41:27 +05:30
Vamsi KrishnaandGitHub 86f3ff1bd2 [WEB-4471]fix: full screen view visibility (#7387)
* fix: full screen mode for analytics, work items peek and timeline chart

* chore: added null check for portal elements
2025-07-11 11:50:26 +05:30
Aaryan KhandelwalandGitHub fce4729f22 [WIKI-497] refactor: table insert handles size (#7376)
* refactor: table insert handles size

* fix: insert handles z-idnex
2025-07-11 04:17:04 +05:30
Anmol Singh BhatiaandGitHub bb48250467 [WEB-4477] fix: sidebar resize behaviour #7386 2025-07-11 04:16:36 +05:30
Prateek ShouryaandGitHub 5fbe411e04 [WEB-4475] fix: add default theme for subscription colors (#7382) 2025-07-10 18:32:17 +05:30
2e75ff7f1c [WEB-4405] chore: project settings events (#7362)
* chore: workspace events

* fix: refactor

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-07-10 17:05:30 +05:30
eb4239417a [WEB-4405] chore: profile settings events (#7313)
* feat: event tracker helper

* feat: track click events for `data-ph-element`

* fix: handled click events

* fix: handled name

* chore: tracker element updates

* chore: remove export

* chore: tracker element type

* chore: track element and event helper.

* chore: minor improvements

* chore: minor refactors

* fix: workspace events

* fix: added slug

* fix: changes nomenclature

* fix: nomenclature

* chore: update event tracker helper types

* fix: data id

* refactor: cycle events (#7290)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* chore: update tracker elements

* chore: check for closest element with data-ph-element attribute

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* Refactor module events (#7291)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* refactor: module tracker event and element

* chore: update tracker element

* chore: revert unnecessary changes

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* refactor: global views, product tour, notifications, onboarding, users and sidebar related events

* chore: member tracker events (#7302)

* chore: member-tracker-events

* fix: constants

* refactor: update event tracker constants

* refactor: auth related event trackers (#7306)

* Chore: state events (#7307)

* chore: state events

* fix: refactor

* chore: project events (#7305)

* chore: project-events

* fix: refactor

* fix: removed hardcoded values

* fix: github redirection event

* chore: project page tracker events (#7304)

* added events for most page events

* refactor: simplify lock button event handling in PageLockControl

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>

* chore: minor cleanup and import fixes

* refactor: added tracker elements for buttons (#7308)

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>

* fix: event type

* refactor: posthog group event

* chore: removed instances of event tracker (#7309)

* refactor: remove event tracker stores and hooks

* refactor: remove event tracker store

* fix: build errors

* clean up event tracker payloads

* chore: profile settings events

* fix: refactor

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-07-10 17:02:06 +05:30
c6fbbfd8cc [WEB-4405] chore: workspace settings events (#7312)
* feat: event tracker helper

* feat: track click events for `data-ph-element`

* fix: handled click events

* fix: handled name

* chore: tracker element updates

* chore: remove export

* chore: tracker element type

* chore: track element and event helper.

* chore: minor improvements

* chore: minor refactors

* fix: workspace events

* fix: added slug

* fix: changes nomenclature

* fix: nomenclature

* chore: update event tracker helper types

* fix: data id

* refactor: cycle events (#7290)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* chore: update tracker elements

* chore: check for closest element with data-ph-element attribute

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* Refactor module events (#7291)

* chore: update event tracker helper types

* refactor: cycle events

* refactor: cycle events

* refactor: cycle event tracker

* refactor: module tracker event and element

* chore: update tracker element

* chore: revert unnecessary changes

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>

* refactor: global views, product tour, notifications, onboarding, users and sidebar related events

* chore: member tracker events (#7302)

* chore: member-tracker-events

* fix: constants

* refactor: update event tracker constants

* refactor: auth related event trackers (#7306)

* Chore: state events (#7307)

* chore: state events

* fix: refactor

* chore: project events (#7305)

* chore: project-events

* fix: refactor

* fix: removed hardcoded values

* fix: github redirection event

* chore: project page tracker events (#7304)

* added events for most page events

* refactor: simplify lock button event handling in PageLockControl

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>

* chore: minor cleanup and import fixes

* refactor: added tracker elements for buttons (#7308)

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>

* fix: event type

* refactor: posthog group event

* chore: removed instances of event tracker (#7309)

* refactor: remove event tracker stores and hooks

* refactor: remove event tracker store

* fix: build errors

* clean up event tracker payloads

* chore: workspace-settings-events

* fix: refactor

---------

Co-authored-by: Prateek Shourya <prateekshourya@Prateeks-MacBook-Pro.local>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
2025-07-10 16:54:06 +05:30
Anmol Singh BhatiaandGitHub 4d0a7e4658 [WEB-4470] fix: sidebar custom theme bg (#7379) 2025-07-10 15:35:13 +05:30
sriram veeraghantaandGitHub 1cb49cacae fix: packages dev command failing due to missing dependency mapping (#7381)
* fix: packages dev command failing due to missing dependency mapping

* fix: typescript version to match with other packages
2025-07-10 15:26:43 +05:30
Vamsi KrishnaandGitHub 3618ffc2c3 [WEB-4469] fix: full screen mode visibility for Gnatt and Analytics 2025-07-10 14:23:08 +05:30
Aaryan KhandelwalandGitHub fcb6e269a0 [WIKI-497] feat: table insert column and row handles (#7286)
* refactor: tables width and selection UI

* fix: drag handle position

* refactor: selection decorator logic

* refactor: adjacent cells logic

* refactor: folder structure

* chore: default column width for new columns

* refactor: plugin location

* feat: table insert handlers

* refactor: css rules

* refactor: plugins folder structure

* chore: add aria labels
2025-07-09 17:59:04 +05:30
853423608c [INFRA-206] fix: update build context and dockerfile paths in build.yml in deploy/selfhost (#7368)
* chore: update build context and dockerfile paths in build.yml for self-hosted deployment

* fix: build context

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-07-09 16:00:16 +05:30
Anmol Singh BhatiaandGitHub 9d88fc999c [WEB-4464] fix: content wrapper overflow (#7367) 2025-07-09 13:52:17 +05:30
M. PalanikannanandGitHub dadd76b3ed [WIKI-534] fix: save title input on closing (#7366)
* fix: save title input on closing

* fix: title input updatioin
2025-07-09 13:37:17 +05:30
sriram veeraghantaandGitHub 6ce700fd5d chore: format files using prettier (#7364)
* chore: format files using prettier

* chore: api server files formatted
2025-07-08 20:41:11 +05:30
Anmol Singh BhatiaandGitHub 0225d806cc [WEB-4328] feat: sidebar revamp (#7217)
* chore: sidebar peek state added to theme store

* chore: extended sidebar wrapper added

* chore: resizeable sidebar component added

* chore: appsidebar root component

* chore: updated sidebar and applied necessary changes across codebase

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: breadcrumb changes

* chore: sidebar improvements and fixes

* chore: enhancements and fixes

* fix: peek sidebar leave

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: icons added

* chore: add dock variable and toggle function to theme store

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: theme and workspace store updated

* chore: workspace content wrapper and apprail context

* chore: workspace and project wrapper updated

* chore: app rail component

* chore: content wrapper

* chore: sidebar component updated

* chore: layout changes and code refactoring

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: appsidebar toggle button

* chore: code refactor

* chore: workspace menu improvements

* chore: sidebar spacing and padding improvements

* chore: settings layout improvement

* chore: enhancements

* chore: extended sidebar code refactor

* chore: code refactor

* fix: merge conflict

* fix: merge conflict

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-07-08 20:18:39 +05:30
NikhilandGitHub fd9da3164e [WEB-4452] fix: enhance URL detection logic in contains_url function (#7352)
* fix: enhance URL detection logic in contains_url function

* fix: improve error handling in URL detection logic

* fix: enhance URL detection logic with length limits and edge case handling

* fix: adjust URL line length limit for improved detection accuracy
2025-07-08 18:28:47 +05:30
2465 changed files with 58889 additions and 30547 deletions
+45
View File
@@ -16,3 +16,48 @@ out/
**/out/
dist/
**/dist/
# Logs
npm-debug.log*
pnpm-debug.log*
.pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS junk
.DS_Store
Thumbs.db
# Editor settings
.vscode
.idea
# Coverage and test output
coverage/
**/coverage/
*.lcov
.junit/
test-results/
# Caches and build artifacts
.cache/
**/.cache/
storybook-static/
*storybook.log
*.tsbuildinfo
# Local env and secrets
.env.local
.env.development.local
.env.test.local
.env.production.local
.secrets
tmp/
temp/
# Database/cache dumps
*.rdb
*.rdb.gz
# Misc
*.pem
*.key
+13 -3
View File
@@ -15,12 +15,15 @@ RABBITMQ_USER="plane"
RABBITMQ_PASSWORD="plane"
RABBITMQ_VHOST="plane"
LISTEN_HTTP_PORT=80
LISTEN_HTTPS_PORT=443
# AWS Settings
AWS_REGION=""
AWS_ACCESS_KEY_ID="access-key"
AWS_SECRET_ACCESS_KEY="secret-key"
AWS_S3_ENDPOINT_URL="http://plane-minio:9000"
# Changing this requires change in the nginx.conf for uploads if using minio setup
# Changing this requires change in the proxy config for uploads if using minio setup
AWS_S3_BUCKET_NAME="uploads"
# Maximum file upload limit
FILE_SIZE_LIMIT=5242880
@@ -36,8 +39,15 @@ DOCKERIZED=1 # deprecated
# set to 1 If using the pre-configured minio setup
USE_MINIO=1
# Nginx Configuration
NGINX_PORT=80
# If SSL Cert to be generated, set CERT_EMAIl="email <EMAIL_ADDRESS>"
CERT_ACME_CA=https://acme-v02.api.letsencrypt.org/directory
TRUSTED_PROXIES=0.0.0.0/0
SITE_ADDRESS=:80
CERT_EMAIL=
# For DNS Challenge based certificate generation, set the CERT_ACME_DNS, CERT_EMAIL
# CERT_ACME_DNS="acme_dns <CERT_DNS_PROVIDER> <CERT_DNS_PROVIDER_API_KEY>"
CERT_ACME_DNS=
# Force HTTPS for handling SSL Termination
MINIO_ENDPOINT_SSL=0
-139
View File
@@ -1,139 +0,0 @@
name: Build AIO Base Image
on:
workflow_dispatch:
inputs:
base_tag_name:
description: 'Base Tag Name'
required: false
default: ''
env:
TARGET_BRANCH: ${{ github.ref_name }}
jobs:
base_build_setup:
name: Build Preparation
runs-on: ubuntu-latest
outputs:
gh_branch_name: ${{ steps.set_env_variables.outputs.TARGET_BRANCH }}
gh_buildx_driver: ${{ steps.set_env_variables.outputs.BUILDX_DRIVER }}
gh_buildx_version: ${{ steps.set_env_variables.outputs.BUILDX_VERSION }}
gh_buildx_platforms: ${{ steps.set_env_variables.outputs.BUILDX_PLATFORMS }}
gh_buildx_endpoint: ${{ steps.set_env_variables.outputs.BUILDX_ENDPOINT }}
image_tag: ${{ steps.set_env_variables.outputs.IMAGE_TAG }}
steps:
- id: set_env_variables
name: Set Environment Variables
run: |
echo "TARGET_BRANCH=${{ env.TARGET_BRANCH }}" >> $GITHUB_OUTPUT
if [ "${{ github.event.inputs.base_tag_name }}" != "" ]; then
echo "IMAGE_TAG=${{ github.event.inputs.base_tag_name }}" >> $GITHUB_OUTPUT
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
echo "IMAGE_TAG=latest" >> $GITHUB_OUTPUT
elif [ "${{ env.TARGET_BRANCH }}" == "preview" ]; then
echo "IMAGE_TAG=preview" >> $GITHUB_OUTPUT
else
echo "IMAGE_TAG=develop" >> $GITHUB_OUTPUT
fi
if [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
echo "BUILDX_DRIVER=cloud" >> $GITHUB_OUTPUT
echo "BUILDX_VERSION=lab:latest" >> $GITHUB_OUTPUT
echo "BUILDX_PLATFORMS=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
echo "BUILDX_ENDPOINT=makeplane/plane-dev" >> $GITHUB_OUTPUT
else
echo "BUILDX_DRIVER=docker-container" >> $GITHUB_OUTPUT
echo "BUILDX_VERSION=latest" >> $GITHUB_OUTPUT
echo "BUILDX_PLATFORMS=linux/amd64" >> $GITHUB_OUTPUT
echo "BUILDX_ENDPOINT=" >> $GITHUB_OUTPUT
fi
- id: checkout_files
name: Checkout Files
uses: actions/checkout@v4
full_base_build_push:
runs-on: ubuntu-latest
needs: [base_build_setup]
env:
BASE_IMG_TAG: makeplane/plane-aio-base:full-${{ needs.base_build_setup.outputs.image_tag }}
BUILDX_DRIVER: ${{ needs.base_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.base_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.base_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.base_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.9.0
with:
context: ./aio
file: ./aio/Dockerfile-base-full
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.BASE_IMG_TAG }}
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
slim_base_build_push:
runs-on: ubuntu-latest
needs: [base_build_setup]
env:
BASE_IMG_TAG: makeplane/plane-aio-base:slim-${{ needs.base_build_setup.outputs.image_tag }}
BUILDX_DRIVER: ${{ needs.base_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.base_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.base_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.base_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.9.0
with:
context: ./aio
file: ./aio/Dockerfile-base-slim
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.BASE_IMG_TAG }}
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
-207
View File
@@ -1,207 +0,0 @@
name: Branch Build AIO
on:
workflow_dispatch:
inputs:
full:
description: 'Run full build'
type: boolean
required: false
default: false
slim:
description: 'Run slim build'
type: boolean
required: false
default: false
base_tag_name:
description: 'Base Tag Name'
required: false
default: ''
release:
types: [released, prereleased]
env:
TARGET_BRANCH: ${{ github.ref_name || github.event.release.target_commitish }}
FULL_BUILD_INPUT: ${{ github.event.inputs.full }}
SLIM_BUILD_INPUT: ${{ github.event.inputs.slim }}
jobs:
branch_build_setup:
name: Build Setup
runs-on: ubuntu-latest
outputs:
gh_branch_name: ${{ steps.set_env_variables.outputs.TARGET_BRANCH }}
flat_branch_name: ${{ steps.set_env_variables.outputs.FLAT_BRANCH_NAME }}
gh_buildx_driver: ${{ steps.set_env_variables.outputs.BUILDX_DRIVER }}
gh_buildx_version: ${{ steps.set_env_variables.outputs.BUILDX_VERSION }}
gh_buildx_platforms: ${{ steps.set_env_variables.outputs.BUILDX_PLATFORMS }}
gh_buildx_endpoint: ${{ steps.set_env_variables.outputs.BUILDX_ENDPOINT }}
aio_base_tag: ${{ steps.set_env_variables.outputs.AIO_BASE_TAG }}
do_full_build: ${{ steps.set_env_variables.outputs.DO_FULL_BUILD }}
do_slim_build: ${{ steps.set_env_variables.outputs.DO_SLIM_BUILD }}
steps:
- id: set_env_variables
name: Set Environment Variables
run: |
if [ "${{ env.TARGET_BRANCH }}" == "master" ] || [ "${{ github.event_name }}" == "release" ]; then
echo "BUILDX_DRIVER=cloud" >> $GITHUB_OUTPUT
echo "BUILDX_VERSION=lab:latest" >> $GITHUB_OUTPUT
echo "BUILDX_PLATFORMS=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
echo "BUILDX_ENDPOINT=makeplane/plane-dev" >> $GITHUB_OUTPUT
echo "AIO_BASE_TAG=latest" >> $GITHUB_OUTPUT
else
echo "BUILDX_DRIVER=docker-container" >> $GITHUB_OUTPUT
echo "BUILDX_VERSION=latest" >> $GITHUB_OUTPUT
echo "BUILDX_PLATFORMS=linux/amd64" >> $GITHUB_OUTPUT
echo "BUILDX_ENDPOINT=" >> $GITHUB_OUTPUT
if [ "${{ github.event_name}}" == "workflow_dispatch" ] && [ "${{ github.event.inputs.base_tag_name }}" != "" ]; then
echo "AIO_BASE_TAG=${{ github.event.inputs.base_tag_name }}" >> $GITHUB_OUTPUT
elif [ "${{ env.TARGET_BRANCH }}" == "preview" ]; then
echo "AIO_BASE_TAG=preview" >> $GITHUB_OUTPUT
else
echo "AIO_BASE_TAG=develop" >> $GITHUB_OUTPUT
fi
fi
echo "TARGET_BRANCH=${{ env.TARGET_BRANCH }}" >> $GITHUB_OUTPUT
if [ "${{ env.FULL_BUILD_INPUT }}" == "true" ] || [ "${{github.event_name}}" == "push" ] || [ "${{github.event_name}}" == "release" ]; then
echo "DO_FULL_BUILD=true" >> $GITHUB_OUTPUT
else
echo "DO_FULL_BUILD=false" >> $GITHUB_OUTPUT
fi
if [ "${{ env.SLIM_BUILD_INPUT }}" == "true" ] || [ "${{github.event_name}}" == "push" ] || [ "${{github.event_name}}" == "release" ]; then
echo "DO_SLIM_BUILD=true" >> $GITHUB_OUTPUT
else
echo "DO_SLIM_BUILD=false" >> $GITHUB_OUTPUT
fi
FLAT_BRANCH_NAME=$(echo "${{ env.TARGET_BRANCH }}" | sed 's/[^a-zA-Z0-9]/-/g')
echo "FLAT_BRANCH_NAME=$FLAT_BRANCH_NAME" >> $GITHUB_OUTPUT
- id: checkout_files
name: Checkout Files
uses: actions/checkout@v4
full_build_push:
if: ${{ needs.branch_build_setup.outputs.do_full_build == 'true' }}
runs-on: ubuntu-22.04
needs: [branch_build_setup]
env:
BUILD_TYPE: full
AIO_BASE_TAG: ${{ needs.branch_build_setup.outputs.aio_base_tag }}
AIO_IMAGE_TAGS: makeplane/plane-aio:full-${{ needs.branch_build_setup.outputs.flat_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Set Docker Tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
TAG=makeplane/plane-aio:${{env.BUILD_TYPE}}-stable,makeplane/plane-aio:${{env.BUILD_TYPE}}-${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=makeplane/plane-aio:${{env.BUILD_TYPE}}-latest
else
TAG=${{ env.AIO_IMAGE_TAGS }}
fi
echo "AIO_IMAGE_TAGS=${TAG}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.9.0
with:
context: .
file: ./aio/Dockerfile-app
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.AIO_IMAGE_TAGS }}
push: true
build-args: |
BASE_TAG=${{ env.AIO_BASE_TAG }}
BUILD_TYPE=${{env.BUILD_TYPE}}
cache-from: type=gha
cache-to: type=gha,mode=max
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
slim_build_push:
if: ${{ needs.branch_build_setup.outputs.do_slim_build == 'true' }}
runs-on: ubuntu-22.04
needs: [branch_build_setup]
env:
BUILD_TYPE: slim
AIO_BASE_TAG: ${{ needs.branch_build_setup.outputs.aio_base_tag }}
AIO_IMAGE_TAGS: makeplane/plane-aio:slim-${{ needs.branch_build_setup.outputs.flat_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Set Docker Tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
TAG=makeplane/plane-aio:${{env.BUILD_TYPE}}-stable,makeplane/plane-aio:${{env.BUILD_TYPE}}-${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=makeplane/plane-aio:${{env.BUILD_TYPE}}-latest
else
TAG=${{ env.AIO_IMAGE_TAGS }}
fi
echo "AIO_IMAGE_TAGS=${TAG}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.9.0
with:
context: .
file: ./aio/Dockerfile-app
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.AIO_IMAGE_TAGS }}
push: true
build-args: |
BASE_TAG=${{ env.AIO_BASE_TAG }}
BUILD_TYPE=${{env.BUILD_TYPE}}
cache-from: type=gha
cache-to: type=gha,mode=max
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
+125 -11
View File
@@ -25,17 +25,27 @@ on:
required: false
default: false
type: boolean
aio_build:
description: "Build for AIO docker image"
required: false
default: false
type: boolean
push:
branches:
- preview
- canary
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
TARGET_BRANCH: ${{ github.ref_name }}
ARM64_BUILD: ${{ github.event.inputs.arm64 }}
BUILD_TYPE: ${{ github.event.inputs.build_type }}
RELEASE_VERSION: ${{ github.event.inputs.releaseVersion }}
IS_PRERELEASE: ${{ github.event.inputs.isPrerelease }}
AIO_BUILD: ${{ github.event.inputs.aio_build }}
jobs:
branch_build_setup:
@@ -54,11 +64,13 @@ jobs:
dh_img_live: ${{ steps.set_env_variables.outputs.DH_IMG_LIVE }}
dh_img_backend: ${{ steps.set_env_variables.outputs.DH_IMG_BACKEND }}
dh_img_proxy: ${{ steps.set_env_variables.outputs.DH_IMG_PROXY }}
dh_img_aio: ${{ steps.set_env_variables.outputs.DH_IMG_AIO }}
build_type: ${{steps.set_env_variables.outputs.BUILD_TYPE}}
build_release: ${{ steps.set_env_variables.outputs.BUILD_RELEASE }}
build_prerelease: ${{ steps.set_env_variables.outputs.BUILD_PRERELEASE }}
release_version: ${{ steps.set_env_variables.outputs.RELEASE_VERSION }}
aio_build: ${{ steps.set_env_variables.outputs.AIO_BUILD }}
steps:
- id: set_env_variables
@@ -84,12 +96,15 @@ jobs:
echo "DH_IMG_LIVE=plane-live" >> $GITHUB_OUTPUT
echo "DH_IMG_BACKEND=plane-backend" >> $GITHUB_OUTPUT
echo "DH_IMG_PROXY=plane-proxy" >> $GITHUB_OUTPUT
echo "DH_IMG_AIO=plane-aio-community" >> $GITHUB_OUTPUT
echo "BUILD_TYPE=${{env.BUILD_TYPE}}" >> $GITHUB_OUTPUT
BUILD_RELEASE=false
BUILD_PRERELEASE=false
RELVERSION="latest"
BUILD_AIO=${{ env.AIO_BUILD }}
if [ "${{ env.BUILD_TYPE }}" == "Release" ]; then
FLAT_RELEASE_VERSION=$(echo "${{ env.RELEASE_VERSION }}" | sed 's/[^a-zA-Z0-9.-]//g')
echo "FLAT_RELEASE_VERSION=${FLAT_RELEASE_VERSION}" >> $GITHUB_OUTPUT
@@ -108,10 +123,14 @@ jobs:
if [ "${{ env.IS_PRERELEASE }}" == "true" ]; then
BUILD_PRERELEASE=true
fi
BUILD_AIO=true
fi
echo "BUILD_RELEASE=${BUILD_RELEASE}" >> $GITHUB_OUTPUT
echo "BUILD_PRERELEASE=${BUILD_PRERELEASE}" >> $GITHUB_OUTPUT
echo "RELEASE_VERSION=${RELVERSION}" >> $GITHUB_OUTPUT
echo "AIO_BUILD=${BUILD_AIO}" >> $GITHUB_OUTPUT
- id: checkout_files
name: Checkout Files
@@ -242,13 +261,108 @@ jobs:
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
docker-image-owner: makeplane
docker-image-name: ${{ needs.branch_build_setup.outputs.dh_img_proxy }}
build-context: ./nginx
dockerfile-path: ./nginx/Dockerfile
build-context: ./apps/proxy
dockerfile-path: ./apps/proxy/Dockerfile.ce
buildx-driver: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
buildx-version: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
buildx-platforms: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }}
buildx-endpoint: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }}
branch_build_push_aio:
if: ${{ needs.branch_build_setup.outputs.aio_build == 'true' }}
name: Build-Push AIO Docker Image
runs-on: ubuntu-22.04
needs:
- branch_build_setup
- branch_build_push_admin
- branch_build_push_web
- branch_build_push_space
- branch_build_push_live
- branch_build_push_api
- branch_build_push_proxy
steps:
- name: Checkout Files
uses: actions/checkout@v4
- name: Prepare AIO Assets
id: prepare_aio_assets
run: |
cd deployments/aio/community
if [ "${{ needs.branch_build_setup.outputs.build_type }}" == "Release" ]; then
aio_version=${{ needs.branch_build_setup.outputs.release_version }}
else
aio_version=${{ needs.branch_build_setup.outputs.gh_branch_name }}
fi
bash ./build.sh --release $aio_version
echo "AIO_BUILD_VERSION=${aio_version}" >> $GITHUB_OUTPUT
- name: Upload AIO Assets
uses: actions/upload-artifact@v4
with:
path: ./deployments/aio/community/dist
name: aio-assets-dist
- name: AIO Build and Push
uses: makeplane/actions/build-push@v1.1.0
with:
build-release: ${{ needs.branch_build_setup.outputs.build_release }}
build-prerelease: ${{ needs.branch_build_setup.outputs.build_prerelease }}
release-version: ${{ needs.branch_build_setup.outputs.release_version }}
dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
docker-image-owner: makeplane
docker-image-name: ${{ needs.branch_build_setup.outputs.dh_img_aio }}
build-context: ./deployments/aio/community
dockerfile-path: ./deployments/aio/community/Dockerfile
buildx-driver: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
buildx-version: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
buildx-platforms: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }}
buildx-endpoint: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }}
additional-assets: aio-assets-dist
additional-assets-dir: ./deployments/aio/community/dist
build-args: |
PLANE_VERSION=${{ steps.prepare_aio_assets.outputs.AIO_BUILD_VERSION }}
upload_build_assets:
name: Upload Build Assets
runs-on: ubuntu-22.04
needs:
- branch_build_setup
- branch_build_push_admin
- branch_build_push_web
- branch_build_push_space
- branch_build_push_live
- branch_build_push_api
- branch_build_push_proxy
steps:
- name: Checkout Files
uses: actions/checkout@v4
- name: Update Assets
run: |
if [ "${{ needs.branch_build_setup.outputs.build_type }}" == "Release" ]; then
REL_VERSION=${{ needs.branch_build_setup.outputs.release_version }}
else
REL_VERSION=${{ needs.branch_build_setup.outputs.gh_branch_name }}
fi
cp ./deployments/cli/community/install.sh deployments/cli/community/setup.sh
sed -i 's/${APP_RELEASE:-stable}/${APP_RELEASE:-'${REL_VERSION}'}/g' deployments/cli/community/docker-compose.yml
# sed -i 's/APP_RELEASE=stable/APP_RELEASE='${REL_VERSION}'/g' deployments/cli/community/variables.env
- name: Upload Assets
uses: actions/upload-artifact@v4
with:
name: community-assets
path: |
./deployments/cli/community/setup.sh
./deployments/cli/community/restore.sh
./deployments/cli/community/restore-airgapped.sh
./deployments/cli/community/docker-compose.yml
./deployments/cli/community/variables.env
./deployments/swarm/community/swarm.sh
publish_release:
if: ${{ needs.branch_build_setup.outputs.build_type == 'Release' }}
name: Build Release
@@ -271,9 +385,9 @@ jobs:
- name: Update Assets
run: |
cp ./deploy/selfhost/install.sh deploy/selfhost/setup.sh
sed -i 's/${APP_RELEASE:-stable}/${APP_RELEASE:-'${REL_VERSION}'}/g' deploy/selfhost/docker-compose.yml
# sed -i 's/APP_RELEASE=stable/APP_RELEASE='${REL_VERSION}'/g' deploy/selfhost/variables.env
cp ./deployments/cli/community/install.sh deployments/cli/community/setup.sh
sed -i 's/${APP_RELEASE:-stable}/${APP_RELEASE:-'${REL_VERSION}'}/g' deployments/cli/community/docker-compose.yml
# sed -i 's/APP_RELEASE=stable/APP_RELEASE='${REL_VERSION}'/g' deployments/cli/community/variables.env
- name: Create Release
id: create_release
@@ -287,9 +401,9 @@ jobs:
prerelease: ${{ env.IS_PRERELEASE }}
generate_release_notes: true
files: |
${{ github.workspace }}/deploy/selfhost/setup.sh
${{ github.workspace }}/deploy/selfhost/swarm.sh
${{ github.workspace }}/deploy/selfhost/restore.sh
${{ github.workspace }}/deploy/selfhost/restore-airgapped.sh
${{ github.workspace }}/deploy/selfhost/docker-compose.yml
${{ github.workspace }}/deploy/selfhost/variables.env
${{ github.workspace }}/deployments/cli/community/setup.sh
${{ github.workspace }}/deployments/cli/community/restore.sh
${{ github.workspace }}/deployments/cli/community/restore-airgapped.sh
${{ github.workspace }}/deployments/cli/community/docker-compose.yml
${{ github.workspace }}/deployments/cli/community/variables.env
${{ github.workspace }}/deployments/swarm/community/swarm.sh
@@ -1,95 +0,0 @@
name: Build and Lint on Pull Request
on:
workflow_dispatch:
pull_request:
types: ["opened", "synchronize", "ready_for_review"]
jobs:
lint-server:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x" # Specify the Python version you need
- name: Install Pylint
run: python -m pip install ruff
- name: Install Server Dependencies
run: cd apps/server && pip install -r requirements.txt
- name: Lint apps/server
run: ruff check --fix apps/server
lint-admin:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn lint --filter=admin
lint-space:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn lint --filter=space
lint-web:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn lint --filter=web
build-admin:
needs: lint-admin
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn build --filter=admin
build-space:
needs: lint-space
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn build --filter=space
build-web:
needs: lint-web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
- run: yarn build --filter=web
-2
View File
@@ -17,8 +17,6 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Get PR Branch version
run: echo "PR_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
@@ -0,0 +1,40 @@
name: Build and lint API
on:
workflow_dispatch:
pull_request:
branches:
- "preview"
types:
- "opened"
- "synchronize"
- "ready_for_review"
- "review_requested"
- "reopened"
paths:
- "apps/api/**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-api:
name: Lint API
runs-on: ubuntu-latest
timeout-minutes: 25
if: |
github.event.pull_request.draft == false &&
github.event.pull_request.requested_reviewers != null
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install Pylint
run: python -m pip install ruff
- name: Install API Dependencies
run: cd apps/api && pip install -r requirements.txt
- name: Lint apps/api
run: ruff check --fix apps/api
@@ -0,0 +1,53 @@
name: Build and lint web apps
on:
workflow_dispatch:
pull_request:
branches:
- "preview"
types:
- "opened"
- "synchronize"
- "ready_for_review"
- "review_requested"
- "reopened"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-lint:
name: Build and lint web apps
runs-on: ubuntu-latest
timeout-minutes: 25
if: |
github.event.pull_request.draft == false &&
github.event.pull_request.requested_reviewers != null
env:
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }}
TURBO_SCM_HEAD: ${{ github.sha }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 50
filter: blob:none
- name: Set up Node.js
uses: actions/setup-node@v4
- name: Enable Corepack and pnpm
run: corepack enable pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint Affected
run: pnpm turbo run check:lint --affected
- name: Check Affected format
run: pnpm turbo run check:format --affected
- name: Build Affected
run: pnpm turbo run build --affected
+7 -3
View File
@@ -24,11 +24,13 @@ out/
.DS_Store
*.pem
.history
tsconfig.tsbuildinfo
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.pnpm-debug.log*
# Local env files
@@ -60,6 +62,7 @@ node_modules/
assets/dist/
npm-debug.log
yarn-error.log
pnpm-debug.log
# Editor directories and files
.idea
@@ -75,10 +78,9 @@ package-lock.json
# lock files
package-lock.json
pnpm-lock.yaml
pnpm-workspace.yaml
.npmrc
.secrets
tmp/
@@ -95,3 +97,5 @@ dev-editor
# Redis
*.rdb
*.rdb.gz
storybook-static
+34
View File
@@ -0,0 +1,34 @@
# Enforce pnpm workspace behavior and allow Turbo's lifecycle hooks if scripts are disabled
# This repo uses pnpm with workspaces.
# Prefer linking local workspace packages when available
prefer-workspace-packages=true
link-workspace-packages=true
shared-workspace-lockfile=true
# Make peer installs smoother across the monorepo
auto-install-peers=true
strict-peer-dependencies=false
# If scripts are disabled (e.g., CI with --ignore-scripts), allowlisted packages can still run their hooks
# Turbo occasionally performs postinstall tasks for optimal performance
# moved to pnpm-workspace.yaml: onlyBuiltDependencies (e.g., allow turbo)
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=prettier
public-hoist-pattern[]=typescript
# Reproducible installs across CI and dev
prefer-frozen-lockfile=true
# Prefer resolving to highest versions in monorepo to reduce duplication
resolution-mode=highest
# Speed up native module builds by caching side effects
side-effects-cache=true
# Speed up local dev by reusing local store when possible
prefer-offline=true
# Ensure workspace protocol is used when adding internal deps
save-workspace-protocol=true
-1
View File
@@ -1 +0,0 @@
nodeLinker: node-modules
+24 -26
View File
@@ -73,7 +73,7 @@ docker compose -f docker-compose-local.yml up
4. Start web apps:
```bash
yarn dev
pnpm dev
```
5. Open your browser to http://localhost:3001/god-mode/ and register yourself as instance admin
@@ -186,41 +186,39 @@ Adding a new language involves several steps to ensure it integrates seamlessly
1. **Update type definitions**
Add the new language to the TLanguage type in the language definitions file:
```typescript
// types/language.ts
export type TLanguage = "en" | "fr" | "your-lang";
```
```ts
// packages/i18n/src/types/language.ts
export type TLanguage = "en" | "fr" | "your-lang";
```
2. **Add language configuration**
1. **Add language configuration**
Include the new language in the list of supported languages:
```ts
// packages/i18n/src/constants/language.ts
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
{ label: "English", value: "en" },
{ label: "Your Language", value: "your-lang" }
];
```
```typescript
// constants/language.ts
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
{ label: "English", value: "en" },
{ label: "Your Language", value: "your-lang" }
];
```
3. **Create translation files**
2. **Create translation files**
1. Create a new folder for your language under locales (e.g., `locales/your-lang/`).
2. Add a `translations.json` file inside the folder.
3. Copy the structure from an existing translation file and translate all keys.
4. **Update import logic**
3. **Update import logic**
Modify the language import logic to include your new language:
```typescript
private importLanguageFile(language: TLanguage): Promise<any> {
switch (language) {
case "your-lang":
return import("../locales/your-lang/translations.json");
// ...
}
}
```
```ts
private importLanguageFile(language: TLanguage): Promise<any> {
switch (language) {
case "your-lang":
return import("../locales/your-lang/translations.json");
// ...
}
}
```
### Quality checklist
+29 -46
View File
@@ -2,11 +2,10 @@
<p align="center">
<a href="https://plane.so">
<img src="https://plane-marketing.s3.ap-south-1.amazonaws.com/plane-readme/plane_logo_.webp" alt="Plane Logo" width="70">
<img src="https://media.docs.plane.so/logo/plane_github_readme.png" alt="Plane Logo" width="400">
</a>
</p>
<h1 align="center"><b>Plane</b></h1>
<p align="center"><b>Open-source project management that unlocks customer value</b></p>
<p align="center"><b>Modern project management for all teams</b></p>
<p align="center">
<a href="https://discord.com/invite/A92xrEGCge">
@@ -25,14 +24,7 @@
<p>
<a href="https://app.plane.so/#gh-light-mode-only" target="_blank">
<img
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/plane-readme/plane_screen.webp"
alt="Plane Screens"
width="100%"
/>
</a>
<a href="https://app.plane.so/#gh-dark-mode-only" target="_blank">
<img
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/plane-readme/plane_screens_dark_mode.webp"
src="https://media.docs.plane.so/GitHub-readme/github-top.webp"
alt="Plane Screens"
width="100%"
/>
@@ -48,13 +40,13 @@ Meet [Plane](https://plane.so/), an open-source project management tool to track
Getting started with Plane is simple. Choose the setup that works best for you:
- **Plane Cloud**
Sign up for a free account on [Plane Cloud](https://app.plane.so)—it's the fastest way to get up and running without worrying about infrastructure.
Sign up for a free account on [Plane Cloud](https://app.plane.so)—it's the fastest way to get up and running without worrying about infrastructure.
- **Self-host Plane**
Prefer full control over your data and infrastructure? Install and run Plane on your own servers. Follow our detailed [deployment guides](https://developers.plane.so/self-hosting/overview) to get started.
Prefer full control over your data and infrastructure? Install and run Plane on your own servers. Follow our detailed [deployment guides](https://developers.plane.so/self-hosting/overview) to get started.
| Installation methods | Docs link |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Installation methods | Docs link |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Docker | [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://developers.plane.so/self-hosting/methods/docker-compose) |
| Kubernetes | [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://developers.plane.so/self-hosting/methods/kubernetes) |
@@ -63,58 +55,58 @@ Prefer full control over your data and infrastructure? Install and run Plane on
## 🌟 Features
- **Issues**
Efficiently create and manage tasks with a robust rich text editor that supports file uploads. Enhance organization and tracking by adding sub-properties and referencing related issues.
Efficiently create and manage tasks with a robust rich text editor that supports file uploads. Enhance organization and tracking by adding sub-properties and referencing related issues.
- **Cycles**
Maintain your teams momentum with Cycles. Track progress effortlessly using burn-down charts and other insightful tools.
Maintain your teams momentum with Cycles. Track progress effortlessly using burn-down charts and other insightful tools.
- **Modules**
Simplify complex projects by dividing them into smaller, manageable modules.
Simplify complex projects by dividing them into smaller, manageable modules.
- **Views**
Customize your workflow by creating filters to display only the most relevant issues. Save and share these views with ease.
Customize your workflow by creating filters to display only the most relevant issues. Save and share these views with ease.
- **Pages**
Capture and organize ideas using Plane Pages, complete with AI capabilities and a rich text editor. Format text, insert images, add hyperlinks, or convert your notes into actionable items.
Capture and organize ideas using Plane Pages, complete with AI capabilities and a rich text editor. Format text, insert images, add hyperlinks, or convert your notes into actionable items.
- **Analytics**
Access real-time insights across all your Plane data. Visualize trends, remove blockers, and keep your projects moving forward.
Access real-time insights across all your Plane data. Visualize trends, remove blockers, and keep your projects moving forward.
- **Drive** (_coming soon_): The drive helps you share documents, images, videos, or any other files that make sense to you or your team and align on the problem/solution.
## 🛠️ Local development
See [CONTRIBUTING](./CONTRIBUTING.md)
## ⚙️ Built with
[![Next JS](https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white)](https://nextjs.org/)
[![Django](https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=green)](https://www.djangoproject.com/)
[![Node JS](https://img.shields.io/badge/node.js-339933?style=for-the-badge&logo=Node.js&logoColor=white)](https://nodejs.org/en)
## 📸 Screenshots
<p>
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Issues_rNZjrGgFl.png?updatedAt=1709298765880"
src="https://media.docs.plane.so/GitHub-readme/github-work-items.webp"
alt="Plane Views"
width="100%"
/>
</a>
</p>
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Cycles_jCDhqmTl9.png?updatedAt=1709298780697"
width="100%"
/>
</a>
</p>
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Modules_PSCVsbSfI.png?updatedAt=1709298796783"
src="https://media.docs.plane.so/GitHub-readme/github-cycles.webp"
width="100%"
/>
</a>
</p>
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://media.docs.plane.so/GitHub-readme/github-modules.webp"
alt="Plane Cycles and Modules"
width="100%"
/>
@@ -123,7 +115,7 @@ See [CONTRIBUTING](./CONTRIBUTING.md)
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Views_uxXsRatS4.png?updatedAt=1709298834522"
src="https://media.docs.plane.so/GitHub-readme/github-views.webp"
alt="Plane Analytics"
width="100%"
/>
@@ -132,25 +124,16 @@ See [CONTRIBUTING](./CONTRIBUTING.md)
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Analytics_0o22gLRtp.png?updatedAt=1709298834389"
src="https://media.docs.plane.so/GitHub-readme/github-analytics.webp"
alt="Plane Pages"
width="100%"
/>
</a>
</p>
</p>
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/w2okwbtu2/Drive_LlfeY4xn3.png?updatedAt=1709298837917"
alt="Plane Command Menu"
width="100%"
/>
</a>
</p>
</p>
## 📝 Documentation
Explore Plane's [product documentation](https://docs.plane.so/) and [developer documentation](https://developers.plane.so/) to learn about features, setup, and usage.
## ❤️ Community
@@ -186,6 +169,6 @@ Please read [CONTRIBUTING.md](https://github.com/makeplane/plane/blob/master/CON
<img src="https://contrib.rocks/image?repo=makeplane/plane" />
</a>
## License
This project is licensed under the [GNU Affero General Public License v3.0](https://github.com/makeplane/plane/blob/master/LICENSE.txt).
-182
View File
@@ -1,182 +0,0 @@
ARG BASE_TAG=develop
ARG BUILD_TYPE=full
# *****************************************************************************
# STAGE 1: Build the project
# *****************************************************************************
FROM node:18-alpine AS builder
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
RUN yarn global add turbo
COPY . .
RUN turbo prune --scope=web --scope=space --scope=admin --docker
# *****************************************************************************
# STAGE 2: Install dependencies & build the project
# *****************************************************************************
# Add lockfile and package.json's of isolated subworkspace
FROM node:18-alpine AS installer
RUN apk add --no-cache libc6-compat
WORKDIR /app
# First install the dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install
# # Build the project
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
ARG NEXT_PUBLIC_API_BASE_URL=""
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_URL=""
ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
ENV NEXT_PUBLIC_SPACE_BASE_PATH=$NEXT_PUBLIC_SPACE_BASE_PATH
ARG NEXT_PUBLIC_WEB_BASE_URL=""
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV TURBO_TELEMETRY_DISABLED=1
RUN yarn turbo run build --filter=web --filter=space --filter=admin
# *****************************************************************************
# STAGE 3: Copy the project and start it
# *****************************************************************************
FROM makeplane/plane-aio-base:${BUILD_TYPE}-${BASE_TAG} AS runner
WORKDIR /app
SHELL [ "/bin/bash", "-c" ]
# PYTHON APPLICATION SETUP
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
COPY apps/api/requirements.txt ./api/
COPY apps/api/requirements ./api/requirements
RUN pip install -r ./api/requirements.txt --compile --no-cache-dir
# Add in Django deps and generate Django's static files
COPY apps/api/manage.py ./api/manage.py
COPY apps/api/plane ./api/plane/
COPY apps/api/templates ./api/templates/
COPY package.json ./api/package.json
COPY apps/api/bin ./api/bin/
RUN chmod +x ./api/bin/*
RUN chmod -R 777 ./api/
# NEXTJS BUILDS
COPY --from=installer /app/web/next.config.js ./web/
COPY --from=installer /app/web/package.json ./web/
COPY --from=installer /app/web/.next/standalone ./web
COPY --from=installer /app/web/.next/static ./web/web/.next/static
COPY --from=installer /app/web/public ./web/web/public
COPY --from=installer /app/space/next.config.js ./space/
COPY --from=installer /app/space/package.json ./space/
COPY --from=installer /app/space/.next/standalone ./space
COPY --from=installer /app/space/.next/static ./space/space/.next/static
COPY --from=installer /app/space/public ./space/space/public
COPY --from=installer /app/admin/next.config.js ./admin/
COPY --from=installer /app/admin/package.json ./admin/
COPY --from=installer /app/admin/.next/standalone ./admin
COPY --from=installer /app/admin/.next/static ./admin/admin/.next/static
COPY --from=installer /app/admin/public ./admin/admin/public
ARG NEXT_PUBLIC_API_BASE_URL=""
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_URL=""
ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
ENV NEXT_PUBLIC_SPACE_BASE_PATH=$NEXT_PUBLIC_SPACE_BASE_PATH
ARG NEXT_PUBLIC_WEB_BASE_URL=""
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV TURBO_TELEMETRY_DISABLED=1
ARG BUILD_TYPE=full
ENV BUILD_TYPE=$BUILD_TYPE
COPY aio/supervisord-${BUILD_TYPE}-base /app/supervisord.conf
COPY aio/supervisord-app /app/supervisord-app
RUN cat /app/supervisord-app >> /app/supervisord.conf && \
rm /app/supervisord-app
COPY ./aio/nginx.conf /etc/nginx/nginx.conf.template
# if build type is full, run the below copy pg-setup.sh
COPY aio/postgresql.conf /etc/postgresql/postgresql.conf
COPY aio/pg-setup.sh /app/pg-setup.sh
RUN chmod +x /app/pg-setup.sh
# *****************************************************************************
# APPLICATION ENVIRONMENT SETTINGS
# *****************************************************************************
ENV APP_DOMAIN=localhost
ENV WEB_URL=http://${APP_DOMAIN}
ENV DEBUG=0
ENV CORS_ALLOWED_ORIGINS=http://${APP_DOMAIN},https://${APP_DOMAIN}
# Secret Key
ENV SECRET_KEY=60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5
# Gunicorn Workers
ENV GUNICORN_WORKERS=1
ENV POSTGRES_USER="plane"
ENV POSTGRES_PASSWORD="plane"
ENV POSTGRES_DB="plane"
ENV POSTGRES_HOST="localhost"
ENV POSTGRES_PORT="5432"
ENV DATABASE_URL="postgresql://plane:plane@localhost:5432/plane"
ENV REDIS_HOST="localhost"
ENV REDIS_PORT="6379"
ENV REDIS_URL="redis://localhost:6379"
ENV USE_MINIO="1"
ENV AWS_REGION=""
ENV AWS_ACCESS_KEY_ID="access-key"
ENV AWS_SECRET_ACCESS_KEY="secret-key"
ENV AWS_S3_ENDPOINT_URL="http://localhost:9000"
ENV AWS_S3_BUCKET_NAME="uploads"
ENV MINIO_ROOT_USER="access-key"
ENV MINIO_ROOT_PASSWORD="secret-key"
ENV BUCKET_NAME="uploads"
ENV FILE_SIZE_LIMIT="5242880"
# *****************************************************************************
RUN /app/pg-setup.sh
CMD ["/usr/bin/supervisord", "-c", "/app/supervisord.conf"]
-73
View File
@@ -1,73 +0,0 @@
FROM --platform=$BUILDPLATFORM tonistiigi/binfmt AS binfmt
FROM python:3.12-slim
# Set environment variables to non-interactive for apt
ENV DEBIAN_FRONTEND=noninteractive
ENV BUILD_TYPE=full
SHELL [ "/bin/bash", "-c" ]
WORKDIR /app
RUN mkdir -p /app/{data,logs} && \
mkdir -p /app/data/{redis,pg,minio,nginx} && \
mkdir -p /app/logs/{access,error} && \
mkdir -p /etc/supervisor/conf.d
# Update the package list and install prerequisites
RUN apt-get update && \
apt-get install -y \
gnupg2 curl ca-certificates lsb-release software-properties-common \
build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev xz-utils \
tk-dev libffi-dev liblzma-dev supervisor nginx nano vim ncdu \
sudo lsof net-tools libpq-dev procps gettext
# Install Redis 7.2
RUN echo "deb http://deb.debian.org/debian $(lsb_release -cs)-backports main" > /etc/apt/sources.list.d/backports.list && \
curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" > /etc/apt/sources.list.d/redis.list && \
apt-get update && \
apt-get install -y redis-server
# Install PostgreSQL 15
ENV POSTGRES_VERSION=15
RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/pgdg-archive-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/pgdg-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
apt-get update && \
apt-get install -y postgresql-$POSTGRES_VERSION postgresql-client-$POSTGRES_VERSION && \
mkdir -p /var/lib/postgresql/data && \
chown -R postgres:postgres /var/lib/postgresql
COPY postgresql.conf /etc/postgresql/postgresql.conf
RUN sudo -u postgres /usr/lib/postgresql/$POSTGRES_VERSION/bin/initdb -D /var/lib/postgresql/data
# Install MinIO
ARG TARGETARCH
RUN if [ "$TARGETARCH" = "amd64" ]; then \
curl -fSl https://dl.min.io/server/minio/release/linux-amd64/minio -o /usr/local/bin/minio; \
elif [ "$TARGETARCH" = "arm64" ]; then \
curl -fSl https://dl.min.io/server/minio/release/linux-arm64/minio -o /usr/local/bin/minio; \
else \
echo "Unsupported architecture: $TARGETARCH"; exit 1; \
fi && \
chmod +x /usr/local/bin/minio
# Install Node.js 18
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install -y nodejs && \
python -m pip install --upgrade pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Create Supervisor configuration file
COPY supervisord-full-base /app/supervisord.conf
COPY nginx.conf /etc/nginx/nginx.conf.template
COPY env.sh /app/nginx-start.sh
RUN chmod +x /app/nginx-start.sh
# Expose ports for Redis, PostgreSQL, and MinIO
EXPOSE 6379 5432 9000 80 443
# Start Supervisor
CMD ["/usr/bin/supervisord", "-c", "/app/supervisord.conf"]
-45
View File
@@ -1,45 +0,0 @@
FROM --platform=$BUILDPLATFORM tonistiigi/binfmt AS binfmt
FROM python:3.12-slim
# Set environment variables to non-interactive for apt
ENV DEBIAN_FRONTEND=noninteractive
ENV BUILD_TYPE=slim
SHELL [ "/bin/bash", "-c" ]
WORKDIR /app
RUN mkdir -p /app/{data,logs} && \
mkdir -p /app/data/{nginx} && \
mkdir -p /app/logs/{access,error} && \
mkdir -p /etc/supervisor/conf.d
# Update the package list and install prerequisites
RUN apt-get update && \
apt-get install -y \
gnupg2 curl ca-certificates lsb-release software-properties-common \
build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev xz-utils \
tk-dev libffi-dev liblzma-dev supervisor nginx nano vim ncdu \
sudo lsof net-tools libpq-dev procps gettext
# Install Node.js 18
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install -y nodejs
RUN python -m pip install --upgrade pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Create Supervisor configuration file
COPY supervisord-slim-base /app/supervisord.conf
COPY nginx.conf /etc/nginx/nginx.conf.template
COPY env.sh /app/nginx-start.sh
RUN chmod +x /app/nginx-start.sh
# Expose ports for Redis, PostgreSQL, and MinIO
EXPOSE 80 443
# Start Supervisor
CMD ["/usr/bin/supervisord", "-c", "/app/supervisord.conf"]
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
export dollar="$"
export http_upgrade="http_upgrade"
export scheme="scheme"
envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
exec nginx -g 'daemon off;'
-72
View File
@@ -1,72 +0,0 @@
events {
}
http {
sendfile on;
server {
listen 80;
root /www/data/;
access_log /var/log/nginx/access.log;
client_max_body_size ${FILE_SIZE_LIMIT};
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Forwarded-Proto "${dollar}scheme";
add_header X-Forwarded-Host "${dollar}host";
add_header X-Forwarded-For "${dollar}proxy_add_x_forwarded_for";
add_header X-Real-IP "${dollar}remote_addr";
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:3001/;
}
location /spaces/ {
rewrite ^/spaces/?$ /spaces/login break;
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:3002/spaces/;
}
location /god-mode/ {
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:3003/god-mode/;
}
location /api/ {
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:8000/api/;
}
location /auth/ {
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:8000/auth/;
}
location /${BUCKET_NAME}/ {
proxy_http_version 1.1;
proxy_set_header Upgrade ${dollar}http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ${dollar}http_host;
proxy_pass http://localhost:9000/uploads/;
}
}
}
-14
View File
@@ -1,14 +0,0 @@
#!/bin/bash
if [ "$BUILD_TYPE" == "full" ]; then
export PGHOST=localhost
sudo -u postgres "/usr/lib/postgresql/${POSTGRES_VERSION}/bin/pg_ctl" -D /var/lib/postgresql/data start
sudo -u postgres "/usr/lib/postgresql/${POSTGRES_VERSION}/bin/psql" --command "CREATE USER $POSTGRES_USER WITH SUPERUSER PASSWORD '$POSTGRES_PASSWORD';" && \
sudo -u postgres "/usr/lib/postgresql/${POSTGRES_VERSION}/bin/createdb" -O "$POSTGRES_USER" "$POSTGRES_DB" && \
sudo -u postgres "/usr/lib/postgresql/${POSTGRES_VERSION}/bin/psql" --command "GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB TO $POSTGRES_USER;" && \
sudo -u postgres "/usr/lib/postgresql/${POSTGRES_VERSION}/bin/pg_ctl" -D /var/lib/postgresql/data stop
fi
-815
View File
@@ -1,815 +0,0 @@
# -----------------------------
# PostgreSQL configuration file
# -----------------------------
#
# This file consists of lines of the form:
#
# name = value
#
# (The "=" is optional.) Whitespace may be used. Comments are introduced with
# "#" anywhere on a line. The complete list of parameter names and allowed
# values can be found in the PostgreSQL documentation.
#
# The commented-out settings shown in this file represent the default values.
# Re-commenting a setting is NOT sufficient to revert it to the default value;
# you need to reload the server.
#
# This file is read on server startup and when the server receives a SIGHUP
# signal. If you edit the file on a running system, you have to SIGHUP the
# server for the changes to take effect, run "pg_ctl reload", or execute
# "SELECT pg_reload_conf()". Some parameters, which are marked below,
# require a server shutdown and restart to take effect.
#
# Any parameter can also be given as a command-line option to the server, e.g.,
# "postgres -c log_connections=on". Some parameters can be changed at run time
# with the "SET" SQL command.
#
# Memory units: B = bytes Time units: us = microseconds
# kB = kilobytes ms = milliseconds
# MB = megabytes s = seconds
# GB = gigabytes min = minutes
# TB = terabytes h = hours
# d = days
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
# The default values of these variables are driven from the -D command-line
# option or PGDATA environment variable, represented here as ConfigDir.
data_directory = '/var/lib/postgresql/data' # use data in another directory
# (change requires restart)
hba_file = '/etc/postgresql/15/main/pg_hba.conf' # host-based authentication file
# (change requires restart)
ident_file = '/etc/postgresql/15/main/pg_ident.conf' # ident configuration file
# (change requires restart)
# If external_pid_file is not explicitly set, no extra PID file is written.
external_pid_file = '/var/run/postgresql/15-main.pid' # write an extra PID file
# (change requires restart)
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
port = 5432 # (change requires restart)
max_connections = 200 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories
# (change requires restart)
#unix_socket_group = '' # (change requires restart)
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
# (change requires restart)
#bonjour = off # advertise server via Bonjour
# (change requires restart)
#bonjour_name = '' # defaults to the computer name
# (change requires restart)
# - TCP settings -
# see "man tcp" for details
#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
# 0 selects the system default
#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
# 0 selects the system default
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
# 0 selects the system default
#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
# 0 selects the system default
#client_connection_check_interval = 0 # time between checks for client
# disconnection while running queries;
# 0 for never
# - Authentication -
#authentication_timeout = 1min # 1s-600s
#password_encryption = scram-sha-256 # scram-sha-256 or md5
#db_user_namespace = off
# GSSAPI using Kerberos
#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
#krb_caseins_users = off
# - SSL -
ssl = on
#ssl_ca_file = ''
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
#ssl_crl_file = ''
#ssl_crl_dir = ''
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1.2'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
# - Memory -
shared_buffers = 256MB # min 128kB
# (change requires restart)
#huge_pages = try # on, off, or try
# (change requires restart)
#huge_page_size = 0 # zero for system default
# (change requires restart)
#temp_buffers = 8MB # min 800kB
#max_prepared_transactions = 0 # zero disables the feature
# (change requires restart)
# Caution: it is not advisable to set max_prepared_transactions nonzero unless
# you actively intend to use prepared transactions.
#work_mem = 4MB # min 64kB
#hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem
#maintenance_work_mem = 64MB # min 1MB
#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
#logical_decoding_work_mem = 64MB # min 64kB
#max_stack_depth = 2MB # min 100kB
#shared_memory_type = mmap # the default is the first option
# supported by the operating system:
# mmap
# sysv
# windows
# (change requires restart)
dynamic_shared_memory_type = posix # the default is usually the first option
# supported by the operating system:
# posix
# sysv
# windows
# mmap
# (change requires restart)
#min_dynamic_shared_memory = 0MB # (change requires restart)
# - Disk -
#temp_file_limit = -1 # limits per-process temp file space
# in kilobytes, or -1 for no limit
# - Kernel Resources -
#max_files_per_process = 1000 # min 64
# (change requires restart)
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables)
#vacuum_cost_page_hit = 1 # 0-10000 credits
#vacuum_cost_page_miss = 2 # 0-10000 credits
#vacuum_cost_page_dirty = 20 # 0-10000 credits
#vacuum_cost_limit = 200 # 1-10000 credits
# - Background Writer -
#bgwriter_delay = 200ms # 10-10000ms between rounds
#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables
#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
#bgwriter_flush_after = 512kB # measured in pages, 0 disables
# - Asynchronous Behavior -
#backend_flush_after = 0 # measured in pages, 0 disables
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching
#max_worker_processes = 8 # (change requires restart)
#max_parallel_workers_per_gather = 2 # limited by max_parallel_workers
#max_parallel_maintenance_workers = 2 # limited by max_parallel_workers
#max_parallel_workers = 8 # number of max_worker_processes that
# can be used in parallel operations
#parallel_leader_participation = on
#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
# (change requires restart)
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------
# - Settings -
#wal_level = replica # minimal, replica, or logical
# (change requires restart)
#fsync = on # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
#synchronous_commit = on # synchronization level;
# off, local, remote_write, remote_apply, or on
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
# fdatasync (default on Linux and FreeBSD)
# fsync
# fsync_writethrough
# open_sync
#full_page_writes = on # recover from partial page writes
#wal_log_hints = off # also do full page writes of non-critical updates
# (change requires restart)
#wal_compression = off # enables compression of full-page writes;
# off, pglz, lz4, zstd, or on
#wal_init_zero = on # zero-fill new WAL files
#wal_recycle = on # recycle WAL files
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
#wal_writer_delay = 200ms # 1-10000 milliseconds
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
#wal_skip_threshold = 2MB
#commit_delay = 0 # range 0-100000, in microseconds
#commit_siblings = 5 # range 1-1000
# - Checkpoints -
#checkpoint_timeout = 5min # range 30s-1d
#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 256kB # measured in pages, 0 disables
#checkpoint_warning = 30s # 0 disables
max_wal_size = 1GB
min_wal_size = 80MB
# - Prefetching during recovery -
#recovery_prefetch = try # prefetch pages referenced in the WAL?
#wal_decode_buffer_size = 512kB # lookahead window used for prefetching
# (change requires restart)
# - Archiving -
#archive_mode = off # enables archiving; off, on, or always
# (change requires restart)
#archive_library = '' # library to use to archive a logfile segment
# (empty string indicates archive_command should
# be used)
#archive_command = '' # command to use to archive a logfile segment
# placeholders: %p = path of file to archive
# %f = file name only
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
# - Archive Recovery -
# These are only used in recovery mode.
#restore_command = '' # command to use to restore an archived logfile segment
# placeholders: %p = path of file to restore
# %f = file name only
# e.g. 'cp /mnt/server/archivedir/%f %p'
#archive_cleanup_command = '' # command to execute at every restartpoint
#recovery_end_command = '' # command to execute at completion of recovery
# - Recovery Target -
# Set these only when performing a targeted recovery.
#recovery_target = '' # 'immediate' to end recovery as soon as a
# consistent state is reached
# (change requires restart)
#recovery_target_name = '' # the named restore point to which recovery will proceed
# (change requires restart)
#recovery_target_time = '' # the time stamp up to which recovery will proceed
# (change requires restart)
#recovery_target_xid = '' # the transaction ID up to which recovery will proceed
# (change requires restart)
#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
# (change requires restart)
#recovery_target_inclusive = on # Specifies whether to stop:
# just after the specified recovery target (on)
# just before the recovery target (off)
# (change requires restart)
#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
# (change requires restart)
#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
# (change requires restart)
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Sending Servers -
# Set these on the primary and on any standby that will send replication data.
#max_wal_senders = 10 # max number of walsender processes
# (change requires restart)
#max_replication_slots = 10 # max number of replication slots
# (change requires restart)
#wal_keep_size = 0 # in megabytes; 0 disables
#max_slot_wal_keep_size = -1 # in megabytes; -1 disables
#wal_sender_timeout = 60s # in milliseconds; 0 disables
#track_commit_timestamp = off # collect timestamp of transaction commit
# (change requires restart)
# - Primary Server -
# These settings are ignored on a standby server.
#synchronous_standby_names = '' # standby servers that provide sync rep
# method to choose sync standbys, number of sync standbys,
# and comma-separated list of application_name
# from standby(s); '*' = all
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
# - Standby Servers -
# These settings are ignored on a primary server.
#primary_conninfo = '' # connection string to sending server
#primary_slot_name = '' # replication slot on sending server
#promote_trigger_file = '' # file name whose presence ends recovery
#hot_standby = on # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
# when reading WAL from archive;
# -1 allows indefinite delay
#max_standby_streaming_delay = 30s # max delay before canceling queries
# when reading streaming WAL;
# -1 allows indefinite delay
#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name
# is not set
#wal_receiver_status_interval = 10s # send replies at least this often
# 0 disables
#hot_standby_feedback = off # send info from standby to prevent
# query conflicts
#wal_receiver_timeout = 60s # time that receiver waits for
# communication from primary
# in milliseconds; 0 disables
#wal_retrieve_retry_interval = 5s # time to wait before retrying to
# retrieve WAL after a failed attempt
#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery
# - Subscribers -
# These settings are ignored on a publisher.
#max_logical_replication_workers = 4 # taken from max_worker_processes
# (change requires restart)
#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
# - Planner Method Configuration -
#enable_async_append = on
#enable_bitmapscan = on
#enable_gathermerge = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_incremental_sort = on
#enable_indexscan = on
#enable_indexonlyscan = on
#enable_material = on
#enable_memoize = on
#enable_mergejoin = on
#enable_nestloop = on
#enable_parallel_append = on
#enable_parallel_hash = on
#enable_partition_pruning = on
#enable_partitionwise_join = off
#enable_partitionwise_aggregate = off
#enable_seqscan = on
#enable_sort = on
#enable_tidscan = on
# - Planner Cost Constants -
#seq_page_cost = 1.0 # measured on an arbitrary scale
#random_page_cost = 4.0 # same scale as above
#cpu_tuple_cost = 0.01 # same scale as above
#cpu_index_tuple_cost = 0.005 # same scale as above
#cpu_operator_cost = 0.0025 # same scale as above
#parallel_setup_cost = 1000.0 # same scale as above
#parallel_tuple_cost = 0.1 # same scale as above
#min_parallel_table_scan_size = 8MB
#min_parallel_index_scan_size = 512kB
#effective_cache_size = 4GB
#jit_above_cost = 100000 # perform JIT compilation if available
# and query more expensive than this;
# -1 disables
#jit_inline_above_cost = 500000 # inline small functions if query is
# more expensive than this; -1 disables
#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if
# query is more expensive than this;
# -1 disables
# - Genetic Query Optimizer -
#geqo = on
#geqo_threshold = 12
#geqo_effort = 5 # range 1-10
#geqo_pool_size = 0 # selects default based on effort
#geqo_generations = 0 # selects default based on effort
#geqo_selection_bias = 2.0 # range 1.5-2.0
#geqo_seed = 0.0 # range 0.0-1.0
# - Other Planner Options -
#default_statistics_target = 100 # range 1-10000
#constraint_exclusion = partition # on, off, or partition
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
#from_collapse_limit = 8
#jit = on # allow JIT compilation
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
#plan_cache_mode = auto # auto, force_generic_plan or
# force_custom_plan
#recursive_worktable_factor = 10.0 # range 0.001-1000000
#------------------------------------------------------------------------------
# REPORTING AND LOGGING
#------------------------------------------------------------------------------
# - Where to Log -
#log_destination = 'stderr' # Valid values are combinations of
# stderr, csvlog, jsonlog, syslog, and
# eventlog, depending on platform.
# csvlog and jsonlog require
# logging_collector to be on.
# This is used when logging to stderr:
#logging_collector = off # Enable capturing of stderr, jsonlog,
# and csvlog into log files. Required
# to be on for csvlogs and jsonlogs.
# (change requires restart)
# These are only used if logging_collector is on:
#log_directory = 'log' # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
# can include strftime() escapes
#log_file_mode = 0600 # creation mode for log files,
# begin with 0 to use octal notation
#log_rotation_age = 1d # Automatic rotation of logfiles will
# happen after that time. 0 disables.
#log_rotation_size = 10MB # Automatic rotation of logfiles will
# happen after that much log output.
# 0 disables.
#log_truncate_on_rotation = off # If on, an existing log file with the
# same name as the new log file will be
# truncated rather than appended to.
# But such truncation only occurs on
# time-driven rotation, not on restarts
# or size-driven rotation. Default is
# off, meaning append to existing files
# in all cases.
# These are relevant when logging to syslog:
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
#syslog_sequence_numbers = on
#syslog_split_messages = on
# This is only relevant when logging to eventlog (Windows):
# (change requires restart)
#event_source = 'PostgreSQL'
# - When to Log -
#log_min_messages = warning # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic
#log_min_error_statement = error # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic (effectively off)
#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
# and their durations, > 0 logs only
# statements running at least this number
# of milliseconds
#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements
# and their durations, > 0 logs only a sample of
# statements running at least this number
# of milliseconds;
# sample fraction is determined by log_statement_sample_rate
#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding
# log_min_duration_sample to be logged;
# 1.0 logs all such statements, 0.0 never logs
#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements
# are logged regardless of their duration; 1.0 logs all
# statements from all transactions, 0.0 never logs
#log_startup_progress_interval = 10s # Time between progress updates for
# long-running startup operations.
# 0 disables the feature, > 0 indicates
# the interval in milliseconds.
# - What to Log -
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_autovacuum_min_duration = 10min # log autovacuum activity;
# -1 disables, 0 logs all actions and
# their durations, > 0 logs only
# actions running at least this number
# of milliseconds.
#log_checkpoints = on
#log_connections = off
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default # terse, default, or verbose messages
#log_hostname = off
log_line_prefix = '%m [%p] %q%u@%d ' # special values:
# %a = application name
# %u = user name
# %d = database name
# %r = remote host and port
# %h = remote host
# %b = backend type
# %p = process ID
# %P = process ID of parallel group leader
# %t = timestamp without milliseconds
# %m = timestamp with milliseconds
# %n = timestamp with milliseconds (as a Unix epoch)
# %Q = query ID (0 if none or not computed)
# %i = command tag
# %e = SQL state
# %c = session ID
# %l = session line number
# %s = session start timestamp
# %v = virtual transaction ID
# %x = transaction ID (0 if none)
# %q = stop here in non-session
# processes
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
#log_recovery_conflict_waits = off # log standby recovery conflict waits
# >= deadlock_timeout
#log_parameter_max_length = -1 # when logging statements, limit logged
# bind-parameter values to N bytes;
# -1 means print in full, 0 disables
#log_parameter_max_length_on_error = 0 # when logging an error, limit logged
# bind-parameter values to N bytes;
# -1 means print in full, 0 disables
#log_statement = 'none' # none, ddl, mod, all
#log_replication_commands = off
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
# -1 disables, 0 logs all temp files
log_timezone = 'Etc/UTC'
#------------------------------------------------------------------------------
# PROCESS TITLE
#------------------------------------------------------------------------------
cluster_name = '15/main' # added to process titles if nonempty
# (change requires restart)
#update_process_title = on
#------------------------------------------------------------------------------
# STATISTICS
#------------------------------------------------------------------------------
# - Cumulative Query and Index Statistics -
#track_activities = on
#track_activity_query_size = 1024 # (change requires restart)
#track_counts = on
#track_io_timing = off
#track_wal_io_timing = off
#track_functions = none # none, pl, all
#stats_fetch_consistency = cache
# - Monitoring -
#compute_query_id = auto
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
#------------------------------------------------------------------------------
# AUTOVACUUM
#------------------------------------------------------------------------------
#autovacuum = on # Enable autovacuum subprocess? 'on'
# requires track_counts to also be on.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs
#autovacuum_vacuum_threshold = 50 # min number of row updates before
# vacuum
#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts
# before vacuum; -1 disables insert
# vacuums
#autovacuum_analyze_threshold = 50 # min number of row updates before
# analyze
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
# size before insert vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
# - Statement Behavior -
#client_min_messages = notice # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# log
# notice
# warning
# error
#search_path = '"$user", public' # schema names
#row_security = on
#default_table_access_method = 'heap'
#default_tablespace = '' # a tablespace name, '' uses the default
#default_toast_compression = 'pglz' # 'pglz' or 'lz4'
#temp_tablespaces = '' # a list of tablespace names, '' uses
# only default tablespace
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'
#statement_timeout = 0 # in milliseconds, 0 is disabled
#lock_timeout = 0 # in milliseconds, 0 is disabled
#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
#idle_session_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_table_age = 150000000
#vacuum_freeze_min_age = 50000000
#vacuum_failsafe_age = 1600000000
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_failsafe_age = 1600000000
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_pending_list_limit = 4MB
# - Locale and Formatting -
datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
timezone = 'Etc/UTC'
#timezone_abbreviations = 'Default' # Select the set of available time zone
# abbreviations. Currently, there are
# Default
# Australia (historical usage)
# India
# You can create your own file in
# share/timezonesets/.
#extra_float_digits = 1 # min -15, max 3; any value >0 actually
# selects precise output mode
#client_encoding = sql_ascii # actually, defaults to database
# encoding
# These settings are initialized by initdb, but they can be changed.
lc_messages = 'C.UTF-8' # locale for system error message
# strings
lc_monetary = 'C.UTF-8' # locale for monetary formatting
lc_numeric = 'C.UTF-8' # locale for number formatting
lc_time = 'C.UTF-8' # locale for time formatting
# default configuration for text search
default_text_search_config = 'pg_catalog.english'
# - Shared Library Preloading -
#local_preload_libraries = ''
#session_preload_libraries = ''
#shared_preload_libraries = '' # (change requires restart)
#jit_provider = 'llvmjit' # JIT library to use
# - Other Defaults -
#dynamic_library_path = '$libdir'
#extension_destdir = '' # prepend path when loading extensions
# and shared objects (added by Debian)
#gin_fuzzy_search_limit = 0
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
#max_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_relation = -2 # negative values mean
# (max_pred_locks_per_transaction
# / -max_pred_locks_per_relation) - 1
#max_pred_locks_per_page = 2 # min 0
#------------------------------------------------------------------------------
# VERSION AND PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
# - Previous PostgreSQL Versions -
#array_nulls = on
#backslash_quote = safe_encoding # on, off, or safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
# - Other Platforms and Clients -
#transform_null_equals = off
#------------------------------------------------------------------------------
# ERROR HANDLING
#------------------------------------------------------------------------------
#exit_on_error = off # terminate session on any error?
#restart_after_crash = on # reinitialize after backend crash?
#data_sync_retry = off # retry or panic on failure to fsync
# data?
# (change requires restart)
#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+)
#------------------------------------------------------------------------------
# CONFIG FILE INCLUDES
#------------------------------------------------------------------------------
# These options allow settings to be loaded from files other than the
# default postgresql.conf. Note that these are directives, not variable
# assignments, so they can usefully be given more than once.
# include_dir = 'conf.d' # include files ending in '.conf' from
# a directory, e.g., 'conf.d'
#include_if_exists = '...' # include file only if it exists
#include = '...' # include file
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# Add settings for extensions here
-71
View File
@@ -1,71 +0,0 @@
[program:web]
command=node /app/web/web/server.js
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
environment=PORT=3001,HOSTNAME=0.0.0.0
[program:space]
command=node /app/space/space/server.js
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
environment=PORT=3002,HOSTNAME=0.0.0.0
[program:admin]
command=node /app/admin/admin/server.js
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
environment=PORT=3003,HOSTNAME=0.0.0.0
[program:migrator]
directory=/app/api
command=sh -c "./bin/docker-entrypoint-migrator.sh"
autostart=true
autorestart=false
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
[program:api]
directory=/app/api
command=sh -c "./bin/docker-entrypoint-api.sh"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
[program:worker]
directory=/app/api
command=sh -c "./bin/docker-entrypoint-worker.sh"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
[program:beat]
directory=/app/api
command=sh -c "./bin/docker-entrypoint-beat.sh"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stdout
stderr_logfile_maxbytes=0
-38
View File
@@ -1,38 +0,0 @@
[supervisord]
user=root
nodaemon=true
stderr_logfile=/app/logs/error/supervisor.err.log
stdout_logfile=/app/logs/access/supervisor.log
[program:redis]
directory=/app/data/redis
command=redis-server
autostart=true
autorestart=true
stderr_logfile=/app/logs/error/redis.err.log
stdout_logfile=/app/logs/access/redis.log
[program:postgresql]
user=postgres
command=/usr/lib/postgresql/15/bin/postgres --config-file=/etc/postgresql/postgresql.conf
autostart=true
autorestart=true
stderr_logfile=/app/logs/error/postgresql.err.log
stdout_logfile=/app/logs/access/postgresql.log
[program:minio]
directory=/app/data/minio
command=minio server /app/data/minio
autostart=true
autorestart=true
stderr_logfile=/app/logs/error/minio.err.log
stdout_logfile=/app/logs/access/minio.log
[program:nginx]
directory=/app/data/nginx
command=/app/nginx-start.sh
autostart=true
autorestart=true
stderr_logfile=/app/logs/error/nginx.err.log
stdout_logfile=/app/logs/access/nginx.log
-14
View File
@@ -1,14 +0,0 @@
[supervisord]
user=root
nodaemon=true
stderr_logfile=/app/logs/error/supervisor.err.log
stdout_logfile=/app/logs/access/supervisor.log
[program:nginx]
directory=/app/data/nginx
command=/app/nginx-start.sh
autostart=true
autorestart=true
stderr_logfile=/app/logs/error/nginx.err.log
stdout_logfile=/app/logs/access/nginx.log
+12
View File
@@ -0,0 +1,12 @@
.next/*
out/*
public/*
dist/*
node_modules/*
.turbo/*
.env*
.env
.env.local
.env.development
.env.production
.env.test
-1
View File
@@ -1,5 +1,4 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
};
+2 -2
View File
@@ -2,5 +2,5 @@
.vercel
.tubro
out/
dis/
build/
dist/
build/
+14 -5
View File
@@ -1,5 +1,11 @@
# syntax=docker/dockerfile:1.7
FROM node:22-alpine AS base
# Setup pnpm package manager with corepack and configure global bin directory for caching
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# *****************************************************************************
# STAGE 1: Build the project
# *****************************************************************************
@@ -7,7 +13,8 @@ FROM base AS builder
RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN yarn global add turbo
ARG TURBO_VERSION=2.5.6
RUN corepack enable pnpm && pnpm add -g turbo@${TURBO_VERSION}
COPY . .
RUN turbo prune --scope=admin --docker
@@ -22,11 +29,13 @@ WORKDIR /app
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install --network-timeout 500000
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm fetch --store-dir=/pnpm/store
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install --offline --frozen-lockfile --store-dir=/pnpm/store
ARG NEXT_PUBLIC_API_BASE_URL=""
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
@@ -49,7 +58,7 @@ ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV TURBO_TELEMETRY_DISABLED=1
RUN yarn turbo run build --filter=admin
RUN pnpm turbo run build --filter=admin
# *****************************************************************************
# STAGE 3: Copy the project and start it
@@ -91,4 +100,4 @@ ENV TURBO_TELEMETRY_DISABLED=1
EXPOSE 3000
CMD ["node", "apps/admin/server.js"]
CMD ["node", "apps/admin/server.js"]
+3 -3
View File
@@ -5,8 +5,8 @@ WORKDIR /app
COPY . .
RUN yarn global add turbo
RUN yarn install
RUN corepack enable pnpm && pnpm add -g turbo
RUN pnpm install
ENV NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
@@ -14,4 +14,4 @@ EXPOSE 3000
VOLUME [ "/app/node_modules", "/app/admin/node_modules" ]
CMD ["yarn", "dev", "--filter=admin"]
CMD ["pnpm", "dev", "--filter=admin"]
@@ -66,9 +66,11 @@ const InstanceGitlabAuthenticationPage = observer(() => {
<ToggleSwitch
value={Boolean(parseInt(enableGitlabConfig))}
onChange={() => {
Boolean(parseInt(enableGitlabConfig)) === true
? updateConfig("IS_GITLAB_ENABLED", "0")
: updateConfig("IS_GITLAB_ENABLED", "1");
if (Boolean(parseInt(enableGitlabConfig)) === true) {
updateConfig("IS_GITLAB_ENABLED", "0");
} else {
updateConfig("IS_GITLAB_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
@@ -67,9 +67,11 @@ const InstanceGoogleAuthenticationPage = observer(() => {
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
Boolean(parseInt(enableGoogleConfig)) === true
? updateConfig("IS_GOOGLE_ENABLED", "0")
: updateConfig("IS_GOOGLE_ENABLED", "1");
if (Boolean(parseInt(enableGoogleConfig)) === true) {
updateConfig("IS_GOOGLE_ENABLED", "0");
} else {
updateConfig("IS_GOOGLE_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
@@ -49,9 +49,9 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
EMAIL_USE_TLS: config["EMAIL_USE_TLS"],
EMAIL_USE_SSL: config["EMAIL_USE_SSL"],
EMAIL_FROM: config["EMAIL_FROM"],
ENABLE_SMTP: config["ENABLE_SMTP"],
},
});
const emailFormFields: TControllerInputFormField[] = [
{
key: "EMAIL_HOST",
@@ -101,7 +101,7 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
];
const onSubmit = async (formData: EmailFormValues) => {
const payload: Partial<EmailFormValues> = { ...formData };
const payload: Partial<EmailFormValues> = { ...formData, ENABLE_SMTP: "1" };
await updateInstanceConfigurations(payload)
.then(() =>
+66 -21
View File
@@ -1,45 +1,90 @@
"use client";
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
import { Loader } from "@plane/ui";
import { Loader, setToast, TOAST_TYPE, ToggleSwitch } from "@plane/ui";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { InstanceEmailForm } from "./email-config-form";
const InstanceEmailPage = observer(() => {
const InstanceEmailPage: React.FC = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig } = useInstance();
const { fetchInstanceConfigurations, formattedConfig, disableEmail } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const { isLoading } = useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSMTPEnabled, setIsSMTPEnabled] = useState(false);
const handleToggle = async () => {
if (isSMTPEnabled) {
setIsSubmitting(true);
try {
await disableEmail();
setIsSMTPEnabled(false);
setToast({
title: "Email feature disabled",
message: "Email feature has been disabled",
type: TOAST_TYPE.SUCCESS,
});
} catch (_error) {
setToast({
title: "Error disabling email",
message: "Failed to disable email feature. Please try again.",
type: TOAST_TYPE.ERROR,
});
} finally {
setIsSubmitting(false);
}
return;
}
setIsSMTPEnabled(true);
};
useEffect(() => {
if (formattedConfig) {
setIsSMTPEnabled(formattedConfig.ENABLE_SMTP === "1");
}
}, [formattedConfig]);
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
<div className="text-sm font-normal text-custom-text-300">
Plane can send useful emails to you and your users from your own instance without talking to the Internet.
<div className="flex items-center justify-between gap-4 border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
<div className="text-sm font-normal text-custom-text-300">
Set it up below and please test your settings before you save them.&nbsp;
<span className="text-red-400">Misconfigs can lead to email bounces and errors.</span>
Plane can send useful emails to you and your users from your own instance without talking to the Internet.
<div className="text-sm font-normal text-custom-text-300">
Set it up below and please test your settings before you save them.&nbsp;
<span className="text-red-400">Misconfigs can lead to email bounces and errors.</span>
</div>
</div>
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceEmailForm config={formattedConfig} />
) : (
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
{isLoading ? (
<Loader>
<Loader.Item width="24px" height="16px" className="rounded-full" />
</Loader>
) : (
<ToggleSwitch value={isSMTPEnabled} onChange={handleToggle} size="sm" disabled={isSubmitting} />
)}
</div>
{isSMTPEnabled && !isLoading && (
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceEmailForm config={formattedConfig} />
) : (
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</div>
)}
</div>
</>
);
+1 -1
View File
@@ -72,7 +72,7 @@ export const AdminHeader: FC = observer(() => {
const breadcrumbItems = generateBreadcrumbItems(pathName);
return (
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-sidebar-border-200 bg-custom-sidebar-background-100 p-4">
<div className="relative z-10 flex h-header w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-sidebar-border-200 bg-custom-sidebar-background-100 p-4">
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
<HamburgerToggle />
{breadcrumbItems.length >= 0 && (
@@ -42,7 +42,7 @@ export const AdminSidebarDropdown = observer(() => {
)}
>
<div className="flex flex-col gap-2.5 pb-2">
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
<span className="px-2 text-custom-sidebar-text-200 truncate">{currentUser?.email}</span>
</div>
<div className="py-2">
<Menu.Item
@@ -77,7 +77,7 @@ export const AdminSidebarDropdown = observer(() => {
}, [csrfToken]);
return (
<div className="flex max-h-[3.75rem] items-center gap-x-5 gap-y-2 border-b border-custom-sidebar-border-200 px-4 py-3.5">
<div className="flex max-h-header items-center gap-x-5 gap-y-2 border-b border-custom-sidebar-border-200 px-4 py-3.5">
<div className="h-full w-full truncate">
<div
className={`flex flex-grow items-center gap-x-2 truncate rounded py-1 ${
@@ -7,7 +7,8 @@ import { ExternalLink, FileText, HelpCircle, MoveLeft } from "lucide-react";
import { Transition } from "@headlessui/react";
// plane internal packages
import { WEB_BASE_URL } from "@plane/constants";
import { DiscordIcon, GithubIcon, Tooltip } from "@plane/ui";
import { Tooltip } from "@plane/propel/tooltip";
import { DiscordIcon, GithubIcon } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks
import { useTheme } from "@/hooks/store";
@@ -5,7 +5,8 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { Image, BrainCog, Cog, Lock, Mail } from "lucide-react";
// plane internal packages
import { Tooltip, WorkspaceIcon } from "@plane/ui";
import { Tooltip } from "@plane/propel/tooltip";
import { WorkspaceIcon } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks
import { useTheme } from "@/hooks/store";
@@ -0,0 +1,12 @@
"use client";
import Link from "next/link";
import { PlaneLockup } from "@plane/ui";
export const AuthHeader = () => (
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
<Link href="/">
<PlaneLockup height={20} width={95} className="text-custom-text-100" />
</Link>
</div>
);
+2 -28
View File
@@ -1,35 +1,9 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useTheme } from "next-themes";
// logo assets
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.png";
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.png";
export default function RootLayout({ children }: { children: React.ReactNode }) {
const { resolvedTheme } = useTheme();
const patternBackground = resolvedTheme === "light" ? PlaneBackgroundPattern : PlaneBackgroundPatternDark;
const logo = resolvedTheme === "light" ? BlackHorizontalLogo : WhiteHorizontalLogo;
return (
<div className="relative">
<div className="h-screen w-full overflow-hidden overflow-y-auto flex flex-col">
<div className="container h-[110px] flex-shrink-0 mx-auto px-5 lg:px-0 flex items-center justify-between gap-5 z-50">
<div className="flex items-center gap-x-2 py-10">
<Link href={`/`} className="h-[30px] w-[133px]">
<Image src={logo} alt="Plane logo" />
</Link>
</div>
</div>
<div className="absolute inset-0 z-0">
<Image src={patternBackground} className="w-screen h-full object-cover" alt="Plane background pattern" />
</div>
<div className="relative z-10 flex-grow">{children}</div>
</div>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
{children}
</div>
);
}
+6 -28
View File
@@ -2,8 +2,8 @@
import { observer } from "mobx-react";
// components
import { LogoSpinner } from "@/components/common/logo-spinner";
import { InstanceFailureView } from "@/components/instance/failure";
import { InstanceLoading } from "@/components/instance/loading";
import { InstanceSetupForm } from "@/components/instance/setup-form";
// hooks
import { useInstance } from "@/hooks/store";
@@ -17,46 +17,24 @@ const HomePage = () => {
// if instance is not fetched, show loading
if (!instance && !error) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceLoading />
<div className="flex items-center justify-center h-screen w-full">
<LogoSpinner />
</div>
);
}
// if instance fetch fails, show failure view
if (error) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceFailureView />
</div>
);
return <InstanceFailureView />;
}
// if instance is fetched and setup is not done, show setup form
if (instance && !instance?.is_setup_done) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceSetupForm />
</div>
);
return <InstanceSetupForm />;
}
// if instance is fetched and setup is done, show sign in form
return (
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Manage your Plane instance
</h3>
<p className="font-medium text-onboarding-text-400">
Configure instance-wide settings to secure your instance
</p>
</div>
<InstanceSignInForm />
</div>
</div>
);
return <InstanceSignInForm />;
};
export default observer(HomePage);
+85 -70
View File
@@ -10,7 +10,9 @@ import { Button, Input, Spinner } from "@plane/ui";
// components
import { Banner } from "@/components/common/banner";
// local components
import { FormHeader } from "../../../core/components/instance/form-header";
import { AuthBanner } from "./auth-banner";
import { AuthHeader } from "./auth-header";
import { authErrorHandler } from "./auth-helpers";
// service initialization
@@ -101,78 +103,91 @@ export const InstanceSignInForm: FC = () => {
}, [errorCode]);
return (
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
{errorData.type && errorData?.message ? (
<Banner type="error" message={errorData?.message} />
) : (
<>{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}</>
)}
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword ? "text" : "password"}
inputSize="md"
placeholder="Enter your password"
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
autoComplete="on"
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<FormHeader
heading="Manage your Plane instance"
subHeading="Configure instance-wide settings to secure your instance"
/>
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
{errorData.type && errorData?.message ? (
<Banner type="error" message={errorData?.message} />
) : (
<>
{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}
</>
)}
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="password"
name="password"
type={showPassword ? "text" : "password"}
inputSize="md"
placeholder="Enter your password"
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
autoComplete="on"
/>
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
</form>
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
</form>
</>
);
};
@@ -7,8 +7,8 @@ import { cn } from "@plane/utils";
type Props = {
name: string;
description: string;
icon: JSX.Element;
config: JSX.Element;
icon: React.ReactNode;
config: React.ReactNode;
disabled?: boolean;
withBorder?: boolean;
unavailable?: boolean;
@@ -25,9 +25,8 @@ export const EmailCodesConfiguration: React.FC<Props> = observer((props) => {
<ToggleSwitch
value={Boolean(parseInt(enableMagicLogin))}
onChange={() => {
Boolean(parseInt(enableMagicLogin)) === true
? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0")
: updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1");
const newEnableMagicLogin = Boolean(parseInt(enableMagicLogin)) === true ? "0" : "1";
updateConfig("ENABLE_MAGIC_LINK_LOGIN", newEnableMagicLogin);
}}
size="sm"
disabled={disabled}
@@ -35,9 +35,8 @@ export const GithubConfiguration: React.FC<Props> = observer((props) => {
<ToggleSwitch
value={Boolean(parseInt(enableGithubConfig))}
onChange={() => {
Boolean(parseInt(enableGithubConfig)) === true
? updateConfig("IS_GITHUB_ENABLED", "0")
: updateConfig("IS_GITHUB_ENABLED", "1");
const newEnableGithubConfig = Boolean(parseInt(enableGithubConfig)) === true ? "0" : "1";
updateConfig("IS_GITHUB_ENABLED", newEnableGithubConfig);
}}
size="sm"
disabled={disabled}
@@ -35,9 +35,8 @@ export const GitlabConfiguration: React.FC<Props> = observer((props) => {
<ToggleSwitch
value={Boolean(parseInt(enableGitlabConfig))}
onChange={() => {
Boolean(parseInt(enableGitlabConfig)) === true
? updateConfig("IS_GITLAB_ENABLED", "0")
: updateConfig("IS_GITLAB_ENABLED", "1");
const newEnableGitlabConfig = Boolean(parseInt(enableGitlabConfig)) === true ? "0" : "1";
updateConfig("IS_GITLAB_ENABLED", newEnableGitlabConfig);
}}
size="sm"
disabled={disabled}
@@ -35,9 +35,8 @@ export const GoogleConfiguration: React.FC<Props> = observer((props) => {
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
Boolean(parseInt(enableGoogleConfig)) === true
? updateConfig("IS_GOOGLE_ENABLED", "0")
: updateConfig("IS_GOOGLE_ENABLED", "1");
const newEnableGoogleConfig = Boolean(parseInt(enableGoogleConfig)) === true ? "0" : "1";
updateConfig("IS_GOOGLE_ENABLED", newEnableGoogleConfig);
}}
size="sm"
disabled={disabled}
@@ -25,9 +25,8 @@ export const PasswordLoginConfiguration: React.FC<Props> = observer((props) => {
<ToggleSwitch
value={Boolean(parseInt(enableEmailPassword))}
onChange={() => {
Boolean(parseInt(enableEmailPassword)) === true
? updateConfig("ENABLE_EMAIL_PASSWORD", "0")
: updateConfig("ENABLE_EMAIL_PASSWORD", "1");
const newEnableEmailPassword = Boolean(parseInt(enableEmailPassword)) === true ? "0" : "1";
updateConfig("ENABLE_EMAIL_PASSWORD", newEnableEmailPassword);
}}
size="sm"
disabled={disabled}
@@ -1,7 +1,7 @@
"use client";
import Link from "next/link";
import { Tooltip } from "@plane/ui";
import { Tooltip } from "@plane/propel/tooltip";
type Props = {
label?: string;
@@ -13,7 +13,7 @@ type Props = {
type: "text" | "password";
name: string;
label: string;
description?: string | JSX.Element;
description?: string | React.ReactNode;
placeholder: string;
error: boolean;
required: boolean;
@@ -23,7 +23,7 @@ export type TControllerInputFormField = {
key: string;
type: "text" | "password";
label: string;
description?: string | JSX.Element;
description?: string | React.ReactNode;
placeholder: string;
error: boolean;
required: boolean;
@@ -9,14 +9,14 @@ import { Button, TOAST_TYPE, setToast } from "@plane/ui";
type Props = {
label: string;
url: string;
description: string | JSX.Element;
description: string | React.ReactNode;
};
export type TCopyField = {
key: string;
label: string;
url: string;
description: string | JSX.Element;
description: string | React.ReactNode;
};
export const CopyField: React.FC<Props> = (props) => {
@@ -7,11 +7,11 @@ import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const LogoSpinner = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="w-[82px] h-[82px] mr-2" priority={false} />
<Image src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11" />
</div>
);
};
@@ -1,89 +0,0 @@
"use client";
import { FC, useMemo } from "react";
// plane internal packages
import { E_PASSWORD_STRENGTH } from "@plane/constants";
import { cn, getPasswordStrength } from "@plane/utils";
type TPasswordStrengthMeter = {
password: string;
isFocused?: boolean;
};
export const PasswordStrengthMeter: FC<TPasswordStrengthMeter> = (props) => {
const { password, isFocused = false } = props;
// derived values
const strength = useMemo(() => getPasswordStrength(password), [password]);
const strengthBars = useMemo(() => {
switch (strength) {
case E_PASSWORD_STRENGTH.EMPTY: {
return {
bars: [`bg-custom-text-100`, `bg-custom-text-100`, `bg-custom-text-100`],
text: "Please enter your password.",
textColor: "text-custom-text-100",
};
}
case E_PASSWORD_STRENGTH.LENGTH_NOT_VALID: {
return {
bars: [`bg-red-500`, `bg-custom-text-100`, `bg-custom-text-100`],
text: "Password length should me more than 8 characters.",
textColor: "text-red-500",
};
}
case E_PASSWORD_STRENGTH.STRENGTH_NOT_VALID: {
return {
bars: [`bg-red-500`, `bg-custom-text-100`, `bg-custom-text-100`],
text: "Password is weak.",
textColor: "text-red-500",
};
}
case E_PASSWORD_STRENGTH.STRENGTH_VALID: {
return {
bars: [`bg-green-500`, `bg-green-500`, `bg-green-500`],
text: "Password is strong.",
textColor: "text-green-500",
};
}
default: {
return {
bars: [`bg-custom-text-100`, `bg-custom-text-100`, `bg-custom-text-100`],
text: "Please enter your password.",
textColor: "text-custom-text-100",
};
}
}
}, [strength]);
const isPasswordMeterVisible = isFocused ? true : strength === E_PASSWORD_STRENGTH.STRENGTH_VALID ? false : true;
if (!isPasswordMeterVisible) return <></>;
return (
<div className="w-full space-y-2 pt-2">
<div className="space-y-1.5">
<div className="relative flex items-center gap-2">
{strengthBars?.bars.map((color, index) => (
<div key={`${color}-${index}`} className={cn("w-full h-1 rounded-full", color)} />
))}
</div>
<div className={cn(`text-xs font-medium text-custom-text-100`, strengthBars?.textColor)}>
{strengthBars?.text}
</div>
</div>
{/* <div className="relative flex flex-wrap gap-x-4 gap-y-2">
{PASSWORD_CRITERIA.map((criteria) => (
<div
key={criteria.key}
className={cn(
"relative flex items-center gap-1 text-xs",
criteria.isCriteriaValid(password) ? `text-green-500/70` : "text-custom-text-300"
)}
>
<CircleCheck width={14} height={14} />
{criteria.label}
</div>
))}
</div> */}
</div>
);
};
+21 -17
View File
@@ -1,13 +1,15 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react";
import Image from "next/image";
import { useTheme } from "next-themes";
import { Button } from "@plane/ui";
// assets
import { AuthHeader } from "@/app/(all)/(home)/auth-header";
import InstanceFailureDarkImage from "@/public/instance/instance-failure-dark.svg";
import InstanceFailureImage from "@/public/instance/instance-failure.svg";
export const InstanceFailureView: FC = () => {
export const InstanceFailureView: FC = observer(() => {
const { resolvedTheme } = useTheme();
const instanceImage = resolvedTheme === "dark" ? InstanceFailureDarkImage : InstanceFailureImage;
@@ -17,22 +19,24 @@ export const InstanceFailureView: FC = () => {
};
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white ">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. <br />
Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={handleRetry}>
Retry
</Button>
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white text-center">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={handleRetry}>
Retry
</Button>
</div>
</div>
</div>
</div>
</>
);
};
});
@@ -0,0 +1,8 @@
"use client";
export const FormHeader = ({ heading, subHeading }: { heading: string; subHeading: string }) => (
<div className="flex flex-col gap-1">
<span className="text-2xl font-semibold text-custom-text-100 leading-7">{heading}</span>
<span className="text-lg font-semibold text-custom-text-400 leading-7">{subHeading}</span>
</div>
);
@@ -13,7 +13,7 @@ export const InstanceNotReady: FC = () => (
<div className="relative flex flex-col justify-center items-center space-y-4">
<h1 className="text-3xl font-bold pb-3">Welcome aboard Plane!</h1>
<Image src={PlaneTakeOffImage} alt="Plane Logo" />
<p className="font-medium text-base text-onboarding-text-400">
<p className="font-medium text-base text-custom-text-400">
Get started by setting up your instance and workspace
</p>
</div>
@@ -6,16 +6,12 @@ import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const InstanceLoading = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={logoSrc} alt="logo" className="w-[82px] h-[82px] mr-2" priority={false} />
<h3 className="font-medium text-2xl text-white ">Fetching instance details...</h3>
</div>
</div>
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11" />
</div>
);
};
+217 -222
View File
@@ -7,11 +7,12 @@ import { Eye, EyeOff } from "lucide-react";
// plane internal packages
import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants";
import { AuthService } from "@plane/services";
import { Button, Checkbox, Input, Spinner } from "@plane/ui";
import { Button, Checkbox, Input, PasswordStrengthIndicator, Spinner } from "@plane/ui";
import { getPasswordStrength } from "@plane/utils";
// components
import { AuthHeader } from "@/app/(all)/(home)/auth-header";
import { Banner } from "@/components/common/banner";
import { PasswordStrengthMeter } from "@/components/common/password-strength-meter";
import { FormHeader } from "@/components/instance/form-header";
// service initialization
const authService = new AuthService();
@@ -132,227 +133,221 @@ export const InstanceSetupForm: FC = (props) => {
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
return (
<div className="max-w-lg lg:max-w-md w-full">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Setup your Plane Instance
</h3>
<p className="font-medium text-onboarding-text-400">
Post setup you will be able to manage this Plane instance.
</p>
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<FormHeader
heading="Setup your Plane Instance"
subHeading="Post setup you will be able to manage this Plane instance."
/>
{errorData.type &&
errorData?.message &&
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
<Banner type="error" message={errorData?.message} />
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="first_name">
First name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="first_name"
name="first_name"
type="text"
inputSize="md"
placeholder="Wilber"
value={formData.first_name}
onChange={(e) => handleFormChange("first_name", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="last_name">
Last name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="last_name"
name="last_name"
type="text"
inputSize="md"
placeholder="Wright"
value={formData.last_name}
onChange={(e) => handleFormChange("last_name", e.target.value)}
autoComplete="on"
/>
</div>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false}
autoComplete="on"
/>
{errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="company_name">
Company name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="company_name"
name="company_name"
type="text"
inputSize="md"
placeholder="Company name"
value={formData.company_name}
onChange={(e) => handleFormChange("company_name", e.target.value)}
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
Set a password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="password"
name="password"
type={showPassword.password ? "text" : "password"}
inputSize="md"
placeholder="New password..."
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
/>
{showPassword.password ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
<PasswordStrengthIndicator password={formData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="confirm_password">
Confirm password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
type={showPassword.retypePassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
inputSize="md"
value={formData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder="Confirm password"
className="w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>
{showPassword.retypePassword ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{!!formData.confirm_password &&
formData.password !== formData.confirm_password &&
renderPasswordMatchError && <span className="text-sm text-red-500">Passwords don{"'"}t match</span>}
</div>
<div className="relative flex gap-2">
<div>
<Checkbox
className="w-4 h-4"
iconClassName="w-3 h-3"
id="is_telemetry_enabled"
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled}
/>
</div>
<label className="text-sm text-custom-text-300 font-medium cursor-pointer" htmlFor="is_telemetry_enabled">
Allow Plane to anonymously collect usage events.{" "}
<a
tabIndex={-1}
href="https://developers.plane.so/self-hosting/telemetry"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-blue-500 hover:text-blue-600 flex-shrink-0"
>
See More
</a>
</label>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</div>
</form>
</div>
{errorData.type &&
errorData?.message &&
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
<Banner type="error" message={errorData?.message} />
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="first_name">
First name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="first_name"
name="first_name"
type="text"
inputSize="md"
placeholder="Wilber"
value={formData.first_name}
onChange={(e) => handleFormChange("first_name", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="last_name">
Last name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="last_name"
name="last_name"
type="text"
inputSize="md"
placeholder="Wright"
value={formData.last_name}
onChange={(e) => handleFormChange("last_name", e.target.value)}
autoComplete="on"
/>
</div>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false}
autoComplete="on"
/>
{errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="company_name">
Company name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="company_name"
name="company_name"
type="text"
inputSize="md"
placeholder="Company name"
value={formData.company_name}
onChange={(e) => handleFormChange("company_name", e.target.value)}
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Set a password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword.password ? "text" : "password"}
inputSize="md"
placeholder="New password..."
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
/>
{showPassword.password ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
<PasswordStrengthMeter password={formData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
Confirm password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
type={showPassword.retypePassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
inputSize="md"
value={formData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder="Confirm password"
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>
{showPassword.retypePassword ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{!!formData.confirm_password &&
formData.password !== formData.confirm_password &&
renderPasswordMatchError && <span className="text-sm text-red-500">Passwords don{"'"}t match</span>}
</div>
<div className="relative flex items-center pt-2 gap-2">
<div>
<Checkbox
className="w-4 h-4"
iconClassName="w-3 h-3"
id="is_telemetry_enabled"
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled}
/>
</div>
<label
className="text-sm text-onboarding-text-300 font-medium cursor-pointer"
htmlFor="is_telemetry_enabled"
>
Allow Plane to anonymously collect usage events.
</label>
<a
tabIndex={-1}
href="https://developers.plane.so/self-hosting/telemetry"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-blue-500 hover:text-blue-600"
>
See More
</a>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</div>
</form>
</div>
</div>
</>
);
};
@@ -2,7 +2,7 @@ import { observer } from "mobx-react";
import { ExternalLink } from "lucide-react";
// plane internal packages
import { WEB_BASE_URL } from "@plane/constants";
import { Tooltip } from "@plane/ui";
import { Tooltip } from "@plane/propel/tooltip";
import { getFileURL } from "@plane/utils";
// hooks
import { useWorkspace } from "@/hooks/store";
+27
View File
@@ -32,6 +32,7 @@ export interface IInstanceStore {
fetchInstanceAdmins: () => Promise<IInstanceAdmin[] | undefined>;
fetchInstanceConfigurations: () => Promise<IInstanceConfiguration[] | undefined>;
updateInstanceConfigurations: (data: Partial<IFormattedInstanceConfiguration>) => Promise<IInstanceConfiguration[]>;
disableEmail: () => Promise<void>;
}
export class InstanceStore implements IInstanceStore {
@@ -187,4 +188,30 @@ export class InstanceStore implements IInstanceStore {
throw error;
}
};
disableEmail = async () => {
const instanceConfigurations = this.instanceConfigurations;
try {
runInAction(() => {
this.instanceConfigurations = this.instanceConfigurations?.map((config) => {
if (
[
"EMAIL_HOST",
"EMAIL_PORT",
"EMAIL_HOST_USER",
"EMAIL_HOST_PASSWORD",
"EMAIL_FROM",
"ENABLE_SMTP",
].includes(config.key)
)
return { ...config, value: "" };
return config;
});
});
await this.instanceService.disableEmail();
} catch (_error) {
console.error("Error disabling the email");
this.instanceConfigurations = instanceConfigurations;
}
};
}
+27 -29
View File
@@ -1,7 +1,7 @@
{
"name": "admin",
"description": "Admin UI for Plane",
"version": "0.27.1",
"version": "1.0.0",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@@ -10,7 +10,7 @@
"preview": "next build && next start",
"start": "next start",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist",
"check:lint": "eslint . --max-warnings 0",
"check:lint": "eslint . --max-warnings 19",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
@@ -18,40 +18,38 @@
},
"dependencies": {
"@headlessui/react": "^1.7.19",
"@plane/constants": "*",
"@plane/hooks": "*",
"@plane/propel": "*",
"@plane/services": "*",
"@plane/types": "*",
"@plane/ui": "*",
"@plane/utils": "*",
"@tailwindcss/typography": "^0.5.9",
"@types/lodash": "^4.17.0",
"@plane/constants": "workspace:*",
"@plane/hooks": "workspace:*",
"@plane/propel": "workspace:*",
"@plane/services": "workspace:*",
"@plane/types": "workspace:*",
"@plane/ui": "workspace:*",
"@plane/utils": "workspace:*",
"autoprefixer": "10.4.14",
"axios": "^1.8.3",
"lodash": "^4.17.21",
"lucide-react": "^0.469.0",
"mobx": "^6.12.0",
"mobx-react": "^9.1.1",
"next": "14.2.30",
"axios": "catalog:",
"lodash": "catalog:",
"lucide-react": "catalog:",
"mobx": "catalog:",
"mobx-react": "catalog:",
"next": "catalog:",
"next-themes": "^0.2.1",
"postcss": "^8.4.49",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "catalog:",
"react-dom": "catalog:",
"react-hook-form": "7.51.5",
"swr": "^2.2.4",
"uuid": "^9.0.1",
"zxcvbn": "^4.4.2"
"sharp": "catalog:",
"swr": "catalog:",
"uuid": "catalog:"
},
"devDependencies": {
"@plane/eslint-config": "*",
"@plane/tailwind-config": "*",
"@plane/typescript-config": "*",
"@plane/eslint-config": "workspace:*",
"@plane/tailwind-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
"@types/lodash": "catalog:",
"@types/node": "18.16.1",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.2.18",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/uuid": "^9.0.8",
"@types/zxcvbn": "^4.4.4",
"typescript": "5.8.3"
"typescript": "catalog:"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 15 KiB

+2 -2
View File
@@ -2,8 +2,8 @@
"name": "",
"short_name": "",
"icons": [
{ "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
{ "src": "/favicon/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/favicon/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

+37 -80
View File
@@ -24,24 +24,24 @@
:root {
color-scheme: light !important;
--color-primary-10: 236, 241, 255;
--color-primary-20: 217, 228, 255;
--color-primary-30: 197, 214, 255;
--color-primary-40: 178, 200, 255;
--color-primary-50: 159, 187, 255;
--color-primary-60: 140, 173, 255;
--color-primary-70: 121, 159, 255;
--color-primary-80: 101, 145, 255;
--color-primary-90: 82, 132, 255;
--color-primary-100: 63, 118, 255;
--color-primary-200: 57, 106, 230;
--color-primary-300: 50, 94, 204;
--color-primary-400: 44, 83, 179;
--color-primary-500: 38, 71, 153;
--color-primary-600: 32, 59, 128;
--color-primary-700: 25, 47, 102;
--color-primary-800: 19, 35, 76;
--color-primary-900: 13, 24, 51;
--color-primary-10: 229, 243, 250;
--color-primary-20: 216, 237, 248;
--color-primary-30: 199, 229, 244;
--color-primary-40: 169, 214, 239;
--color-primary-50: 144, 202, 234;
--color-primary-60: 109, 186, 227;
--color-primary-70: 75, 170, 221;
--color-primary-80: 41, 154, 214;
--color-primary-90: 34, 129, 180;
--color-primary-100: 0, 99, 153;
--color-primary-200: 0, 92, 143;
--color-primary-300: 0, 86, 133;
--color-primary-400: 0, 77, 117;
--color-primary-500: 0, 66, 102;
--color-primary-600: 0, 53, 82;
--color-primary-700: 0, 43, 66;
--color-primary-800: 0, 33, 51;
--color-primary-900: 0, 23, 36;
--color-background-100: 255, 255, 255; /* primary bg */
--color-background-90: 247, 247, 247; /* secondary bg */
@@ -135,28 +135,6 @@
--color-border-300: 212, 212, 212; /* strong border- 1 */
--color-border-400: 185, 185, 185; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #f2f6ff 29.8%, #e1eaff 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(164deg, #fff 4.25%, rgba(255, 255, 255, 0.06) 93.5%);
--gradient-onboarding-400: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--color-onboarding-text-100: 23, 23, 23;
--color-onboarding-text-200: 58, 58, 58;
--color-onboarding-text-300: 82, 82, 82;
--color-onboarding-text-400: 163, 163, 163;
--color-onboarding-background-100: 236, 241, 255;
--color-onboarding-background-200: 255, 255, 255;
--color-onboarding-background-300: 236, 241, 255;
--color-onboarding-background-400: 177, 206, 250;
--color-onboarding-border-100: 229, 229, 229;
--color-onboarding-border-200: 217, 228, 255;
--color-onboarding-border-300: 229, 229, 229, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(126, 139, 171, 0.1);
/* toast theme */
--color-toast-success-text: 62, 155, 79;
--color-toast-error-text: 220, 62, 66;
@@ -197,6 +175,25 @@
[data-theme="dark-contrast"] {
color-scheme: dark !important;
--color-primary-10: 8, 31, 43;
--color-primary-20: 10, 37, 51;
--color-primary-30: 13, 49, 69;
--color-primary-40: 16, 58, 81;
--color-primary-50: 18, 68, 94;
--color-primary-60: 23, 86, 120;
--color-primary-70: 28, 104, 146;
--color-primary-80: 31, 116, 163;
--color-primary-90: 34, 129, 180;
--color-primary-100: 40, 146, 204;
--color-primary-200: 41, 154, 214;
--color-primary-300: 75, 170, 221;
--color-primary-400: 109, 186, 227;
--color-primary-500: 144, 202, 234;
--color-primary-600: 169, 214, 239;
--color-primary-700: 199, 229, 244;
--color-primary-800: 216, 237, 248;
--color-primary-900: 229, 243, 250;
--color-background-100: 25, 25, 25; /* primary bg */
--color-background-90: 32, 32, 32; /* secondary bg */
--color-background-80: 44, 44, 44; /* tertiary bg */
@@ -225,27 +222,6 @@
--color-border-300: 46, 46, 46; /* strong border- 1 */
--color-border-400: 58, 58, 58; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #18191b 25.17%, #18191b 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(47, 49, 53, 0.8) -22.23%, rgba(33, 34, 37, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(167deg, rgba(47, 49, 53, 0.45) 19.22%, #212225 98.48%);
--color-onboarding-text-100: 237, 238, 240;
--color-onboarding-text-200: 176, 180, 187;
--color-onboarding-text-300: 118, 123, 132;
--color-onboarding-text-400: 105, 110, 119;
--color-onboarding-background-100: 54, 58, 64;
--color-onboarding-background-200: 40, 42, 45;
--color-onboarding-background-300: 40, 42, 45;
--color-onboarding-background-400: 67, 72, 79;
--color-onboarding-border-100: 54, 58, 64;
--color-onboarding-border-200: 54, 58, 64;
--color-onboarding-border-300: 34, 35, 38, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(39, 44, 56, 0.1);
/* toast theme */
--color-toast-success-text: 178, 221, 181;
--color-toast-error-text: 206, 44, 49;
@@ -286,25 +262,6 @@
[data-theme="dark"],
[data-theme="light-contrast"],
[data-theme="dark-contrast"] {
--color-primary-10: 236, 241, 255;
--color-primary-20: 217, 228, 255;
--color-primary-30: 197, 214, 255;
--color-primary-40: 178, 200, 255;
--color-primary-50: 159, 187, 255;
--color-primary-60: 140, 173, 255;
--color-primary-70: 121, 159, 255;
--color-primary-80: 101, 145, 255;
--color-primary-90: 82, 132, 255;
--color-primary-100: 63, 118, 255;
--color-primary-200: 57, 106, 230;
--color-primary-300: 50, 94, 204;
--color-primary-400: 44, 83, 179;
--color-primary-500: 38, 71, 153;
--color-primary-600: 32, 59, 128;
--color-primary-700: 25, 47, 102;
--color-primary-800: 19, 35, 76;
--color-primary-900: 13, 24, 51;
--color-sidebar-background-100: var(--color-background-100); /* primary sidebar bg */
--color-sidebar-background-90: var(--color-background-90); /* secondary sidebar bg */
--color-sidebar-background-80: var(--color-background-80); /* tertiary sidebar bg */
+2 -3
View File
@@ -28,7 +28,7 @@ AWS_REGION=""
AWS_ACCESS_KEY_ID="access-key"
AWS_SECRET_ACCESS_KEY="secret-key"
AWS_S3_ENDPOINT_URL="http://localhost:9000"
# Changing this requires change in the nginx.conf for uploads if using minio setup
# Changing this requires change in the proxy config for uploads if using minio setup
AWS_S3_BUCKET_NAME="uploads"
# Maximum file upload limit
FILE_SIZE_LIMIT=5242880
@@ -39,8 +39,7 @@ DOCKERIZED=1 # deprecated
# set to 1 If using the pre-configured minio setup
USE_MINIO=0
# Nginx Configuration
NGINX_PORT=80
# Email redirections and minio domain settings
WEB_URL="http://localhost:8000"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "plane-api",
"version": "0.27.1",
"version": "1.0.0",
"license": "AGPL-3.0",
"private": true,
"description": "API server powering Plane's backend"
+7
View File
@@ -3,3 +3,10 @@ from django.apps import AppConfig
class ApiConfig(AppConfig):
name = "plane.api"
def ready(self):
# Import authentication extensions to register them with drf-spectacular
try:
import plane.utils.openapi.auth # noqa
except ImportError:
pass
+41 -4
View File
@@ -1,8 +1,14 @@
from .user import UserLiteSerializer
from .workspace import WorkspaceLiteSerializer
from .project import ProjectSerializer, ProjectLiteSerializer
from .project import (
ProjectSerializer,
ProjectLiteSerializer,
ProjectCreateSerializer,
ProjectUpdateSerializer,
)
from .issue import (
IssueSerializer,
LabelCreateUpdateSerializer,
LabelSerializer,
IssueLinkSerializer,
IssueCommentSerializer,
@@ -10,9 +16,40 @@ from .issue import (
IssueActivitySerializer,
IssueExpandSerializer,
IssueLiteSerializer,
IssueAttachmentUploadSerializer,
IssueSearchSerializer,
IssueCommentCreateSerializer,
IssueLinkCreateSerializer,
IssueLinkUpdateSerializer,
)
from .state import StateLiteSerializer, StateSerializer
from .cycle import CycleSerializer, CycleIssueSerializer, CycleLiteSerializer
from .module import ModuleSerializer, ModuleIssueSerializer, ModuleLiteSerializer
from .intake import IntakeIssueSerializer
from .cycle import (
CycleSerializer,
CycleIssueSerializer,
CycleLiteSerializer,
CycleIssueRequestSerializer,
TransferCycleIssueRequestSerializer,
CycleCreateSerializer,
CycleUpdateSerializer,
)
from .module import (
ModuleSerializer,
ModuleIssueSerializer,
ModuleLiteSerializer,
ModuleIssueRequestSerializer,
ModuleCreateSerializer,
ModuleUpdateSerializer,
)
from .intake import (
IntakeIssueSerializer,
IntakeIssueCreateSerializer,
IntakeIssueUpdateSerializer,
)
from .estimate import EstimatePointSerializer
from .asset import (
UserAssetUploadSerializer,
AssetUpdateSerializer,
GenericAssetUploadSerializer,
GenericAssetUpdateSerializer,
FileAssetSerializer,
)
+123
View File
@@ -0,0 +1,123 @@
# Third party imports
from rest_framework import serializers
# Module imports
from .base import BaseSerializer
from plane.db.models import FileAsset
class UserAssetUploadSerializer(serializers.Serializer):
"""
Serializer for user asset upload requests.
This serializer validates the metadata required to generate a presigned URL
for uploading user profile assets (avatar or cover image) directly to S3 storage.
Supports JPEG, PNG, WebP, JPG, and GIF image formats with size validation.
"""
name = serializers.CharField(help_text="Original filename of the asset")
type = serializers.ChoiceField(
choices=[
("image/jpeg", "JPEG"),
("image/png", "PNG"),
("image/webp", "WebP"),
("image/jpg", "JPG"),
("image/gif", "GIF"),
],
default="image/jpeg",
help_text="MIME type of the file",
style={"placeholder": "image/jpeg"},
)
size = serializers.IntegerField(help_text="File size in bytes")
entity_type = serializers.ChoiceField(
choices=[
(FileAsset.EntityTypeContext.USER_AVATAR, "User Avatar"),
(FileAsset.EntityTypeContext.USER_COVER, "User Cover"),
],
help_text="Type of user asset",
)
class AssetUpdateSerializer(serializers.Serializer):
"""
Serializer for asset status updates after successful upload completion.
Handles post-upload asset metadata updates including attribute modifications
and upload confirmation for S3-based file storage workflows.
"""
attributes = serializers.JSONField(
required=False, help_text="Additional attributes to update for the asset"
)
class GenericAssetUploadSerializer(serializers.Serializer):
"""
Serializer for generic asset upload requests with project association.
Validates metadata for generating presigned URLs for workspace assets including
project association, external system tracking, and file validation for
document management and content storage workflows.
"""
name = serializers.CharField(help_text="Original filename of the asset")
type = serializers.CharField(required=False, help_text="MIME type of the file")
size = serializers.IntegerField(help_text="File size in bytes")
project_id = serializers.UUIDField(
required=False,
help_text="UUID of the project to associate with the asset",
style={"placeholder": "123e4567-e89b-12d3-a456-426614174000"},
)
external_id = serializers.CharField(
required=False,
help_text="External identifier for the asset (for integration tracking)",
)
external_source = serializers.CharField(
required=False, help_text="External source system (for integration tracking)"
)
class GenericAssetUpdateSerializer(serializers.Serializer):
"""
Serializer for generic asset upload confirmation and status management.
Handles post-upload status updates for workspace assets including
upload completion marking and metadata finalization.
"""
is_uploaded = serializers.BooleanField(
default=True, help_text="Whether the asset has been successfully uploaded"
)
class FileAssetSerializer(BaseSerializer):
"""
Comprehensive file asset serializer with complete metadata and URL generation.
Provides full file asset information including storage metadata, access URLs,
relationship data, and upload status for complete asset management workflows.
"""
asset_url = serializers.CharField(read_only=True)
class Meta:
model = FileAsset
fields = "__all__"
read_only_fields = [
"id",
"created_by",
"updated_by",
"created_at",
"updated_at",
"workspace",
"project",
"issue",
"comment",
"page",
"draft_issue",
"user",
"is_deleted",
"deleted_at",
"storage_metadata",
"asset_url",
]
+8
View File
@@ -3,6 +3,13 @@ from rest_framework import serializers
class BaseSerializer(serializers.ModelSerializer):
"""
Base serializer providing common functionality for all model serializers.
Features field filtering, dynamic expansion of related fields, and standardized
primary key handling for consistent API responses across the application.
"""
id = serializers.PrimaryKeyRelatedField(read_only=True)
def __init__(self, *args, **kwargs):
@@ -84,6 +91,7 @@ class BaseSerializer(serializers.ModelSerializer):
"project_lead": UserLiteSerializer,
"state": StateLiteSerializer,
"created_by": UserLiteSerializer,
"updated_by": UserLiteSerializer,
"issue": IssueSerializer,
"actor": UserLiteSerializer,
"owned_by": UserLiteSerializer,
+104 -10
View File
@@ -8,16 +8,13 @@ from plane.db.models import Cycle, CycleIssue
from plane.utils.timezone_converter import convert_to_utc
class CycleSerializer(BaseSerializer):
total_issues = serializers.IntegerField(read_only=True)
cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True)
started_issues = serializers.IntegerField(read_only=True)
unstarted_issues = serializers.IntegerField(read_only=True)
backlog_issues = serializers.IntegerField(read_only=True)
total_estimates = serializers.FloatField(read_only=True)
completed_estimates = serializers.FloatField(read_only=True)
started_estimates = serializers.FloatField(read_only=True)
class CycleCreateSerializer(BaseSerializer):
"""
Serializer for creating cycles with timezone handling and date validation.
Manages cycle creation including project timezone conversion, date range validation,
and UTC normalization for time-bound iteration planning and sprint management.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -27,6 +24,29 @@ class CycleSerializer(BaseSerializer):
self.fields["start_date"].timezone = project_timezone
self.fields["end_date"].timezone = project_timezone
class Meta:
model = Cycle
fields = [
"name",
"description",
"start_date",
"end_date",
"owned_by",
"external_source",
"external_id",
"timezone",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
]
def validate(self, data):
if (
data.get("start_date", None) is not None
@@ -59,6 +79,40 @@ class CycleSerializer(BaseSerializer):
)
return data
class CycleUpdateSerializer(CycleCreateSerializer):
"""
Serializer for updating cycles with enhanced ownership management.
Extends cycle creation with update-specific features including ownership
assignment and modification tracking for cycle lifecycle management.
"""
class Meta(CycleCreateSerializer.Meta):
model = Cycle
fields = CycleCreateSerializer.Meta.fields + [
"owned_by",
]
class CycleSerializer(BaseSerializer):
"""
Cycle serializer with comprehensive project metrics and time tracking.
Provides cycle details including work item counts by status, progress estimates,
and time-bound iteration data for project management and sprint planning.
"""
total_issues = serializers.IntegerField(read_only=True)
cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True)
started_issues = serializers.IntegerField(read_only=True)
unstarted_issues = serializers.IntegerField(read_only=True)
backlog_issues = serializers.IntegerField(read_only=True)
total_estimates = serializers.FloatField(read_only=True)
completed_estimates = serializers.FloatField(read_only=True)
started_estimates = serializers.FloatField(read_only=True)
class Meta:
model = Cycle
fields = "__all__"
@@ -76,6 +130,13 @@ class CycleSerializer(BaseSerializer):
class CycleIssueSerializer(BaseSerializer):
"""
Serializer for cycle-issue relationships with sub-issue counting.
Manages the association between cycles and work items, including
hierarchical issue tracking for nested work item structures.
"""
sub_issues_count = serializers.IntegerField(read_only=True)
class Meta:
@@ -85,6 +146,39 @@ class CycleIssueSerializer(BaseSerializer):
class CycleLiteSerializer(BaseSerializer):
"""
Lightweight cycle serializer for minimal data transfer.
Provides essential cycle information without computed metrics,
optimized for list views and reference lookups.
"""
class Meta:
model = Cycle
fields = "__all__"
class CycleIssueRequestSerializer(serializers.Serializer):
"""
Serializer for bulk work item assignment to cycles.
Validates work item ID lists for batch operations including
cycle assignment and sprint planning workflows.
"""
issues = serializers.ListField(
child=serializers.UUIDField(), help_text="List of issue IDs to add to the cycle"
)
class TransferCycleIssueRequestSerializer(serializers.Serializer):
"""
Serializer for transferring work items between cycles.
Handles work item migration between cycles including validation
and relationship updates for sprint reallocation workflows.
"""
new_cycle_id = serializers.UUIDField(
help_text="ID of the target cycle to transfer issues to"
)
@@ -4,6 +4,13 @@ from .base import BaseSerializer
class EstimatePointSerializer(BaseSerializer):
"""
Serializer for project estimation points and story point values.
Handles numeric estimation data for work item sizing and sprint planning,
providing standardized point values for project velocity calculations.
"""
class Meta:
model = EstimatePoint
fields = ["id", "value"]
+117 -1
View File
@@ -1,11 +1,77 @@
# Module improts
from .base import BaseSerializer
from .issue import IssueExpandSerializer
from plane.db.models import IntakeIssue
from plane.db.models import IntakeIssue, Issue
from rest_framework import serializers
class IssueForIntakeSerializer(BaseSerializer):
"""
Serializer for work item data within intake submissions.
Handles essential work item fields for intake processing including
content validation and priority assignment for triage workflows.
"""
class Meta:
model = Issue
fields = [
"name",
"description",
"description_html",
"priority",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
]
class IntakeIssueCreateSerializer(BaseSerializer):
"""
Serializer for creating intake work items with embedded issue data.
Manages intake work item creation including nested issue creation,
status assignment, and source tracking for issue queue management.
"""
issue = IssueForIntakeSerializer(help_text="Issue data for the intake issue")
class Meta:
model = IntakeIssue
fields = [
"issue",
"intake",
"status",
"snoozed_till",
"duplicate_to",
"source",
"source_email",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
]
class IntakeIssueSerializer(BaseSerializer):
"""
Comprehensive serializer for intake work items with expanded issue details.
Provides full intake work item data including embedded issue information,
status tracking, and triage metadata for issue queue management.
"""
issue_detail = IssueExpandSerializer(read_only=True, source="issue")
inbox = serializers.UUIDField(source="intake.id", read_only=True)
@@ -22,3 +88,53 @@ class IntakeIssueSerializer(BaseSerializer):
"created_at",
"updated_at",
]
class IntakeIssueUpdateSerializer(BaseSerializer):
"""
Serializer for updating intake work items and their associated issues.
Handles intake work item modifications including status changes, triage decisions,
and embedded issue updates for issue queue processing workflows.
"""
issue = IssueForIntakeSerializer(
required=False, help_text="Issue data to update in the intake issue"
)
class Meta:
model = IntakeIssue
fields = [
"status",
"snoozed_till",
"duplicate_to",
"source",
"source_email",
"issue",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
]
class IssueDataSerializer(serializers.Serializer):
"""
Serializer for nested work item data in intake request payloads.
Validates core work item fields within intake requests including
content formatting, priority levels, and metadata for issue creation.
"""
name = serializers.CharField(max_length=255, help_text="Issue name")
description_html = serializers.CharField(
required=False, allow_null=True, help_text="Issue description HTML"
)
priority = serializers.ChoiceField(
choices=Issue.PRIORITY_CHOICES, default="none", help_text="Issue priority"
)
+280 -5
View File
@@ -20,6 +20,11 @@ from plane.db.models import (
ProjectMember,
State,
User,
EstimatePoint,
)
from plane.utils.content_validator import (
validate_html_content,
validate_binary_data,
)
from .base import BaseSerializer
@@ -34,6 +39,13 @@ from django.core.validators import URLValidator
class IssueSerializer(BaseSerializer):
"""
Comprehensive work item serializer with full relationship management.
Handles complete work item lifecycle including assignees, labels, validation,
and related model updates. Supports dynamic field expansion and HTML content processing.
"""
assignees = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(
queryset=User.objects.values_list("id", flat=True)
@@ -75,6 +87,26 @@ class IssueSerializer(BaseSerializer):
except Exception:
raise serializers.ValidationError("Invalid HTML passed")
# Validate description content for security
if data.get("description_html"):
is_valid, error_msg, sanitized_html = validate_html_content(
data["description_html"]
)
if not is_valid:
raise serializers.ValidationError(
{"error": "html content is not valid"}
)
# Update the data with sanitized HTML if available
if sanitized_html is not None:
data["description_html"] = sanitized_html
if data.get("description_binary"):
is_valid, error_msg = validate_binary_data(data["description_binary"])
if not is_valid:
raise serializers.ValidationError(
{"description_binary": "Invalid binary data"}
)
# Validate assignees are from project
if data.get("assignees", []):
data["assignees"] = ProjectMember.objects.filter(
@@ -105,13 +137,27 @@ class IssueSerializer(BaseSerializer):
if (
data.get("parent")
and not Issue.objects.filter(
workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id
workspace_id=self.context.get("workspace_id"),
project_id=self.context.get("project_id"),
pk=data.get("parent").id,
).exists()
):
raise serializers.ValidationError(
"Parent is not valid issue_id please pass a valid issue_id"
)
if (
data.get("estimate_point")
and not EstimatePoint.objects.filter(
workspace_id=self.context.get("workspace_id"),
project_id=self.context.get("project_id"),
pk=data.get("estimate_point").id,
).exists()
):
raise serializers.ValidationError(
"Estimate point is not valid please pass a valid estimate_point_id"
)
return data
def create(self, validated_data):
@@ -300,13 +346,58 @@ class IssueSerializer(BaseSerializer):
class IssueLiteSerializer(BaseSerializer):
"""
Lightweight work item serializer for minimal data transfer.
Provides essential work item identifiers optimized for list views,
references, and performance-critical operations.
"""
class Meta:
model = Issue
fields = ["id", "sequence_id", "project_id"]
read_only_fields = fields
class LabelCreateUpdateSerializer(BaseSerializer):
"""
Serializer for creating and updating work item labels.
Manages label metadata including colors, descriptions, hierarchy,
and sorting for work item categorization and filtering.
"""
class Meta:
model = Label
fields = [
"name",
"color",
"description",
"external_source",
"external_id",
"parent",
"sort_order",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
]
class LabelSerializer(BaseSerializer):
"""
Full serializer for work item labels with complete metadata.
Provides comprehensive label information including hierarchical relationships,
visual properties, and organizational data for work item tagging.
"""
class Meta:
model = Label
fields = "__all__"
@@ -322,10 +413,17 @@ class LabelSerializer(BaseSerializer):
]
class IssueLinkSerializer(BaseSerializer):
class IssueLinkCreateSerializer(BaseSerializer):
"""
Serializer for creating work item external links with validation.
Handles URL validation, format checking, and duplicate prevention
for attaching external resources to work items.
"""
class Meta:
model = IssueLink
fields = "__all__"
fields = ["url", "issue_id"]
read_only_fields = [
"id",
"workspace",
@@ -361,6 +459,22 @@ class IssueLinkSerializer(BaseSerializer):
)
return IssueLink.objects.create(**validated_data)
class IssueLinkUpdateSerializer(IssueLinkCreateSerializer):
"""
Serializer for updating work item external links.
Extends link creation with update-specific validation to prevent
URL conflicts and maintain link integrity during modifications.
"""
class Meta(IssueLinkCreateSerializer.Meta):
model = IssueLink
fields = IssueLinkCreateSerializer.Meta.fields + [
"issue_id",
]
read_only_fields = IssueLinkCreateSerializer.Meta.read_only_fields
def update(self, instance, validated_data):
if (
IssueLink.objects.filter(
@@ -376,7 +490,37 @@ class IssueLinkSerializer(BaseSerializer):
return super().update(instance, validated_data)
class IssueLinkSerializer(BaseSerializer):
"""
Full serializer for work item external links.
Provides complete link information including metadata and timestamps
for managing external resource associations with work items.
"""
class Meta:
model = IssueLink
fields = "__all__"
read_only_fields = [
"id",
"workspace",
"project",
"issue",
"created_by",
"updated_by",
"created_at",
"updated_at",
]
class IssueAttachmentSerializer(BaseSerializer):
"""
Serializer for work item file attachments.
Manages file asset associations with work items including metadata,
storage information, and access control for document management.
"""
class Meta:
model = FileAsset
fields = "__all__"
@@ -390,7 +534,47 @@ class IssueAttachmentSerializer(BaseSerializer):
]
class IssueCommentCreateSerializer(BaseSerializer):
"""
Serializer for creating work item comments.
Handles comment creation with JSON and HTML content support,
access control, and external integration tracking.
"""
class Meta:
model = IssueComment
fields = [
"comment_json",
"comment_html",
"access",
"external_source",
"external_id",
]
read_only_fields = [
"id",
"workspace",
"project",
"issue",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
"actor",
"comment_stripped",
"edited_at",
]
class IssueCommentSerializer(BaseSerializer):
"""
Full serializer for work item comments with membership context.
Provides complete comment data including member status, content formatting,
and edit tracking for collaborative work item discussions.
"""
is_member = serializers.BooleanField(read_only=True)
class Meta:
@@ -420,12 +604,26 @@ class IssueCommentSerializer(BaseSerializer):
class IssueActivitySerializer(BaseSerializer):
"""
Serializer for work item activity and change history.
Tracks and represents work item modifications, state changes,
and user interactions for audit trails and activity feeds.
"""
class Meta:
model = IssueActivity
exclude = ["created_by", "updated_by"]
class CycleIssueSerializer(BaseSerializer):
"""
Serializer for work items within cycles.
Provides cycle context for work items including cycle metadata
and timing information for sprint and iteration management.
"""
cycle = CycleSerializer(read_only=True)
class Meta:
@@ -433,6 +631,13 @@ class CycleIssueSerializer(BaseSerializer):
class ModuleIssueSerializer(BaseSerializer):
"""
Serializer for work items within modules.
Provides module context for work items including module metadata
and organizational information for feature-based work grouping.
"""
module = ModuleSerializer(read_only=True)
class Meta:
@@ -440,18 +645,50 @@ class ModuleIssueSerializer(BaseSerializer):
class LabelLiteSerializer(BaseSerializer):
"""
Lightweight label serializer for minimal data transfer.
Provides essential label information with visual properties,
optimized for UI display and performance-critical operations.
"""
class Meta:
model = Label
fields = ["id", "name", "color"]
class IssueExpandSerializer(BaseSerializer):
"""
Extended work item serializer with full relationship expansion.
Provides work items with expanded related data including cycles, modules,
labels, assignees, and states for comprehensive data representation.
"""
cycle = CycleLiteSerializer(source="issue_cycle.cycle", read_only=True)
module = ModuleLiteSerializer(source="issue_module.module", read_only=True)
labels = LabelLiteSerializer(read_only=True, many=True)
assignees = UserLiteSerializer(read_only=True, many=True)
labels = serializers.SerializerMethodField()
assignees = serializers.SerializerMethodField()
state = StateLiteSerializer(read_only=True)
def get_labels(self, obj):
expand = self.context.get("expand", [])
if "labels" in expand:
# Use prefetched data
return LabelLiteSerializer(
[il.label for il in obj.label_issue.all()], many=True
).data
return [il.label_id for il in obj.label_issue.all()]
def get_assignees(self, obj):
expand = self.context.get("expand", [])
if "assignees" in expand:
return UserLiteSerializer(
[ia.assignee for ia in obj.issue_assignee.all()], many=True
).data
return [ia.assignee_id for ia in obj.issue_assignee.all()]
class Meta:
model = Issue
fields = "__all__"
@@ -464,3 +701,41 @@ class IssueExpandSerializer(BaseSerializer):
"created_at",
"updated_at",
]
class IssueAttachmentUploadSerializer(serializers.Serializer):
"""
Serializer for work item attachment upload request validation.
Handles file upload metadata validation including size, type, and external
integration tracking for secure work item document attachment workflows.
"""
name = serializers.CharField(help_text="Original filename of the asset")
type = serializers.CharField(required=False, help_text="MIME type of the file")
size = serializers.IntegerField(help_text="File size in bytes")
external_id = serializers.CharField(
required=False,
help_text="External identifier for the asset (for integration tracking)",
)
external_source = serializers.CharField(
required=False, help_text="External source system (for integration tracking)"
)
class IssueSearchSerializer(serializers.Serializer):
"""
Serializer for work item search result data formatting.
Provides standardized search result structure including work item identifiers,
project context, and workspace information for search API responses.
"""
id = serializers.CharField(required=True, help_text="Issue ID")
name = serializers.CharField(required=True, help_text="Issue name")
sequence_id = serializers.CharField(required=True, help_text="Issue sequence ID")
project__identifier = serializers.CharField(
required=True, help_text="Project identifier"
)
project_id = serializers.CharField(required=True, help_text="Project ID")
workspace__slug = serializers.CharField(required=True, help_text="Workspace slug")
+111 -16
View File
@@ -13,24 +13,33 @@ from plane.db.models import (
)
class ModuleSerializer(BaseSerializer):
class ModuleCreateSerializer(BaseSerializer):
"""
Serializer for creating modules with member validation and date checking.
Handles module creation including member assignment validation, date range verification,
and duplicate name prevention for feature-based project organization setup.
"""
members = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(
queryset=User.objects.values_list("id", flat=True)
),
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()),
write_only=True,
required=False,
)
total_issues = serializers.IntegerField(read_only=True)
cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True)
started_issues = serializers.IntegerField(read_only=True)
unstarted_issues = serializers.IntegerField(read_only=True)
backlog_issues = serializers.IntegerField(read_only=True)
class Meta:
model = Module
fields = "__all__"
fields = [
"name",
"description",
"start_date",
"target_date",
"status",
"lead",
"members",
"external_source",
"external_id",
]
read_only_fields = [
"id",
"workspace",
@@ -42,11 +51,6 @@ class ModuleSerializer(BaseSerializer):
"deleted_at",
]
def to_representation(self, instance):
data = super().to_representation(instance)
data["members"] = [str(member.id) for member in instance.members.all()]
return data
def validate(self, data):
if (
data.get("start_date", None) is not None
@@ -96,6 +100,22 @@ class ModuleSerializer(BaseSerializer):
return module
class ModuleUpdateSerializer(ModuleCreateSerializer):
"""
Serializer for updating modules with enhanced validation and member management.
Extends module creation with update-specific validations including member reassignment,
name conflict checking, and relationship management for module modifications.
"""
class Meta(ModuleCreateSerializer.Meta):
model = Module
fields = ModuleCreateSerializer.Meta.fields + [
"members",
]
read_only_fields = ModuleCreateSerializer.Meta.read_only_fields
def update(self, instance, validated_data):
members = validated_data.pop("members", None)
module_name = validated_data.get("name")
@@ -131,7 +151,54 @@ class ModuleSerializer(BaseSerializer):
return super().update(instance, validated_data)
class ModuleSerializer(BaseSerializer):
"""
Comprehensive module serializer with work item metrics and member management.
Provides complete module data including work item counts by status, member relationships,
and progress tracking for feature-based project organization.
"""
members = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()),
write_only=True,
required=False,
)
total_issues = serializers.IntegerField(read_only=True)
cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True)
started_issues = serializers.IntegerField(read_only=True)
unstarted_issues = serializers.IntegerField(read_only=True)
backlog_issues = serializers.IntegerField(read_only=True)
class Meta:
model = Module
fields = "__all__"
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
]
def to_representation(self, instance):
data = super().to_representation(instance)
data["members"] = [str(member.id) for member in instance.members.all()]
return data
class ModuleIssueSerializer(BaseSerializer):
"""
Serializer for module-work item relationships with sub-item counting.
Manages the association between modules and work items, including
hierarchical issue tracking for nested work item structures.
"""
sub_issues_count = serializers.IntegerField(read_only=True)
class Meta:
@@ -149,6 +216,13 @@ class ModuleIssueSerializer(BaseSerializer):
class ModuleLinkSerializer(BaseSerializer):
"""
Serializer for module external links with URL validation.
Handles external resource associations with modules including
URL validation and duplicate prevention for reference management.
"""
class Meta:
model = ModuleLink
fields = "__all__"
@@ -174,6 +248,27 @@ class ModuleLinkSerializer(BaseSerializer):
class ModuleLiteSerializer(BaseSerializer):
"""
Lightweight module serializer for minimal data transfer.
Provides essential module information without computed metrics,
optimized for list views and reference lookups.
"""
class Meta:
model = Module
fields = "__all__"
class ModuleIssueRequestSerializer(serializers.Serializer):
"""
Serializer for bulk work item assignment to modules.
Validates work item ID lists for batch operations including
module assignment and work item organization workflows.
"""
issues = serializers.ListField(
child=serializers.UUIDField(),
help_text="List of issue IDs to add to the module",
)
+163 -1
View File
@@ -2,12 +2,153 @@
from rest_framework import serializers
# Module imports
from plane.db.models import Project, ProjectIdentifier, WorkspaceMember
from plane.db.models import (
Project,
ProjectIdentifier,
WorkspaceMember,
State,
Estimate,
)
from plane.utils.content_validator import (
validate_html_content,
)
from .base import BaseSerializer
class ProjectCreateSerializer(BaseSerializer):
"""
Serializer for creating projects with workspace validation.
Handles project creation including identifier validation, member verification,
and workspace association for new project initialization.
"""
class Meta:
model = Project
fields = [
"name",
"description",
"project_lead",
"default_assignee",
"identifier",
"icon_prop",
"emoji",
"cover_image",
"module_view",
"cycle_view",
"issue_views_view",
"page_view",
"intake_view",
"guest_view_all_features",
"archive_in",
"close_in",
"timezone",
"logo_props",
"external_source",
"external_id",
"is_issue_type_enabled",
]
read_only_fields = [
"id",
"workspace",
"created_at",
"updated_at",
"created_by",
"updated_by",
]
def validate(self, data):
if data.get("project_lead", None) is not None:
# Check if the project lead is a member of the workspace
if not WorkspaceMember.objects.filter(
workspace_id=self.context["workspace_id"],
member_id=data.get("project_lead"),
).exists():
raise serializers.ValidationError(
"Project lead should be a user in the workspace"
)
if data.get("default_assignee", None) is not None:
# Check if the default assignee is a member of the workspace
if not WorkspaceMember.objects.filter(
workspace_id=self.context["workspace_id"],
member_id=data.get("default_assignee"),
).exists():
raise serializers.ValidationError(
"Default assignee should be a user in the workspace"
)
return data
def create(self, validated_data):
identifier = validated_data.get("identifier", "").strip().upper()
if identifier == "":
raise serializers.ValidationError(detail="Project Identifier is required")
if ProjectIdentifier.objects.filter(
name=identifier, workspace_id=self.context["workspace_id"]
).exists():
raise serializers.ValidationError(detail="Project Identifier is taken")
project = Project.objects.create(
**validated_data, workspace_id=self.context["workspace_id"]
)
return project
class ProjectUpdateSerializer(ProjectCreateSerializer):
"""
Serializer for updating projects with enhanced state and estimation management.
Extends project creation with update-specific validations including default state
assignment, estimation configuration, and project setting modifications.
"""
class Meta(ProjectCreateSerializer.Meta):
model = Project
fields = ProjectCreateSerializer.Meta.fields + [
"default_state",
"estimate",
]
read_only_fields = ProjectCreateSerializer.Meta.read_only_fields
def update(self, instance, validated_data):
"""Update a project"""
if (
validated_data.get("default_state", None) is not None
and not State.objects.filter(
project=instance, id=validated_data.get("default_state")
).exists()
):
# Check if the default state is a state in the project
raise serializers.ValidationError(
"Default state should be a state in the project"
)
if (
validated_data.get("estimate", None) is not None
and not Estimate.objects.filter(
project=instance, id=validated_data.get("estimate")
).exists()
):
# Check if the estimate is a estimate in the project
raise serializers.ValidationError(
"Estimate should be a estimate in the project"
)
return super().update(instance, validated_data)
class ProjectSerializer(BaseSerializer):
"""
Comprehensive project serializer with metrics and member context.
Provides complete project data including member counts, cycle/module totals,
deployment status, and user-specific context for project management.
"""
total_members = serializers.IntegerField(read_only=True)
total_cycles = serializers.IntegerField(read_only=True)
total_modules = serializers.IntegerField(read_only=True)
@@ -57,6 +198,20 @@ class ProjectSerializer(BaseSerializer):
"Default assignee should be a user in the workspace"
)
# Validate description content for security
if "description_html" in data and data["description_html"]:
if isinstance(data["description_html"], dict):
is_valid, error_msg, sanitized_html = validate_html_content(
str(data["description_html"])
)
# Update the data with sanitized HTML if available
if sanitized_html is not None:
data["description_html"] = sanitized_html
if not is_valid:
raise serializers.ValidationError(
{"error": "html content is not valid"}
)
return data
def create(self, validated_data):
@@ -81,6 +236,13 @@ class ProjectSerializer(BaseSerializer):
class ProjectLiteSerializer(BaseSerializer):
"""
Lightweight project serializer for minimal data transfer.
Provides essential project information including identifiers, visual properties,
and basic metadata optimized for list views and references.
"""
cover_image_url = serializers.CharField(read_only=True)
class Meta:
+15
View File
@@ -4,6 +4,13 @@ from plane.db.models import State
class StateSerializer(BaseSerializer):
"""
Serializer for work item states with default state management.
Handles state creation and updates including default state validation
and automatic default state switching for workflow management.
"""
def validate(self, data):
# If the default is being provided then make all other states default False
if data.get("default", False):
@@ -24,10 +31,18 @@ class StateSerializer(BaseSerializer):
"workspace",
"project",
"deleted_at",
"slug",
]
class StateLiteSerializer(BaseSerializer):
"""
Lightweight state serializer for minimal data transfer.
Provides essential state information including visual properties
and grouping data optimized for UI display and filtering.
"""
class Meta:
model = State
fields = ["id", "name", "color", "group"]
+14
View File
@@ -1,3 +1,5 @@
from rest_framework import serializers
# Module imports
from plane.db.models import User
@@ -5,6 +7,18 @@ from .base import BaseSerializer
class UserLiteSerializer(BaseSerializer):
"""
Lightweight user serializer for minimal data transfer.
Provides essential user information including names, avatar, and contact details
optimized for member lists, assignee displays, and user references.
"""
avatar_url = serializers.CharField(
help_text="Avatar URL",
read_only=True,
)
class Meta:
model = User
fields = [
+6 -1
View File
@@ -4,7 +4,12 @@ from .base import BaseSerializer
class WorkspaceLiteSerializer(BaseSerializer):
"""Lite serializer with only required fields"""
"""
Lightweight workspace serializer for minimal data transfer.
Provides essential workspace identifiers including name, slug, and ID
optimized for navigation, references, and performance-critical operations.
"""
class Meta:
model = Workspace
+4
View File
@@ -5,8 +5,11 @@ from .cycle import urlpatterns as cycle_patterns
from .module import urlpatterns as module_patterns
from .intake import urlpatterns as intake_patterns
from .member import urlpatterns as member_patterns
from .asset import urlpatterns as asset_patterns
from .user import urlpatterns as user_patterns
urlpatterns = [
*asset_patterns,
*project_patterns,
*state_patterns,
*issue_patterns,
@@ -14,4 +17,5 @@ urlpatterns = [
*module_patterns,
*intake_patterns,
*member_patterns,
*user_patterns,
]
+40
View File
@@ -0,0 +1,40 @@
from django.urls import path
from plane.api.views import (
UserAssetEndpoint,
UserServerAssetEndpoint,
GenericAssetEndpoint,
)
urlpatterns = [
path(
"assets/user-assets/",
UserAssetEndpoint.as_view(http_method_names=["post"]),
name="user-assets",
),
path(
"assets/user-assets/<uuid:asset_id>/",
UserAssetEndpoint.as_view(http_method_names=["patch", "delete"]),
name="user-assets-detail",
),
path(
"assets/user-assets/server/",
UserServerAssetEndpoint.as_view(http_method_names=["post"]),
name="user-server-assets",
),
path(
"assets/user-assets/<uuid:asset_id>/server/",
UserServerAssetEndpoint.as_view(http_method_names=["patch", "delete"]),
name="user-server-assets-detail",
),
path(
"workspaces/<str:slug>/assets/",
GenericAssetEndpoint.as_view(http_method_names=["post"]),
name="generic-asset",
),
path(
"workspaces/<str:slug>/assets/<uuid:asset_id>/",
GenericAssetEndpoint.as_view(http_method_names=["get", "patch"]),
name="generic-asset-detail",
),
]
+16 -9
View File
@@ -1,8 +1,10 @@
from django.urls import path
from plane.api.views.cycle import (
CycleAPIEndpoint,
CycleIssueAPIEndpoint,
CycleListCreateAPIEndpoint,
CycleDetailAPIEndpoint,
CycleIssueListCreateAPIEndpoint,
CycleIssueDetailAPIEndpoint,
TransferCycleIssueAPIEndpoint,
CycleArchiveUnarchiveAPIEndpoint,
)
@@ -10,37 +12,42 @@ from plane.api.views.cycle import (
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/",
CycleAPIEndpoint.as_view(),
CycleListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="cycles",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:pk>/",
CycleAPIEndpoint.as_view(),
CycleDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="cycles",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/cycle-issues/",
CycleIssueAPIEndpoint.as_view(),
CycleIssueListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="cycle-issues",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/cycle-issues/<uuid:issue_id>/",
CycleIssueAPIEndpoint.as_view(),
CycleIssueDetailAPIEndpoint.as_view(http_method_names=["get", "delete"]),
name="cycle-issues",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/transfer-issues/",
TransferCycleIssueAPIEndpoint.as_view(),
TransferCycleIssueAPIEndpoint.as_view(http_method_names=["post"]),
name="transfer-issues",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/archive/",
CycleArchiveUnarchiveAPIEndpoint.as_view(),
CycleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["post"]),
name="cycle-archive-unarchive",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-cycles/",
CycleArchiveUnarchiveAPIEndpoint.as_view(),
CycleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["get"]),
name="cycle-archive-unarchive",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-cycles/<uuid:pk>/unarchive/",
CycleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["delete"]),
name="cycle-archive-unarchive",
),
]
+8 -3
View File
@@ -1,17 +1,22 @@
from django.urls import path
from plane.api.views import IntakeIssueAPIEndpoint
from plane.api.views import (
IntakeIssueListCreateAPIEndpoint,
IntakeIssueDetailAPIEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/",
IntakeIssueAPIEndpoint.as_view(),
IntakeIssueListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="intake-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/<uuid:issue_id>/",
IntakeIssueAPIEndpoint.as_view(),
IntakeIssueDetailAPIEndpoint.as_view(
http_method_names=["get", "patch", "delete"]
),
name="intake-issue",
),
]
+38 -20
View File
@@ -1,79 +1,97 @@
from django.urls import path
from plane.api.views import (
IssueAPIEndpoint,
LabelAPIEndpoint,
IssueLinkAPIEndpoint,
IssueCommentAPIEndpoint,
IssueActivityAPIEndpoint,
IssueListCreateAPIEndpoint,
IssueDetailAPIEndpoint,
LabelListCreateAPIEndpoint,
LabelDetailAPIEndpoint,
IssueLinkListCreateAPIEndpoint,
IssueLinkDetailAPIEndpoint,
IssueCommentListCreateAPIEndpoint,
IssueCommentDetailAPIEndpoint,
IssueActivityListAPIEndpoint,
IssueActivityDetailAPIEndpoint,
IssueAttachmentListCreateAPIEndpoint,
IssueAttachmentDetailAPIEndpoint,
WorkspaceIssueAPIEndpoint,
IssueAttachmentEndpoint,
IssueSearchEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/issues/<str:project__identifier>-<str:issue__identifier>/",
WorkspaceIssueAPIEndpoint.as_view(),
"workspaces/<str:slug>/issues/search/",
IssueSearchEndpoint.as_view(http_method_names=["get"]),
name="issue-search",
),
path(
"workspaces/<str:slug>/issues/<str:project_identifier>-<str:issue_identifier>/",
WorkspaceIssueAPIEndpoint.as_view(http_method_names=["get"]),
name="issue-by-identifier",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/",
IssueAPIEndpoint.as_view(),
IssueListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:pk>/",
IssueAPIEndpoint.as_view(),
IssueDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/labels/",
LabelAPIEndpoint.as_view(),
LabelListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="label",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/labels/<uuid:pk>/",
LabelAPIEndpoint.as_view(),
LabelDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="label",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/links/",
IssueLinkAPIEndpoint.as_view(),
IssueLinkListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="link",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/links/<uuid:pk>/",
IssueLinkAPIEndpoint.as_view(),
IssueLinkDetailAPIEndpoint.as_view(
http_method_names=["get", "patch", "delete"]
),
name="link",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/comments/",
IssueCommentAPIEndpoint.as_view(),
IssueCommentListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="comment",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/comments/<uuid:pk>/",
IssueCommentAPIEndpoint.as_view(),
IssueCommentDetailAPIEndpoint.as_view(
http_method_names=["get", "patch", "delete"]
),
name="comment",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/activities/",
IssueActivityAPIEndpoint.as_view(),
IssueActivityListAPIEndpoint.as_view(http_method_names=["get"]),
name="activity",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/activities/<uuid:pk>/",
IssueActivityAPIEndpoint.as_view(),
IssueActivityDetailAPIEndpoint.as_view(http_method_names=["get"]),
name="activity",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-attachments/",
IssueAttachmentEndpoint.as_view(),
IssueAttachmentListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="attachment",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-attachments/<uuid:pk>/",
IssueAttachmentEndpoint.as_view(),
IssueAttachmentDetailAPIEndpoint.as_view(
http_method_names=["get", "patch", "delete"]
),
name="issue-attachment",
),
]
+10 -5
View File
@@ -1,11 +1,16 @@
from django.urls import path
from plane.api.views import ProjectMemberAPIEndpoint
from plane.api.views import ProjectMemberAPIEndpoint, WorkspaceMemberAPIEndpoint
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<str:project_id>/members/",
ProjectMemberAPIEndpoint.as_view(),
name="users",
)
"workspaces/<str:slug>/projects/<uuid:project_id>/members/",
ProjectMemberAPIEndpoint.as_view(http_method_names=["get"]),
name="project-members",
),
path(
"workspaces/<str:slug>/members/",
WorkspaceMemberAPIEndpoint.as_view(http_method_names=["get"]),
name="workspace-members",
),
]
+19 -12
View File
@@ -1,40 +1,47 @@
from django.urls import path
from plane.api.views import (
ModuleAPIEndpoint,
ModuleIssueAPIEndpoint,
ModuleListCreateAPIEndpoint,
ModuleDetailAPIEndpoint,
ModuleIssueListCreateAPIEndpoint,
ModuleIssueDetailAPIEndpoint,
ModuleArchiveUnarchiveAPIEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/",
ModuleAPIEndpoint.as_view(),
ModuleListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="modules",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:pk>/",
ModuleAPIEndpoint.as_view(),
name="modules",
ModuleDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="modules-detail",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-issues/",
ModuleIssueAPIEndpoint.as_view(),
ModuleIssueListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="module-issues",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-issues/<uuid:issue_id>/",
ModuleIssueAPIEndpoint.as_view(),
name="module-issues",
ModuleIssueDetailAPIEndpoint.as_view(http_method_names=["delete"]),
name="module-issues-detail",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:pk>/archive/",
ModuleArchiveUnarchiveAPIEndpoint.as_view(),
name="module-archive-unarchive",
ModuleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["post"]),
name="module-archive",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-modules/",
ModuleArchiveUnarchiveAPIEndpoint.as_view(),
name="module-archive-unarchive",
ModuleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["get"]),
name="module-archive-list",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-modules/<uuid:pk>/unarchive/",
ModuleArchiveUnarchiveAPIEndpoint.as_view(http_method_names=["delete"]),
name="module-unarchive",
),
]
+12 -4
View File
@@ -1,19 +1,27 @@
from django.urls import path
from plane.api.views import ProjectAPIEndpoint, ProjectArchiveUnarchiveAPIEndpoint
from plane.api.views import (
ProjectListCreateAPIEndpoint,
ProjectDetailAPIEndpoint,
ProjectArchiveUnarchiveAPIEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/", ProjectAPIEndpoint.as_view(), name="project"
"workspaces/<str:slug>/projects/",
ProjectListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="project",
),
path(
"workspaces/<str:slug>/projects/<uuid:pk>/",
ProjectAPIEndpoint.as_view(),
ProjectDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="project",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archive/",
ProjectArchiveUnarchiveAPIEndpoint.as_view(),
ProjectArchiveUnarchiveAPIEndpoint.as_view(
http_method_names=["post", "delete"]
),
name="project-archive-unarchive",
),
]
+20
View File
@@ -0,0 +1,20 @@
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)
from django.urls import path
urlpatterns = [
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path(
"schema/swagger-ui/",
SpectacularSwaggerView.as_view(url_name="schema"),
name="swagger-ui",
),
path(
"schema/redoc/",
SpectacularRedocView.as_view(url_name="schema"),
name="redoc",
),
]
+6 -3
View File
@@ -1,16 +1,19 @@
from django.urls import path
from plane.api.views import StateAPIEndpoint
from plane.api.views import (
StateListCreateAPIEndpoint,
StateDetailAPIEndpoint,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/states/",
StateAPIEndpoint.as_view(),
StateListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="states",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/states/<uuid:state_id>/",
StateAPIEndpoint.as_view(),
StateDetailAPIEndpoint.as_view(http_method_names=["get", "patch", "delete"]),
name="states",
),
]
+11
View File
@@ -0,0 +1,11 @@
from django.urls import path
from plane.api.views import UserEndpoint
urlpatterns = [
path(
"users/me/",
UserEndpoint.as_view(http_method_names=["get"]),
name="users",
),
]
+39 -14
View File
@@ -1,30 +1,55 @@
from .project import ProjectAPIEndpoint, ProjectArchiveUnarchiveAPIEndpoint
from .project import (
ProjectListCreateAPIEndpoint,
ProjectDetailAPIEndpoint,
ProjectArchiveUnarchiveAPIEndpoint,
)
from .state import StateAPIEndpoint
from .state import (
StateListCreateAPIEndpoint,
StateDetailAPIEndpoint,
)
from .issue import (
WorkspaceIssueAPIEndpoint,
IssueAPIEndpoint,
LabelAPIEndpoint,
IssueLinkAPIEndpoint,
IssueCommentAPIEndpoint,
IssueActivityAPIEndpoint,
IssueAttachmentEndpoint,
IssueListCreateAPIEndpoint,
IssueDetailAPIEndpoint,
LabelListCreateAPIEndpoint,
LabelDetailAPIEndpoint,
IssueLinkListCreateAPIEndpoint,
IssueLinkDetailAPIEndpoint,
IssueCommentListCreateAPIEndpoint,
IssueCommentDetailAPIEndpoint,
IssueActivityListAPIEndpoint,
IssueActivityDetailAPIEndpoint,
IssueAttachmentListCreateAPIEndpoint,
IssueAttachmentDetailAPIEndpoint,
IssueSearchEndpoint,
)
from .cycle import (
CycleAPIEndpoint,
CycleIssueAPIEndpoint,
CycleListCreateAPIEndpoint,
CycleDetailAPIEndpoint,
CycleIssueListCreateAPIEndpoint,
CycleIssueDetailAPIEndpoint,
TransferCycleIssueAPIEndpoint,
CycleArchiveUnarchiveAPIEndpoint,
)
from .module import (
ModuleAPIEndpoint,
ModuleIssueAPIEndpoint,
ModuleListCreateAPIEndpoint,
ModuleDetailAPIEndpoint,
ModuleIssueListCreateAPIEndpoint,
ModuleIssueDetailAPIEndpoint,
ModuleArchiveUnarchiveAPIEndpoint,
)
from .member import ProjectMemberAPIEndpoint
from .member import ProjectMemberAPIEndpoint, WorkspaceMemberAPIEndpoint
from .intake import IntakeIssueAPIEndpoint
from .intake import (
IntakeIssueListCreateAPIEndpoint,
IntakeIssueDetailAPIEndpoint,
)
from .asset import UserAssetEndpoint, UserServerAssetEndpoint, GenericAssetEndpoint
from .user import UserEndpoint

Some files were not shown because too many files have changed in this diff Show More