-
Notifications
You must be signed in to change notification settings - Fork 3.2k
[WEB-5512]feat: workspace api tokens #8463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: preview
Are you sure you want to change the base?
Conversation
- Added WorkspaceAPITokenEndpoint for managing API tokens within specific workspaces. - Enhanced APITokenSerializer to associate tokens with workspaces and users. - Updated URL routing to include endpoints for workspace API tokens. - Introduced ServiceApiTokenEndpoint for handling service tokens linked to workspaces. - Created base views for API token operations, including create, read, update, and delete functionalities.
…e into feat-workspace-api-tokens
📝 WalkthroughWalkthroughThis pull request introduces workspace-scoped API token management, enabling administrators to create and manage API tokens bound to specific workspaces. The backend adds workspace validation in authentication middleware, dynamic rate limiting per workspace token, and new REST endpoints. The frontend provides a dedicated workspace settings page for token CRUD operations. Internationalization and services are updated to support the new workspace-scoped functionality. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Rationale: The PR spans backend authentication logic (middleware + rate limiting + new endpoints), frontend page & component refactoring, service layer additions, settings/routing configuration, and extensive i18n updates across 20+ locales. While individual changes follow consistent patterns, the heterogeneous scope (backend control flow, frontend UI state, new service abstractions, and translation consistency) requires varied reasoning for each cohort. The authentication middleware changes and new endpoint implementations are logic-dense and warrant careful review. Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Linked to Plane Work Item(s) This comment was auto-generated by Plane |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/app/routes/core.ts (1)
257-283: Path conflict between new workspace API tokens route and existing redirectYou now have two route entries for the exact same path
:workspaceSlug/settings/api-tokens:
- Workspace settings page route under the settings layout (Lines 257‑283).
- Legacy redirect route sending the same path to
routes/redirects/core/api-tokens.tsx(Lines 395‑397).This duplication is likely to cause one of these behaviors (depending on how @react-router/dev resolves siblings):
- The redirect still fires and makes the new workspace API tokens page unreachable.
- Or the redirect becomes dead/never used, leaving stale behavior in the config.
Given the PR’s goal (workspace‑scoped API tokens), this path should probably resolve directly to the new workspace settings page, and the legacy redirect should either be deleted or updated to point somewhere else.
I recommend cleaning this up so there is a single, unambiguous route for
:workspaceSlug/settings/api-tokens.Proposed cleanup options
Option A — Remove legacy redirect if no longer needed:
- // API tokens redirect: /:workspaceSlug/settings/api-tokens - // → /:workspaceSlug/settings/account/api-tokens - route(":workspaceSlug/settings/api-tokens", "routes/redirects/core/api-tokens.tsx"),Option B — Keep redirect but move it to a different deprecated path (if you still need one), and let the new workspace settings route own
:workspaceSlug/settings/api-tokensexclusively.Also applies to: 395-397
apps/api/plane/app/views/api/base.py (2)
47-50: Add workspace filter to DELETE method for security.The DELETE method is missing the
workspace_id__isnull=Truefilter that was added to the GET methods. This could allow users to delete workspace-scoped tokens through the user token endpoint, bypassing workspace-level permissions.🔎 Proposed fix
def delete(self, request: Request, pk: str) -> Response: - api_token = APIToken.objects.get(user=request.user, pk=pk, is_service=False) + api_token = APIToken.objects.get(user=request.user, pk=pk, is_service=False, workspace_id__isnull=True) api_token.delete() return Response(status=status.HTTP_204_NO_CONTENT)
52-58: Add workspace filter to PATCH method for security.The PATCH method is missing the
workspace_id__isnull=Truefilter that was added to the GET methods. This could allow users to modify workspace-scoped tokens through the user token endpoint, bypassing workspace-level permissions.🔎 Proposed fix
def patch(self, request: Request, pk: str) -> Response: - api_token = APIToken.objects.get(user=request.user, pk=pk) + api_token = APIToken.objects.get(user=request.user, pk=pk, workspace_id__isnull=True) serializer = APITokenSerializer(api_token, data=request.data, partial=True) if serializer.is_valid(): serializer.save()
🧹 Nitpick comments (2)
apps/api/plane/api/rate_limit.py (1)
114-124: Consider extracting duplicated rate-limit header logic.The
allow_requestlogic for computingX-RateLimit-RemainingandX-RateLimit-Resetheaders is repeated acrossApiKeyRateThrottle,ServiceTokenRateThrottle, andWorkspaceTokenRateThrottle. Consider extracting this into a shared mixin or base class to reduce duplication and ensure consistency.apps/api/plane/api/middleware/api_authentication.py (1)
44-46: Consider reducing database writes forlast_usedupdates.Updating
last_usedon every successful authentication adds a write operation per request. For high-traffic endpoints, consider:
- Batching updates asynchronously
- Using a sampling approach (update every N requests)
- Caching with periodic flush
This is a minor performance consideration and may be acceptable depending on traffic patterns.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (48)
apps/api/plane/api/middleware/api_authentication.pyapps/api/plane/api/rate_limit.pyapps/api/plane/api/views/base.pyapps/api/plane/app/serializers/api.pyapps/api/plane/app/urls/api.pyapps/api/plane/app/views/__init__.pyapps/api/plane/app/views/api/__init__.pyapps/api/plane/app/views/api/base.pyapps/api/plane/app/views/api/service.pyapps/api/plane/app/views/api/workspace.pyapps/api/plane/db/migrations/0113_webhook_version.pyapps/api/plane/db/models/api.pyapps/api/plane/db/models/user.pyapps/api/plane/db/models/webhook.pyapps/api/plane/db/models/workspace.pyapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxapps/web/app/routes/core.tsapps/web/core/components/api-token/delete-token-modal.tsxapps/web/core/components/api-token/modal/create-token-modal.tsxapps/web/core/components/api-token/token-list-item.tsxapps/web/core/components/ui/loader/settings/api-token.tsxapps/web/core/constants/fetch-keys.tspackages/constants/src/event-tracker/core.tspackages/constants/src/settings.tspackages/constants/src/workspace.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/en/translations.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ko/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/sk/translations.tspackages/i18n/src/locales/tr-TR/translations.tspackages/i18n/src/locales/ua/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/zh-TW/translations.tspackages/services/src/developer/index.tspackages/services/src/developer/workspace-api-token.service.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}
📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)
**/*.{ts,tsx,mts,cts}: Useconsttype parameters for more precise literal inference in TypeScript 5.0+
Use thesatisfiesoperator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicitisreturn types in filter/check functions
UseNoInfer<T>utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing inswitch(true)blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacyexperimentalDecorators
Useusingdeclarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Usewith { type: "json" }for import attributes; avoid deprecatedassertsyntax (TypeScript 5.3/5.8+)
Useimport typeexplicitly when importing types to ensure they are erased during compilation, respectingverbatimModuleSyntaxflag
Use.ts,.mts,.ctsextensions inimport typestatements (TypeScript 5.2+)
Useimport type { Type } from "mod" with { "resolution-mode": "import" }for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize newSetmethods likeunion,intersection, etc., when available (TypeScript 5.5+)
UseObject.groupBy/Map.groupBystandard methods for grouping instead of external libraries (TypeScript 5.4+)
UsePromise.withResolvers()for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted,toSpliced,with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields viasuperin classes (TypeScript 5....
Files:
apps/web/core/constants/fetch-keys.tspackages/services/src/developer/index.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ua/translations.tsapps/web/core/components/ui/loader/settings/api-token.tsxpackages/constants/src/workspace.tspackages/constants/src/settings.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxpackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/routes/core.tsapps/web/core/components/api-token/token-list-item.tsxpackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/constants/src/event-tracker/core.tspackages/i18n/src/locales/ru/translations.tspackages/services/src/developer/workspace-api-token.service.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/sk/translations.tsapps/web/core/components/api-token/delete-token-modal.tsxpackages/i18n/src/locales/tr-TR/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxpackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/pt-BR/translations.tsapps/web/core/components/api-token/modal/create-token-modal.tsxpackages/i18n/src/locales/en/translations.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Enable TypeScript strict mode and ensure all files are fully typed
Files:
apps/web/core/constants/fetch-keys.tspackages/services/src/developer/index.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ua/translations.tsapps/web/core/components/ui/loader/settings/api-token.tsxpackages/constants/src/workspace.tspackages/constants/src/settings.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxpackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/routes/core.tsapps/web/core/components/api-token/token-list-item.tsxpackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/constants/src/event-tracker/core.tspackages/i18n/src/locales/ru/translations.tspackages/services/src/developer/workspace-api-token.service.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/sk/translations.tsapps/web/core/components/api-token/delete-token-modal.tsxpackages/i18n/src/locales/tr-TR/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxpackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/pt-BR/translations.tsapps/web/core/components/api-token/modal/create-token-modal.tsxpackages/i18n/src/locales/en/translations.ts
**/*.{js,jsx,ts,tsx,json,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Prettier with Tailwind plugin for code formatting, run
pnpm fix:format
Files:
apps/web/core/constants/fetch-keys.tspackages/services/src/developer/index.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ua/translations.tsapps/web/core/components/ui/loader/settings/api-token.tsxpackages/constants/src/workspace.tspackages/constants/src/settings.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxpackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/routes/core.tsapps/web/core/components/api-token/token-list-item.tsxpackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/constants/src/event-tracker/core.tspackages/i18n/src/locales/ru/translations.tspackages/services/src/developer/workspace-api-token.service.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/sk/translations.tsapps/web/core/components/api-token/delete-token-modal.tsxpackages/i18n/src/locales/tr-TR/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxpackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/pt-BR/translations.tsapps/web/core/components/api-token/modal/create-token-modal.tsxpackages/i18n/src/locales/en/translations.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately
Files:
apps/web/core/constants/fetch-keys.tspackages/services/src/developer/index.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ua/translations.tsapps/web/core/components/ui/loader/settings/api-token.tsxpackages/constants/src/workspace.tspackages/constants/src/settings.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxpackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/routes/core.tsapps/web/core/components/api-token/token-list-item.tsxpackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/constants/src/event-tracker/core.tspackages/i18n/src/locales/ru/translations.tspackages/services/src/developer/workspace-api-token.service.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/sk/translations.tsapps/web/core/components/api-token/delete-token-modal.tsxpackages/i18n/src/locales/tr-TR/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxpackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/pt-BR/translations.tsapps/web/core/components/api-token/modal/create-token-modal.tsxpackages/i18n/src/locales/en/translations.ts
🧠 Learnings (12)
📚 Learning: 2025-12-12T15:20:36.542Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T15:20:36.542Z
Learning: Applies to **/package.json : Use `workspace:*` for internal packages and `catalog:` for external dependencies in imports
Applied to files:
apps/web/core/constants/fetch-keys.tspackages/services/src/developer/index.ts
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghanta
Repo: makeplane/plane PR: 7697
File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13
Timestamp: 2025-09-02T08:14:49.260Z
Learning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.
Applied to files:
packages/services/src/developer/index.ts
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxapps/web/core/components/api-token/token-list-item.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxapps/web/core/components/api-token/modal/create-token-modal.tsx
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.
Applied to files:
apps/api/plane/api/middleware/api_authentication.py
📚 Learning: 2025-12-23T14:18:32.899Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/models/api.py:35-35
Timestamp: 2025-12-23T14:18:32.899Z
Learning: Django REST Framework rate limit strings are flexible: only the first character of the time unit matters. Acceptable formats include: "60/s", "60/sec", "60/second" (all equivalent), "60/m", "60/min", "60/minute" (all equivalent), "60/h", "60/hr", "60/hour" (all equivalent), and "60/d", "60/day" (all equivalent). Abbreviations like "min" are valid and do not need to be changed to "minute". Apply this guidance to any Python files in the project that configure DRF throttling rules.
Applied to files:
apps/api/plane/api/middleware/api_authentication.pyapps/api/plane/api/views/base.pyapps/api/plane/app/views/api/__init__.pyapps/api/plane/app/views/__init__.pyapps/api/plane/db/models/workspace.pyapps/api/plane/api/rate_limit.pyapps/api/plane/db/models/api.pyapps/api/plane/db/models/user.pyapps/api/plane/app/views/api/workspace.pyapps/api/plane/app/views/api/base.pyapps/api/plane/app/serializers/api.pyapps/api/plane/db/migrations/0113_webhook_version.pyapps/api/plane/app/views/api/service.pyapps/api/plane/app/urls/api.pyapps/api/plane/db/models/webhook.py
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxapps/web/core/components/api-token/token-list-item.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsxapps/web/core/components/api-token/modal/create-token-modal.tsx
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx
📚 Learning: 2025-05-14T13:16:23.323Z
Learnt from: vamsikrishnamathala
Repo: makeplane/plane PR: 7061
File: web/core/components/workspace-notifications/root.tsx:18-18
Timestamp: 2025-05-14T13:16:23.323Z
Learning: In the Plane project codebase, the path alias `@/plane-web` resolves to the `ce` directory, making imports like `@/plane-web/hooks/use-notification-preview` correctly resolve to files in `web/ce/hooks/`.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx
📚 Learning: 2025-12-29T08:58:39.209Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/migrations/0113_webhook_version.py:7-14
Timestamp: 2025-12-29T08:58:39.209Z
Learning: In the Plane codebase, when adding product tour or onboarding fields via migrations, it's intentional to set existing records to `True` (completed) while having the model default to `False` for new records. This ensures existing users don't see tours they don't need.
Applied to files:
apps/api/plane/db/models/workspace.pyapps/api/plane/db/models/user.pyapps/api/plane/db/migrations/0113_webhook_version.py
📚 Learning: 2025-10-09T22:12:26.424Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T22:12:26.424Z
Learning: When `types/react` is installed in a TypeScript project (which is standard for React + TypeScript codebases), React types (React.FC, React.ReactNode, React.ComponentProps, etc.) are globally available by design. These type annotations can and should be used without explicitly importing the React namespace. This is a TypeScript/DefinitelyTyped feature, not codebase-specific configuration.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx
📚 Learning: 2025-12-12T15:20:36.542Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-12T15:20:36.542Z
Learning: Applies to packages/shared-state/**/*.{ts,tsx} : Maintain MobX stores in `packages/shared-state` using reactive patterns
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx
📚 Learning: 2025-03-11T19:42:41.769Z
Learnt from: janreges
Repo: makeplane/plane PR: 6743
File: packages/i18n/src/store/index.ts:160-161
Timestamp: 2025-03-11T19:42:41.769Z
Learning: In the Plane project, the file 'packages/i18n/src/store/index.ts' already includes support for Polish language translations with the case "pl".
Applied to files:
packages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/cs/translations.ts
🧬 Code graph analysis (15)
apps/web/core/constants/fetch-keys.ts (1)
apps/space/core/store/publish/publish.store.ts (1)
workspaceSlug(93-95)
packages/constants/src/settings.ts (1)
packages/constants/src/workspace.ts (1)
WORKSPACE_SETTINGS(74-117)
apps/api/plane/api/middleware/api_authentication.py (3)
apps/api/plane/db/models/api.py (1)
APIToken(19-44)apps/api/plane/db/models/workspace.py (1)
Workspace(125-188)apps/api/plane/tests/conftest.py (1)
api_token(46-53)
apps/api/plane/api/views/base.py (2)
apps/api/plane/api/rate_limit.py (3)
ApiKeyRateThrottle(9-47)ServiceTokenRateThrottle(50-88)WorkspaceTokenRateThrottle(91-126)apps/api/plane/db/models/api.py (1)
APIToken(19-44)
apps/api/plane/app/views/api/__init__.py (3)
apps/api/plane/app/views/api/base.py (1)
ApiTokenEndpoint(16-58)apps/api/plane/app/views/api/service.py (1)
ServiceApiTokenEndpoint(15-37)apps/api/plane/app/views/api/workspace.py (1)
WorkspaceAPITokenEndpoint(18-59)
apps/api/plane/app/views/__init__.py (2)
apps/api/plane/app/views/api/base.py (1)
ApiTokenEndpoint(16-58)apps/api/plane/app/views/api/workspace.py (1)
WorkspaceAPITokenEndpoint(18-59)
packages/services/src/developer/workspace-api-token.service.ts (2)
packages/constants/src/endpoints.ts (1)
API_BASE_URL(1-1)apps/space/core/store/publish/publish.store.ts (1)
workspaceSlug(93-95)
apps/api/plane/api/rate_limit.py (1)
apps/api/plane/db/models/api.py (1)
APIToken(19-44)
apps/web/core/components/api-token/delete-token-modal.tsx (4)
packages/services/src/developer/workspace-api-token.service.ts (1)
WorkspaceAPITokenService(10-73)apps/space/core/store/publish/publish.store.ts (1)
workspaceSlug(93-95)apps/web/core/constants/fetch-keys.ts (2)
WORKSPACE_API_TOKENS_LIST(146-147)API_TOKENS_LIST(145-145)packages/constants/src/event-tracker/core.ts (2)
WORKSPACE_SETTINGS_TRACKER_EVENTS(475-489)PROFILE_SETTINGS_TRACKER_EVENTS(434-448)
apps/api/plane/db/models/user.py (1)
apps/api/plane/utils/exporters/schemas/base.py (1)
BooleanField(97-105)
apps/api/plane/app/views/api/workspace.py (4)
apps/api/plane/app/serializers/api.py (2)
APITokenSerializer(12-23)APITokenReadSerializer(26-36)apps/api/plane/utils/permissions/workspace.py (1)
WorkSpaceAdminPermission(57-67)apps/api/plane/app/views/api/base.py (3)
post(17-35)get(37-45)delete(47-50)apps/api/plane/tests/conftest.py (1)
api_token(46-53)
apps/api/plane/app/views/api/base.py (3)
apps/api/plane/db/models/api.py (1)
APIToken(19-44)apps/api/plane/app/serializers/api.py (1)
APITokenReadSerializer(26-36)apps/api/plane/app/views/api/workspace.py (1)
get(46-54)
apps/api/plane/db/migrations/0113_webhook_version.py (1)
apps/api/plane/db/models/workspace.py (2)
get_default_product_tour(115-122)WorkspaceUserProperties(313-355)
apps/web/core/components/api-token/modal/create-token-modal.tsx (4)
packages/services/src/developer/workspace-api-token.service.ts (1)
WorkspaceAPITokenService(10-73)apps/space/core/store/publish/publish.store.ts (1)
workspaceSlug(93-95)apps/web/core/constants/fetch-keys.ts (2)
WORKSPACE_API_TOKENS_LIST(146-147)API_TOKENS_LIST(145-145)packages/constants/src/event-tracker/core.ts (2)
WORKSPACE_SETTINGS_TRACKER_EVENTS(475-489)PROFILE_SETTINGS_TRACKER_EVENTS(434-448)
apps/api/plane/app/urls/api.py (1)
apps/api/plane/app/views/api/workspace.py (1)
WorkspaceAPITokenEndpoint(18-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build packages
🔇 Additional comments (62)
packages/i18n/src/locales/id/translations.ts (1)
1723-1723: LGTM! Translation correctly reflects workspace-scoped tokens.The Indonesian translation has been appropriately updated to specify "workspace access token" instead of a generic API token, aligning with the PR's implementation of workspace-scoped API token management.
packages/i18n/src/locales/it/translations.ts (1)
1727-1727: LGTM! Translation accurately reflects workspace-scoped API tokens.The Italian translation correctly updates the label from a generic "Add API token" to the more specific "Add workspace access token", properly reflecting the workspace-scoped nature of the API tokens feature being implemented in this PR.
packages/i18n/src/locales/ko/translations.ts (1)
1706-1706: LGTM! Translation update aligns with workspace API token feature.The Korean translation has been appropriately updated from "API 토큰 추가" (Add API token) to "워크스페이스 액세스 토큰 추가" (Add workspace access token), which accurately reflects the workspace-scoped nature of the new token functionality introduced in this PR.
packages/i18n/src/locales/sk/translations.ts (1)
1714-1714: LGTM! Translation aligns with workspace API tokens feature.The updated Slovak translation correctly reflects the workspace-scoped nature of the API tokens being implemented in this PR. The change from "Pridať API token" (Add API token) to "Pridať token prístupu k pracovnému priestoru" (Add workspace access token) provides better clarity and specificity for users.
packages/i18n/src/locales/pt-BR/translations.ts (1)
1735-1735: LGTM! Translation improvement enhances clarity.The updated translation from "Adicionar token de API" to "Adicionar token de acesso ao espaço de trabalho" is more specific and accurately reflects that these are workspace-scoped tokens rather than generic API tokens. This aligns well with the PR's objective of implementing workspace API token functionality.
packages/i18n/src/locales/vi-VN/translations.ts (1)
1723-1723: LGTM! Translation improves clarity for workspace-scoped tokens.The updated Vietnamese translation now specifies "Thêm token truy cập không gian làm việc" (Add workspace access token) instead of the generic "Thêm token API" (Add API token). This change better reflects the workspace-scoped nature of these tokens and aligns with the PR's objective to implement workspace API token management.
The terminology is consistent with other workspace-related translations throughout this file that use "không gian làm việc" for workspace.
packages/i18n/src/locales/ru/translations.ts (1)
1717-1717: LGTM! Translation accurately reflects workspace-scoped API tokens.The Russian translation has been appropriately updated from "Добавить то��ен" (Add token) to "Добавить токен доступа к рабочему пространству" (Add workspace access token). This change is grammatically correct, natural in Russian, and aligns perfectly with the PR's objective of implementing workspace-scoped API tokens.
packages/i18n/src/locales/tr-TR/translations.ts (1)
1723-1723: LGTM! Translation correctly updated for workspace-scoped tokens.The Turkish translation accurately reflects the workspace access token terminology introduced in this PR. The grammar and apostrophe usage with "token'ı" follows proper Turkish orthography conventions.
packages/i18n/src/locales/zh-TW/translations.ts (1)
1695-1695: LGTM! Translation update correctly reflects workspace-scoped token terminology.The updated translation from "新增 API 權杖" to "新增工作區存取權杖" (Add Workspace Access Token) accurately reflects the workspace-scoped nature of the API tokens introduced in this PR. The translation is grammatically correct and consistent with other workspace-related terminology used throughout the file.
packages/i18n/src/locales/zh-CN/translations.ts (1)
1692-1694: Translation correctly implements workspace-scoped API tokens across all locales.The update from "添加 API 令牌" to "添加工作区访问令牌" appropriately adds workspace context and is consistently applied across all 19+ locale files, ensuring uniform labeling throughout the application.
packages/i18n/src/locales/pl/translations.ts (1)
1715-1715: LGTM! Translation accurately reflects workspace-scoped API tokens.The updated Polish translation correctly changes from a generic "API token" to "workspace access token," aligning with the PR's workspace-scoped API token implementation. The terminology "obszar roboczy" (workspace) is consistent throughout the file. This change is part of a coordinated translation update across all 18+ language files in the project, confirming the consistency of the effort.
The new translation is notably longer (43 vs 19 characters). Consider verifying that this text displays properly in the UI, especially in buttons or constrained layouts, though the proportional length increase is consistent with the corresponding changes in other languages.
packages/i18n/src/locales/cs/translations.ts (1)
1712-1712: LGTM! Translation updated to reflect workspace-scoped tokens.The Czech translation has been correctly updated from "Add API token" to "Add workspace access token", aligning with the broader workspace API tokens feature introduced in this PR.
apps/api/plane/app/serializers/api.py (1)
1-6: LGTM! Import organization improved.The explicit imports for
django.utils.timezoneandrest_framework.serializersimprove code clarity with no functional changes to the serializers.apps/api/plane/db/models/workspace.py (2)
115-122: LGTM! Product tour defaults correctly configured.The
get_default_product_tour()function appropriately returnsFalsefor all tour flags on new records, while the migration (0113_webhook_version.py) sets existing records toTrueto mark tours as completed for existing users.Based on learnings, this pattern ensures existing users don't see tours they don't need.
338-338: LGTM! Product tour field addition.The
product_tourJSONField correctly uses theget_default_product_tourfunction as its default, enabling per-user tracking of product tour completion across different features.packages/i18n/src/locales/es/translations.ts (1)
1737-1737: LGTM! Spanish translation updated for workspace tokens.The translation has been correctly updated from "Add API token" to "Add workspace access token", maintaining consistency with the workspace API tokens feature across all supported locales.
apps/api/plane/db/models/api.py (1)
35-35: LGTM! Rate limit field addition.The
allowed_rate_limitfield with default"60/min"correctly supports per-token rate limiting for workspace API tokens. The format is valid for Django REST Framework throttling.Based on learnings, DRF rate limit abbreviations like "min" are valid and equivalent to "minute".
packages/i18n/src/locales/fr/translations.ts (1)
1734-1753: Workspace API token label update looks goodThe new wording clearly indicates a workspace-scoped access token and remains consistent with surrounding API token copy.
packages/i18n/src/locales/ja/translations.ts (1)
1711-1731: Japanese workspace token label aligns with new semanticsThe updated
add_tokenstring correctly clarifies this as a workspace access token while remaining consistent with the surrounding API token copy.apps/api/plane/app/urls/api.py (1)
2-30: Workspace API token endpoints are wired consistentlyThe new import and URL patterns cleanly extend the existing token API surface:
workspaces/<str:slug>/api-tokens/andworkspaces/<str:slug>/api-tokens/<uuid:pk>/map toWorkspaceAPITokenEndpoint, matching its(slug, pk?)signature.- Naming (
workspace-api-tokens,workspace-api-tokens-details) follows the existingapi-tokensconventions.One thing to double‑check outside this file:
WorkspaceAPITokenEndpointcurrently exposesPOST/GET/DELETEonly. If you ever need to edit workspace tokens (e.g., label or expiry) similar to user tokens, you’ll also want aPATCHhandler and corresponding expectations on the client.packages/i18n/src/locales/ua/translations.ts (1)
1716-1735: Ukrainian workspace token label matches new conceptThe updated
add_tokentext clearly communicates that this is a workspace access token and stays consistent with the rest of the workspace settings section.apps/api/plane/app/views/__init__.py (1)
164-165: Publicly exportingWorkspaceAPITokenEndpointis consistentRe‑exporting
WorkspaceAPITokenEndpointalongsideApiTokenEndpointandServiceApiTokenEndpointkeeps the views package’s public surface coherent and simplifies imports for URL configuration.apps/api/plane/db/models/webhook.py (1)
41-41: LGTM! Version field addition looks good.The addition of the
versionfield to the Webhook model is straightforward and follows Django model conventions. The default value of "v1" and max_length of 50 are appropriate for version tracking.packages/services/src/developer/index.ts (1)
3-3: LGTM! Service export follows existing patterns.The re-export of the workspace API token service is consistent with the existing service exports in this file and appropriately extends the public API surface.
packages/i18n/src/locales/de/translations.ts (1)
1732-1732: LGTM! Translation update aligns with workspace-scoped tokens.The translation change from "API-Token hinzufügen" to "Workspace-Zugriffstoken hinzufügen" (Add API Token → Add Workspace Access Token) appropriately reflects the workspace-scoped nature of the new token functionality.
apps/api/plane/db/models/user.py (1)
236-237: LGTM! Navigation tour field follows established patterns.The addition of
is_navigation_tour_completedfollows the established pattern for product tour fields in the Profile model. The default ofFalseis appropriate for new users, with existing users being handled via migration.Based on learnings, this aligns with the project's approach to tour/onboarding fields.
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx (1)
2-2: LGTM! Icon addition is appropriate and consistent.The addition of the
KeyRoundicon for API tokens is:
- Semantically appropriate for representing API tokens/keys
- Consistent with the existing icon mapping pattern
- Properly imported from lucide-react
Also applies to: 28-28
apps/web/core/constants/fetch-keys.ts (1)
146-147: LGTM! Fetch key follows established patterns.The
WORKSPACE_API_TOKENS_LISTfunction follows the same pattern as other workspace-scoped fetch keys in this file, using consistent naming conventions and thetoUpperCase()transformation.apps/api/plane/app/views/api/__init__.py (1)
1-3: LGTM! Clean API surface for token endpoints.The module consolidates token endpoint exports appropriately, following standard Python package patterns and creating a clean public API surface.
apps/api/plane/app/views/api/base.py (1)
37-45: LGTM! GET methods properly filter workspace tokens.The addition of
workspace_id__isnull=Trueto both the list and detail GET methods ensures that workspace-scoped tokens are not exposed through the user API token endpoints. This is a critical security enhancement that properly separates user tokens from workspace tokens.apps/web/core/components/api-token/token-list-item.tsx (2)
25-27: LGTM!The conditional tracker element selection correctly distinguishes between workspace-scoped and profile-scoped contexts. The ternary expression is clear and the tracker constants are appropriately named.
31-36: LGTM!The
workspaceSlugprop is correctly propagated toDeleteApiTokenModal, enabling workspace-scoped token deletion when applicable.packages/constants/src/settings.ts (1)
40-40: LGTM!The
api-tokenssetting is correctly added to theDEVELOPERcategory, consistent with the workspace settings structure.packages/constants/src/workspace.ts (2)
110-116: LGTM!The new
api-tokensworkspace setting follows the established pattern with correct access control (admin-only), consistent href structure, and proper highlight function. The i18n label key aligns with the translation structure.
135-135: LGTM!The
api-tokensentry is correctly appended toWORKSPACE_SETTINGS_LINKS, making it available in workspace settings navigation.apps/web/core/components/ui/loader/settings/api-token.tsx (1)
3-12: LGTM!Good refactor to accept
titleas a prop instead of hardcoding the translation. This makes the loader reusable across different contexts (workspace and profile API tokens pages) while delegating translation responsibility to the caller.apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/api-tokens/page.tsx (4)
26-26: Module-level service instantiation is acceptable here.The
WorkspaceAPITokenServiceis instantiated at module level. This is a common pattern for stateless services and is fine since the service likely doesn't hold request-specific state. Just ensure the service class is designed to be reused across requests.
41-44: LGTM!The SWR conditional fetching pattern is correctly implemented—both the key and fetcher are
nullwhen the user lacks admin permissions, preventing unauthorized API calls while maintaining the hooks' call order.
50-52: LGTM!Good guard clause: returns
NotAuthorizedViewwhen user info is loaded but lacks admin permissions. TheworkspaceUserInfocheck ensures the permission state is determined before rendering the unauthorized view.
79-86: Consider adding a key prop stability check.The token list renders correctly with
token.idas the key. The implementation is clean.apps/api/plane/api/middleware/api_authentication.py (1)
48-49: All workspace-scoped API routes consistently useslugas the URL parameter name, making the middleware's assumption correct. No action needed.Likely an incorrect or invalid review comment.
apps/api/plane/api/views/base.py (1)
63-75: LGTM!The throttle selection logic is well-structured:
- Single database query for the base queryset
- In-memory filtering for service and workspace tokens
- Clear priority: service tokens → workspace tokens → default API key throttle
- Early returns prevent unnecessary checks
Note: While the APIToken model does not enforce mutual exclusivity between
is_service=Trueandworkspace_idat the database level, the current logic handles this correctly by prioritizing service tokens over workspace tokens, so any ambiguous token would be treated as a service token as intended.apps/api/plane/api/rate_limit.py (1)
102-110: Database query on every request may impact performance.The
allow_requestmethod queries the database for the token'sallowed_rate_limiton every request. Consider caching the rate limit (e.g., in Django's cache framework with the API key as the cache key) to reduce database load under high traffic.apps/web/core/components/api-token/modal/create-token-modal.tsx (3)
1-26: LGTM! Imports and service initialization are correct.The workspace-scoped token support is properly set up with appropriate imports, props type extension, and service instantiation.
54-70: LGTM! API call selection and cache mutation are correctly implemented.The conditional logic properly selects the appropriate service and mutation key based on whether the token is workspace-scoped or global.
71-95: LGTM! Event tracking properly differentiates workspace vs. profile contexts.Success and error event tracking correctly uses workspace-specific or profile-specific event names based on the context, ensuring accurate analytics.
packages/services/src/developer/workspace-api-token.service.ts (3)
1-13: LGTM! Class structure and constructor follow established patterns.The service class correctly extends APIService and follows the same initialization pattern as other service classes in the codebase.
21-42: LGTM! CRUD methods are correctly implemented.The list and retrieve methods properly construct workspace-scoped endpoints and handle responses and errors consistently.
51-72: LGTM! Create and destroy methods are correctly implemented.Both methods properly construct workspace-scoped endpoints, handle payloads appropriately, and follow consistent error handling patterns.
apps/web/core/components/api-token/delete-token-modal.tsx (3)
1-26: LGTM! Setup mirrors create-token-modal pattern correctly.Imports, props extension, and service instantiation follow the same pattern established in the create modal, ensuring consistency.
37-56: LGTM! Deletion logic and cache management are correct.The conditional service selection and cache mutation properly handle both workspace-scoped and global token deletion.
57-86: LGTM! Event tracking properly differentiates contexts.Success and error tracking correctly uses workspace-specific or profile-specific events, maintaining consistency with the create modal implementation.
packages/i18n/src/locales/en/translations.ts (2)
1410-1413: LGTM! Translation key renamed for consistency.The key rename from
headingtotitlein account settings maintains the same display text while improving naming consistency.
1573-1578: LGTM! Workspace token translations properly added.The new translation keys clearly differentiate workspace tokens from personal tokens with appropriate labels:
- "Workspace Access Tokens" for heading/title
- "Add Workspace access token" for the action button
packages/constants/src/event-tracker/core.ts (1)
486-508: LGTM! Workspace-scoped event tracking constants properly added.The new PAT events and UI element identifiers correctly distinguish workspace-level token tracking from profile-level tracking, with appropriate prefixing to avoid naming collisions.
apps/api/plane/app/views/api/workspace.py (1)
18-21: LGTM! Permission class correctly restricts access.The WorkSpaceAdminPermission correctly restricts token management to workspace admins and members, which is appropriate for workspace-scoped API tokens.
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx (3)
1-40: LGTM! Component structure and loading state are correct.The component properly uses FC type, mobx observer, and handles the loading state with appropriate translation keys for account-scoped tokens.
46-58: LGTM! Header with button correctly configured.The SettingsHeading properly includes an add-token button with correct event tracking for the profile/account context (no workspaceSlug passed to modal).
59-86: LGTM! Token list and empty state are correctly implemented.The conditional rendering properly shows either the token list or an appropriately configured empty state, with correct event tracking for the account context.
apps/api/plane/db/migrations/0113_webhook_version.py (3)
7-21: LGTM! Product tour default correctly set for existing users.The migration sets existing records to
True(completed) while the model default isFalsefor new records. This is intentional to prevent existing users from seeing tours they don't need.Based on learnings, this pattern is standard in the Plane codebase.
17-21: LGTM! Data migration efficiently populates product tour.The bulk update approach is performant and correctly sets the product tour field for all existing workspace user properties records.
30-52: LGTM! All field additions are correctly configured.The migration adds four fields with appropriate defaults:
webhook.version: versioning support for webhooksprofile.is_navigation_tour_completed: tracking navigation tour completionworkspaceuserproperties.product_tour: feature tour tracking with correct default patternapitoken.allowed_rate_limit: rate limit configuration (format is valid per DRF standards)The RunPython operation runs after field additions, ensuring correct execution order.
Based on learnings, the rate limit format '60/min' is valid for DRF throttling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/api/plane/app/views/api/workspace.py (1)
23-44: Add error handling for workspace lookup.Line 31 uses
Workspace.objects.get(slug=slug)without error handling. If the workspace doesn't exist, this will raiseDoesNotExistand return a 500 error instead of a proper 404 response.🔎 Recommended fix using get_object_or_404
+from django.shortcuts import get_object_or_404 + class WorkspaceAPITokenEndpoint(BaseAPIView): ... def post(self, request: Request, slug: str) -> Response: label = request.data.get("label", str(uuid4().hex)) description = request.data.get("description", "") expired_at = request.data.get("expired_at", None) # Check the user type user_type = 1 if request.user.is_bot else 0 - workspace = Workspace.objects.get(slug=slug) + workspace = get_object_or_404(Workspace, slug=slug) api_token = APIToken.objects.create( label=label, description=description, user=request.user, user_type=user_type, expired_at=expired_at, workspace=workspace, ) serializer = APITokenSerializer(api_token) return Response(serializer.data, status=status.HTTP_201_CREATED)
🧹 Nitpick comments (1)
apps/api/plane/app/views/api/workspace.py (1)
56-62: Fix variable naming inconsistency.The variable
api_tokens(plural) should beapi_token(singular) when retrieving a single object for clarity.🔎 Proposed refactor
else: try: - api_tokens = APIToken.objects.get(workspace__slug=slug, pk=pk, user=request.user) + api_token = APIToken.objects.get(workspace__slug=slug, pk=pk, user=request.user) except APIToken.DoesNotExist: return Response({"error": "API token does not exist"}, status=status.HTTP_404_NOT_FOUND) - serializer = APITokenReadSerializer(api_tokens) + serializer = APITokenReadSerializer(api_token) return Response(serializer.data, status=status.HTTP_200_OK)
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/api/plane/app/views/api/workspace.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.
Applied to files:
apps/api/plane/app/views/api/workspace.py
📚 Learning: 2025-12-23T14:18:32.899Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/models/api.py:35-35
Timestamp: 2025-12-23T14:18:32.899Z
Learning: Django REST Framework rate limit strings are flexible: only the first character of the time unit matters. Acceptable formats include: "60/s", "60/sec", "60/second" (all equivalent), "60/m", "60/min", "60/minute" (all equivalent), "60/h", "60/hr", "60/hour" (all equivalent), and "60/d", "60/day" (all equivalent). Abbreviations like "min" are valid and do not need to be changed to "minute". Apply this guidance to any Python files in the project that configure DRF throttling rules.
Applied to files:
apps/api/plane/app/views/api/workspace.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: check:lint
- GitHub Check: check:types
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/api/plane/app/views/api/workspace.py (2)
1-21: LGTM!Imports are well-organized and the permission class appropriately restricts access to workspace administrators.
64-71: LGTM!The DELETE method correctly implements error handling with try-except and returns appropriate status codes (404 for not found, 204 for successful deletion). The filters properly enforce security by checking workspace, user, and service token status.
|
#8339 needs to be merged before this, as it contains the allowed_rate_limit field, which is required here. |
…e into feat-workspace-api-tokens
a32412e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/i18n/src/locales/ko/translations.ts (1)
1704-1726: Inconsistent terminology within the API tokens section.The
api_tokenssection uses mixed terminology:
heading(line 1705) andtitle(line 1707) use "API 토큰" (API token)add_token(line 1708) uses "액세스 토큰 추가" (Add access token)For consistency and clarity, consider using the same term throughout this section. If "API 토큰" and "액세스 토큰" refer to the same concept, standardize on one term. If they represent different concepts, the distinction should be documented.
🔎 Suggested fix for consistency
Option 1: Standardize on "API 토큰":
- add_token: "액세스 토큰 추가", + add_token: "API 토큰 추가",Option 2: Standardize on "액세스 토큰":
- heading: "API 토큰", + heading: "액세스 토큰", description: "보안 API 토큰을 생성하여 데이터를 외부 시스템 및 애플리케이션과 통합합니다.", - title: "API 토큰", + title: "액세스 토큰",
♻️ Duplicate comments (1)
packages/i18n/src/locales/ro/translations.ts (1)
1726-1733: Update the entireapi_tokenssection to use consistent "workspace access token" terminology.The Romanian translation has a terminology inconsistency that was previously identified. The section mixes old "cheie secretă API" (API secret key) terminology with the newer "token de acces" (access token) terminology, but neither aligns with the workspace-scoped API token feature introduced in this PR.
Current inconsistencies:
- heading (line 1726): "Chei secrete API" → should reflect workspace tokens
- description (line 1727): "chei secrete API sigure" → should reflect workspace tokens
- add_token (line 1729): "Adaugă token de acces" → should be "token de acces la spațiul de lucru"
- create_token (line 1731): "Creează cheie secretă" → should reflect workspace token terminology
- generate_token (line 1733): "Generează cheie secretă" → should reflect workspace token terminology
All other locales (English, Czech, German, French, Spanish, Italian, Portuguese) have already made comprehensive updates to "Workspace Access Tokens" terminology. The Romanian translations should be updated consistently to match this internationalization pattern.
Recommended updates
api_tokens: { - heading: "Chei secrete API", - description: "Generează chei secrete API sigure pentru a integra datele tale cu sisteme și aplicații externe.", + heading: "Tokenuri de acces la spațiul de lucru", + description: "Generează tokenuri de acces sigure pentru a integra datele tale din spațiul de lucru cu sisteme și aplicații externe.", title: "Chei secrete API", - add_token: "Adaugă token de acces", - create_token: "Creează cheie secretă", + add_token: "Adaugă token de acces la spațiul de lucru", + create_token: "Creează token de acces la spațiul de lucru", never_expires: "Nu expiră niciodată", - generate_token: "Generează cheie secretă", + generate_token: "Generează token de acces la spațiul de lucru", generating: "Se generează",Also update the delete section strings (lines 1735-1737) to use "token de acces la spațiul de lucru" instead of "cheia secretă API".
🧹 Nitpick comments (1)
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsx (1)
41-44: Consider adding error handling for the SWR data fetch.The current implementation doesn't destructure or handle the
errorfromuseSWR. If the API call fails, users will see either a loading state indefinitely or stale data without any error feedback.🔎 Recommended addition for error handling
- const { data: tokens } = useSWR( + const { data: tokens, error } = useSWR( canPerformWorkspaceAdminActions ? WORKSPACE_API_TOKENS_LIST(workspaceSlug) : null, canPerformWorkspaceAdminActions ? () => workspaceApiTokenService.list(workspaceSlug) : null );Then add error state handling in the render logic:
{!tokens && !error ? ( <APITokenSettingsLoader title={t("workspace_settings.settings.api_tokens.title")} /> ) : error ? ( <div className="flex items-center justify-center h-full"> <EmptyStateCompact assetKey="error" title={t("common.error.label")} description={t("common.error.message")} /> </div> ) : ( // ... existing token list/empty state )}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsxapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsxapps/web/app/routes/core.tspackages/constants/src/settings.tspackages/constants/src/workspace.tspackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/de/translations.tspackages/i18n/src/locales/en/translations.tspackages/i18n/src/locales/es/translations.tspackages/i18n/src/locales/fr/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/it/translations.tspackages/i18n/src/locales/ja/translations.tspackages/i18n/src/locales/ko/translations.tspackages/i18n/src/locales/pl/translations.tspackages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/sk/translations.tspackages/i18n/src/locales/tr-TR/translations.tspackages/i18n/src/locales/ua/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/zh-CN/translations.tspackages/i18n/src/locales/zh-TW/translations.ts
🚧 Files skipped from review as they are similar to previous changes (14)
- packages/i18n/src/locales/tr-TR/translations.ts
- packages/i18n/src/locales/ja/translations.ts
- packages/constants/src/settings.ts
- packages/constants/src/workspace.ts
- packages/i18n/src/locales/sk/translations.ts
- packages/i18n/src/locales/pl/translations.ts
- packages/i18n/src/locales/ua/translations.ts
- packages/i18n/src/locales/en/translations.ts
- packages/i18n/src/locales/fr/translations.ts
- apps/web/app/routes/core.ts
- packages/i18n/src/locales/it/translations.ts
- packages/i18n/src/locales/zh-CN/translations.ts
- apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx
- packages/i18n/src/locales/es/translations.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}
📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)
**/*.{ts,tsx,mts,cts}: Useconsttype parameters for more precise literal inference in TypeScript 5.0+
Use thesatisfiesoperator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicitisreturn types in filter/check functions
UseNoInfer<T>utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing inswitch(true)blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacyexperimentalDecorators
Useusingdeclarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Usewith { type: "json" }for import attributes; avoid deprecatedassertsyntax (TypeScript 5.3/5.8+)
Useimport typeexplicitly when importing types to ensure they are erased during compilation, respectingverbatimModuleSyntaxflag
Use.ts,.mts,.ctsextensions inimport typestatements (TypeScript 5.2+)
Useimport type { Type } from "mod" with { "resolution-mode": "import" }for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize newSetmethods likeunion,intersection, etc., when available (TypeScript 5.5+)
UseObject.groupBy/Map.groupBystandard methods for grouping instead of external libraries (TypeScript 5.4+)
UsePromise.withResolvers()for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted,toSpliced,with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields viasuperin classes (TypeScript 5....
Files:
packages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsxpackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/de/translations.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Enable TypeScript strict mode and ensure all files are fully typed
Files:
packages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsxpackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/de/translations.ts
**/*.{js,jsx,ts,tsx,json,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Prettier with Tailwind plugin for code formatting, run
pnpm fix:format
Files:
packages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsxpackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/de/translations.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately
Files:
packages/i18n/src/locales/pt-BR/translations.tspackages/i18n/src/locales/ko/translations.tsapps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsxpackages/i18n/src/locales/cs/translations.tspackages/i18n/src/locales/vi-VN/translations.tspackages/i18n/src/locales/ru/translations.tspackages/i18n/src/locales/zh-TW/translations.tspackages/i18n/src/locales/ro/translations.tspackages/i18n/src/locales/id/translations.tspackages/i18n/src/locales/de/translations.ts
🧠 Learnings (1)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.
Applied to files:
apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (11)
packages/i18n/src/locales/pt-BR/translations.ts (1)
1734-1737: Verify terminology consistency between heading and button label.The section heading uses "Tokens de API" (API tokens) while the button label uses "token de acesso" (access token). This terminology mismatch could cause user confusion.
- Heading (line 1734): "Tokens de API"
- Button (line 1737): "Adicionar token de acesso"
Please confirm whether:
- This generic "access token" terminology is intentional to accommodate multiple token types
- Other locale files follow the same pattern
- The UI implementation consistently uses "access token" terminology
If the generic terminology is intentional, consider updating the heading to match (e.g., "Tokens de acesso") for consistency, or update the button to be more specific (e.g., "Adicionar token de API") to match the heading.
packages/i18n/src/locales/cs/translations.ts (1)
1711-1712: LGTM! New translation keys for API tokens section.The new
headinganddescriptionkeys appropriately support the workspace API tokens feature introduced in this PR. The structure and placement withinworkspace_settings.api_tokensare correct.packages/i18n/src/locales/zh-TW/translations.ts (2)
1694-1695: LGTM! New translation keys added for API tokens UI.The heading and description additions properly support the workspace API tokens feature. The structure follows existing i18n patterns in this file.
1697-1697: Terminology is consistent across all locales.The
add_tokentranslation to "新增存取權杖" (Add access token) aligns with the English source and is uniformly applied across all 20 locale files. The intentional distinction between the section heading "API 權杖" (API tokens) and the action label "存取權杖" (access token) is maintained consistently across all languages, confirming this is a deliberate product terminology design rather than an inconsistency.packages/i18n/src/locales/vi-VN/translations.ts (2)
1725-1725: LGTM: Terminology updated to "access token".The change from "Thêm token API" to "Thêm token truy cập" (Add access token) broadens the terminology and aligns with the UI text changes mentioned in the AI summary across multiple locales.
1722-1724: Bothheadingandtitlekeys are intentionally present and correctly placed.The
api_tokenssection withinworkspace_settingsconsistently includes both keys across all locales (confirmed in English:heading: "Access Tokens"andtitle: "Access Tokens"). They serve different UI purposes and no duplication exists. The structure and translations are correct.packages/i18n/src/locales/de/translations.ts (1)
1730-1753: Translations look good! The terminology distinction is intentional and consistent.The German translations are grammatically correct and professionally written. The new
headinganddescriptionstrings provide clear context for the API tokens feature.The distinction between "API-Tokens" (heading) and "Zugriffstoken" (add_token) is intentional and follows a consistent pattern across all locales. Each locale distinguishes between the feature concept (API tokens as a section header) and the specific user action (adding an access token). This improves user clarity by using more accessible terminology for the button action.
packages/i18n/src/locales/ru/translations.ts (1)
1716-1720: LGTM! Translation keys properly added for workspace API tokens.The Russian translations correctly add the new
headinganddescriptionkeys and update theadd_tokenlabel to use "токен доступа" (access token) terminology, aligning with the broader feature changes across locales.apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/access-tokens/page.tsx (3)
1-26: Clean imports and service instantiation pattern.The module-level service instantiation is appropriate here since
WorkspaceAPITokenServiceappears to be stateless and only receivesworkspaceSlugas a method parameter (line 43).
46-52: Proper authorization flow and page title handling.The permission check correctly waits for
workspaceUserInfoto load before showing the unauthorized view, while the SWR conditional fetching prevents data access attempts without permissions. The dynamic page title handles missing workspace data gracefully.
54-115: Well-structured component with proper event tracking and conditional rendering.The render logic correctly:
- Shows loading state while data fetches
- Integrates event tracking for user interactions (header button and empty state CTA)
- Conditionally renders token list or empty state based on data
- Properly passes
workspaceSlugto child components for workspace-scoped operations- Uses appropriate translation keys that align with the i18n updates in this PR
The MobX
observerwrapper (line 117) ensures reactive updates when workspace data changes.
| description: "Generujte bezpečné API tokeny pro integraci vašich dat s externími systémy a aplikacemi.", | ||
| title: "API Tokeny", | ||
| add_token: "Přidat API token", | ||
| add_token: "Přidat token přístupu", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, check the structure of the i18n locales directory
git ls-files packages/i18n/src/locales/ | head -20Repository: makeplane/plane
Length of output: 920
🏁 Script executed:
# Search for all token-related keys and values in the Czech translations
echo "=== Czech translations with 'token' ==="
rg -n "token" packages/i18n/src/locales/cs/translations.ts -iRepository: makeplane/plane
Length of output: 886
🏁 Script executed:
# Check what other locales use for similar token terminology
echo "=== Checking English locale for token terminology ==="
rg -n "token" packages/i18n/src/locales/en/translations.ts -i -A1
echo ""
echo "=== Checking German locale for token terminology ==="
rg -n "token" packages/i18n/src/locales/de/translations.ts -i -A1
echo ""
echo "=== Checking French locale for token terminology ==="
rg -n "token" packages/i18n/src/locales/fr/translations.ts -i -A1Repository: makeplane/plane
Length of output: 3417
🏁 Script executed:
# Count occurrences of "API token" vs "token přístupu" and other token variations in Czech
echo "=== Token terminology counts in Czech translations ==="
echo "Occurrences of 'API token':"
rg -c "API token" packages/i18n/src/locales/cs/translations.ts
echo "Occurrences of 'token přístupu':"
rg -c "token přístupu" packages/i18n/src/locales/cs/translations.ts
echo "All lines with 'token' (case-insensitive):"
rg -n "token" packages/i18n/src/locales/cs/translations.ts -iRepository: makeplane/plane
Length of output: 1009
Update remaining Czech token terminology for consistency with the new "token přístupu" terminology.
The change at line 1714 from "Přidat API token" to "Přidat token přístupu" aligns with English ("Add access token"), German ("Zugriffstoken"), and French ("jeton d'accès") locales. However, the Czech translations still contain inconsistent terminology:
- Line 1711:
heading: "API Tokeny"should be"Tokeny přístupu"or"Přístupové tokeny" - Line 1712-1713: References to "API tokeny" should be updated to match
- Line 1720:
title: "Smazat API token"should be"Smazat token přístupu" - Line 1735:
title: "Žádné API tokeny"should be"Žádné tokeny přístupu"
For reference, English uses consistent "Access Tokens" throughout the section while maintaining "API tokens" only in the description for technical context.
🤖 Prompt for AI Agents
In packages/i18n/src/locales/cs/translations.ts around lines 1711–1735, the
Czech locale uses mixed "API token" wording; update the strings to use the
consistent "token přístupu" terminology: change heading at ~1711 from "API
Tokeny" to "Tokeny přístupu" (or "Přístupové tokeny"), update the following
lines (~1712–1713) to replace any "API tokeny" occurrences with the chosen
"token přístupu" phrase, change the deletion title at ~1720 from "Smazat API
token" to "Smazat token přístupu", and change the empty-state title at ~1735
from "Žádné API tokeny" to "Žádné tokeny přístupu" so all labels match the new
terminology.
| heading: "Token API", | ||
| description: "Buat token API yang aman untuk mengintegrasikan data Anda dengan sistem dan aplikasi eksternal.", | ||
| title: "Token API", | ||
| add_token: "Tambah token API", | ||
| add_token: "Tambah token akses", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify terminology consistency in Indonesian translations for API tokens
echo "=== Checking 'Token API' occurrences in Indonesian translations ==="
rg -n "Token API" packages/i18n/src/locales/id/translations.ts
echo -e "\n=== Checking 'token akses' occurrences in Indonesian translations ==="
rg -n "token akses" packages/i18n/src/locales/id/translations.ts
echo -e "\n=== Checking full api_tokens section in Indonesian translations ==="
rg -nA15 "api_tokens:" packages/i18n/src/locales/id/translations.ts | head -30
echo -e "\n=== Checking English translation for comparison ==="
rg -nA15 "api_tokens:" packages/i18n/src/locales/en/translations.ts | head -30Repository: makeplane/plane
Length of output: 3447
Ensure consistent terminology in Indonesian API tokens translation.
The translation mixes "Token API" and "token akses" within the same feature section:
heading(line 1722): "Token API"title(line 1724): "Token API"add_token(line 1725): "Tambah token akses"delete.title(line 1731): "Hapus token API"
The English version uses "Access Tokens" consistently throughout. Either standardize the Indonesian on "Token API" or use "token akses" consistently to match the English terminology and improve UX clarity.
🤖 Prompt for AI Agents
packages/i18n/src/locales/id/translations.ts lines 1722-1725 (and related keys
around 1731): the Indonesian copy mixes "Token API" and "token akses" for the
same feature; pick one term and make all keys in this feature consistent (e.g.,
change add_token to "Tambah Token API" and change delete.title to "Hapus Token
API", or alternatively convert heading/title to use "token akses" if you prefer
that term), then update all occurrences in this section so the same phrase is
used everywhere and run a quick grep to ensure no mixed usages remain.
Description
Implementation of workspace api tokens.
Type of Change
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.