Vanilla 401 handler did `window.location.replace('/?next_path=<currentPath>')`.
That IS a hard nav, but the browser's HTTP cache returns the cached SPA
bundle for `/` — the SPA boots, re-fetches the same /api endpoint, gets 401
again, and loops without ever hitting Traefik at the document level. Diagnosed
2026-05-05 via HAR analysis: 9 history entries bouncing `/` ↔ `/?next_path=/`
at ~780ms intervals; zero requests to bridge or oauth2-proxy during the
loop; first bridge.binarybeach.io/handoff request only after Ctrl+Shift+R.
Trigger on the platform side: oauth2-proxy refresh fails for cross-org
gmail-federated users (separate root cause — disabled platform-wide via
OAUTH2_PROXY_OIDC_GROUPS_CLAIM=). The hard-nav fix here is the safety net
that handles that and any other future 401-causing scenario.
Replace with `window.location.replace('/sign-in/?_bb_reauth=<Date.now()>')`:
- /sign-in/ matches Plane's priority-200 plane-signin-redirect Traefik
router (matched on PathRegexp `^/(sign-in|sign-up|signin|login|register|
accounts/sign-in)(/.*)?$$`), which 302s to the bridge handoff regardless
of cookie state.
- _bb_reauth=<ts> cache-busts so even a previously-cached /sign-in/
response can't short-circuit the request.
Vanilla Plane regression-safe: /sign-in/ is also a known SPA route in
upstream that bounces to /, so non-platform deployments see the same
behavior they'd get without this patch (modulo a single extra navigation).
Also fixes BINARYBEACHIO.md frontend build instructions: Dockerfile.web
needs the monorepo root as build context (turbo prune scope), opposite of
Dockerfile.api which needs apps/api/ as context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
== WHY (KEEP THIS — IT'S WHY THE FORK EXISTS) ==
Vanilla Plane's upload flow uses AWS S3 PostObject (presigned POST +
multipart/form-data + signed-policy-document). Cloudflare R2 AND
Backblaze B2 — the two most common self-host S3-compatible backends —
both return HTTP 501 NotImplemented for PostObject. Empirically verified
2026-04-30 against B2 s3.us-west-004.backblazeb2.com from inside Plane's
own prod api container, replicating Plane's exact boto3 call:
PUT against B2: 200 OK
POST against B2: 501 NotImplemented "This API call is not supported."
POST against R2: 501 NotImplemented (failure that started this thread)
The error code is `NotImplemented` (not `SignatureDoesNotMatch` etc),
meaning the server rejects the verb itself — no boto3 config, addressing-
style flag, or signature variant fixes it. Tested both path-style and
virtual-hosted-style URLs against B2; both fail identically for POST.
This patch rewrites the upload flow to use presigned PUT, which is
universally supported (R2, B2, AWS S3 native, MinIO, Wasabi, etc).
== WHAT (FIVE-FILE BACKEND, FIVE-FILE FRONTEND) ==
Backend:
* apps/api/plane/settings/storage.py — S3Storage.generate_presigned_post
now mints a presigned PUT URL via generate_presigned_url(HttpMethod="PUT").
Method name kept for caller compat. Response shape:
{url, method: "PUT", fields: {Content-Type, key}}.
* apps/api/plane/utils/openapi/responses.py — example response updated.
* apps/api/plane/tests/unit/settings/test_storage.py — 2 tests updated to
assert the new boto3 call.
Frontend:
* packages/types/src/file.ts — TFileSignedURLResponse.upload_data adds
optional method?: "PUT" | "POST"; drops AWS POST-form-data fields.
* packages/services/src/file/helper.ts — generateFileUploadPayload now
returns a TFileUploadRequest descriptor (url+method+body+headers) that
dispatches on method. POST branch kept for upstream parity but the
fork backend never emits POST.
* packages/services/src/file/file-upload.service.ts +
apps/web/core/services/file-upload.service.ts — uploadFile signature
changes from (url, FormData, progress?) to (payload, progress?).
* 5 caller sites updated (apps/web/core/services/file.service.ts x3,
issue_attachment.service.ts x1, sites-file.service.ts x1).
== TRADEOFFS ACCEPTED ==
* Lost: signed `content-length-range` enforcement at the storage layer.
Server-side validation in the API view still rejects oversized requests
with 413 before minting the URL, so a determined client could only
over-upload by misreporting size, capped at the bucket's own size limit.
* Different request shape on the wire (PUT with raw binary body vs POST
with multipart form). Externally invisible to users.
== ROLLBACK ==
If this becomes a maintenance nightmare:
git revert <this-commit-sha>
# rebuild + push images, swap compose tags, redeploy
After revert, uploads will only work against backends that implement
PostObject (MinIO, AWS S3 native). R2 and B2 will return 501 again.
== FULL DECISION RECORD ==
binarybeachio repo: docs/features/storage-upload-flow.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: rename IssueUserProperty to ProjectUserProperty and update related references across the codebase
* migrate: move issue user properties to project user properties and update related fields and constraints
* refactor: rename IssueUserPropertySerializer and IssueUserDisplayPropertyEndpoint to ProjectUserPropertySerializer and ProjectUserDisplayPropertyEndpoint, updating all related references
* fix: enhance ProjectUserDisplayPropertyEndpoint to handle missing properties by creating new entries and improve response handling
* fix: correct formatting in migration for ProjectUserProperty model options
* migrate: add migration to update existing non-service API tokens to remove workspace association
* migrate: refine migration to update existing non-service API tokens by excluding bot users from workspace removal
* chore: changed the project sort order in project user property
* chore: remove allowed_rate_limit from APIToken
* chore: updated user-properties endpoint for frontend
* chore: removed the extra projectuserproperty
* chore: updated the migration file
* chore: code refactor
* fix: type error
---------
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sangeethailango <sangeethailango21@gmail.com>
Co-authored-by: vamsikrishnamathala <matalav55@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
* feat: implement cover image handling and static image selection
- Added functionality to handle cover image uploads and selections in project and profile forms.
- Introduced a new helper for managing cover images, including static images and uploaded assets.
- Updated components to utilize the new cover image helper for displaying and processing cover images.
- Added a set of static cover images for selection in the image picker.
- Enhanced error handling for image uploads and processing.
This update improves the user experience by providing a more robust cover image management system.
* refactor: rename STATIC_COVER_IMAGES_ARRAY to STATIC_COVER_IMAGES for consistency
- Updated the cover image helper to export STATIC_COVER_IMAGES instead of STATIC_COVER_IMAGES_ARRAY.
- Adjusted the ImagePickerPopover component to utilize the renamed export for rendering static cover images.
* feat: enhance project creation and image handling
- Introduced default project form values with a random emoji for logo props.
- Updated cover image handling in various components, ensuring consistent usage of the new cover image helper.
- Refactored image picker to improve search functionality and loading states.
- Removed unused constants and streamlined cover image type checks for better clarity and performance.
This update enhances the user experience in project creation and image selection processes.
* refactor: simplify cover image type definition and clean up code
- Removed duplicate type from TCoverImageType, streamlining the definition.
- Cleaned up whitespace in the cover image helper for improved readability.
This update enhances code clarity and maintains consistency in cover image handling.
* refactor: update cover image type definitions and simplify logic
- Changed ICoverImageResult and ICoverImagePayload interfaces to type aliases for better clarity.
- Simplified the logic in getCoverImageDisplayURL function to enhance readability and maintainability.
This update improves the structure and clarity of the cover image helper code.
* refactor: remove unused project cover image endpoint and update cover image handling
- Removed the ProjectPublicCoverImagesEndpoint and its associated URL from the project.
- Updated the cover image handling in the cover-image helper to utilize imported assets instead of static paths.
- Cleaned up the ProjectFavoritesViewSet and FileService by removing the now obsolete getProjectCoverImages method.
This update streamlines the cover image management and eliminates unnecessary code, enhancing overall maintainability.
* refactor: update cover image imports to new asset structure
- Replaced static path imports for cover images with updated paths to the new asset structure.
- This change improves organization and maintainability of cover image assets in the project.
This update aligns with recent refactoring efforts to streamline cover image handling.
* feat: add additional cover images to the helper
- Imported new cover images (24 to 29) into the cover-image helper.
- This update expands the available cover image options for use in the project, enhancing visual variety.
* refactor: remove ProjectPublicCoverImagesEndpoint from project URLs and views
* refactor: update cover image imports to include URL query parameter
- Modified cover image imports in the cover-image helper to append a URL query parameter for better asset handling.
- This change enhances the way cover images are processed and utilized within the project.
* refactor: extract default project form values into a utility function
- Created a new utility function `getProjectFormValues` to encapsulate the default project form values.
- Updated the `CreateProjectForm` component to use this utility function for setting default form values, improving code organization and maintainability.
* feat: integrate project update functionality in CreateProjectForm
- Added `updateProject` method to the `CreateProjectForm` component for updating project cover images after creation.
- Enhanced cover image handling by ensuring the correct URL is set for both uploaded and existing cover images.
This update improves the project creation workflow and ensures cover images are accurately updated.
* fix: update documentation for cover image handling
- Corrected the comment regarding local static images to reflect that they are served from the assets folder instead of the public folder.
- This change ensures accurate documentation for the `getCoverImageType` and `getCoverImageDisplayURL` functions, improving clarity for future developers.
* feat: implement random cover image selection for project forms
- Replaced the default cover image URL with a new utility function `getRandomCoverImage` that selects a random cover image from the available options.
- Updated the `getProjectFormValues` function to utilize this new method, enhancing the project creation experience with varied cover images.
---------
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
* fix: project error message and status code
* fix: incorrect member role check
* fix: project error message and status code
* fix: improve project permission checks and error handling in ProjectViewSet
* feat: enhance project settings layout with better loading strategy and fix all flicker
* fix: prevent rendering during project loading in ProjectAuthWrapper
* refactor: adjust layout structure in ProjectDetailSettingsLayout and enhance access restriction logic in ProjectAccessRestriction
* refactor: replace ProjectAccessRestriction component with updated version and enhance error handling
- Deleted the old ProjectAccessRestriction component.
- Introduced a new ProjectAccessRestriction component with improved error handling and user prompts for joining projects.
- Updated translations for new error states in multiple languages.
* fix: enhance error handling in IssueDetailsPage and remove JoinProject component
---------
Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
* chore: traige state in intake
* chore: triage state changes
* feat: implement intake state dropdown component and integrate into issue properties
* chore: added the triage state validation
* chore: added triage state filter
* chore: added workspace filter
* fix: migration file
* chore: added triage group state check
* chore: updated the filters
* chore: updated the filters
* chore: added variables for intake state
* fix: import error
* refactor: improve project intake state retrieval logic and update TriageGroupIcon component
* chore: changed the intake validation logic
* refactor: update intake state types and clean up unused interfaces
* chore: changed the state color
* chore: changed the update serializer
* chore: updated with current instance
* chore: update TriageGroupIcon color to match new intake state group color
* chore: stringified value
* chore: added validation in serializer
* chore: added logger instead of print
* fix: correct component closing syntax in ActiveProjectItem
* chore: updated the migration file
* chore: added noop in migation
---------
Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
* feat: change user email
* chore: optimised the logic
* feat: add email change functionality and related modals in profile form
* refactor: format checkEmail method for improved readability
* chore: added rate limit exceeded validation
* feat: implement change email modal with localization support
- Added translation support for the change email modal, including titles, descriptions, and error messages.
- Integrated the useTranslation hook for dynamic text rendering.
- Updated form validation messages to utilize localized strings.
- Enhanced user feedback with localized success and error toast messages.
- Updated button labels and placeholders to reflect localization changes.
* chore: added extra validation in cache key
* fix: format files
---------
Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
* [WEB-5134] refactor: update `web` ESLint configuration and refactor imports to use type imports
- Enhanced ESLint configuration by adding new rules for import consistency and type imports.
- Refactored multiple files to replace regular imports with type imports for better clarity and performance.
- Ensured consistent use of type imports across the application to align with TypeScript best practices.
* refactor: standardize type imports across components
- Updated multiple files to replace regular imports with type imports for improved clarity and consistency.
- Ensured adherence to TypeScript best practices in the rich filters and issue layouts components.
- Refactored file upload utilities to use async functions for better handling of file metadata.
- Introduced MIME type detection using the file-type library.
- Updated file service methods to await metadata retrieval.
- Added new dependencies for file-type and updated package.json accordingly.
- Removed deprecated file handling code from utils and adjusted imports across services.