Commit Graph

1125 Commits

Author SHA1 Message Date
Rob Zolkos 8ee2d7d65f Add JSON response format to access token creation (#2662)
* Add JSON response format to access token creation

Allows programmatic clients (e.g., CLI) to create access tokens
via the API by returning the token, description, and permission
as JSON instead of redirecting to the HTML show page.

* Add JSON response format to access token creation

Allows programmatic clients (e.g., CLI) to create access tokens
via the API by returning the token, description, and permission
as JSON instead of redirecting to the HTML show page.

* Style

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
2026-03-05 13:48:51 -04:00
Aleksander 446f22a292 Fix N+1 queries on identity in UsersController and BoardsController (#2365)
* Fix N+1 queries on identity in UsersController and BoardsController

- Add .includes(:identity) to UsersController#index
- Add .includes(creator: :identity) to BoardsController#index
- Add N+1 regression tests for both controllers

* Simplify controller N+1 tests with query assertions

---------

Co-authored-by: Mike Dalessio <mike@37signals.com>
2026-02-25 13:10:56 -05:00
Mike Dalessio 16abe6b38b Remove dead HTML branch from boards#index (#2613)
See #2528 for context
2026-02-25 12:30:39 -05:00
Rosa Gutierrez 628a43ef61 Small native API changes: include identity ID, return empty signup completion body
Include identity.id in the my/identity.json response and return an
empty body from the CREATE signup/completions.json endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:34:09 +01:00
Denis Švara db9f67589c Implement ClientConfigurationsController for mobile client path config. 2026-02-24 11:25:07 +01:00
Adrien Maston c2d5070840 Merge pull request #2567 from basecamp/mobile/fix-empty-search 2026-02-19 14:28:54 +01:00
Adrien Maston 5f25ebe3f4 Add a line break, it looks better 2026-02-19 14:25:26 +01:00
Adrien Maston 97f2f677d6 Don't render empty state instead of hiding it 2026-02-19 14:04:14 +01:00
Adrien Maston 0e1feb0f3f Create @query in controller instead of testing for blank? 2026-02-19 12:06:49 +01:00
Mike Dalessio 41675224d0 Remove redundant insecure context CSRF check
Rails now handles the insecure context case natively in
verified_via_header_only? — when Sec-Fetch-Site is missing and the
request is plain HTTP without force_ssl, the request is allowed.

Remove the now-redundant allowed_insecure_context_request? method and
update the test to set ActionDispatch::Http::URL.secure_protocol
alongside Rails.configuration.force_ssl so the upstream check works
correctly in the test environment.
2026-02-18 14:06:46 -05:00
Alp Keser 056dfc1367 Merge pull request #2528 from basecamp/mobile/missing-boards-fix
Update boards JSON to return all accessible boards ordered by recently accessed
2026-02-18 13:20:49 +03:00
Alp Keser 610cf7a190 Merge pull request #2398 from basecamp/notifications-tray-api
Add JSON response support for `notifications/tray`
2026-02-18 13:19:59 +03:00
Stanko Krtalić 4b2586769b Merge pull request #2556 from basecamp/fix-notifications-staying-unread-on-card-visit
Reinstate card readings
2026-02-17 14:15:24 +01:00
Stanko K.R. 9d4db14fc3 Reinstate card readings 2026-02-17 13:57:24 +01:00
Alp Keser 13d7cd4303 Paginate boards JSON 2026-02-17 10:37:23 +03:00
Andy Smith b638998789 Merge pull request #2456 from nqst/qr-code-in-dark-mode
Improve QR code appearance in dark mode
2026-02-16 09:54:54 -06:00
Alp Keser a6eb331710 Fix query parameter name 2026-02-12 14:55:34 +03:00
Alp Keser b8793bfec3 Add include_unread param to tray API and vary cache 2026-02-12 14:55:34 +03:00
Alp Keser 80546a6b47 Include read notifications in the tray API response 2026-02-12 14:55:34 +03:00
Stanko K.R. 36ee253a1a Stack notifications everywhere
We had client-side notification stacking in the tray since launch, but now we want to stack notifications in the notifications page, in API responses and in email bundles.
2026-02-12 10:29:50 +01:00
Alp Keser 608be1f155 Fix boards JSON to return all accessible boards ordered by recently accessed. 2026-02-12 10:27:32 +03:00
Mike Dalessio e311d8f521 Omit inactive users from board mention picker
The board-scoped mention prompt was returning all users with board access,
including those marked as inactive. This adds the `.active` scope to match
the behavior of the account-wide mention prompt.

Fixes https://app.fizzy.do/5986089/cards/3861
2026-02-03 18:42:15 -05:00
Stanko K.R. 5dac2fa678 Finishing touches 2026-02-02 18:06:19 +01:00
Stanko K.R. a791fe1919 Hide importing accounts 2026-02-02 12:36:48 +01:00
Stanko K.R. 635dbb90cb Rename the Import controller to Account::Import 2026-02-02 12:36:48 +01:00
Stanko K.R. eed2b3c8a7 Cleanup code 2026-02-02 12:36:48 +01:00
Stanko K.R. 0452d243e1 Fix account creation and import updates 2026-02-02 12:36:48 +01:00
Stanko K.R. 19ae555c2e Convert Account::SingleUserExport to User::DataExport 2026-02-02 12:36:48 +01:00
Stanko K.R. 161efb0b8e Implement basic imports 2026-02-02 12:36:48 +01:00
Rosa Gutierrez 63d9f29b1e Return internal account ID after signup completion
This matches what we return from `/my/identity` endpoint for each
account.
2026-01-30 13:41:37 +01:00
Rosa Gutierrez 614527e9c2 Add JSON format support to signup flow for native API clients
The magic link verification endpoint now returns `requires_signup_completion`
to indicate whether the client needs to collect a name and complete signup.
The signup completion endpoint now supports JSON responses with the account ID.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:41:37 +01:00
Alexander Zaytsev b83ccce1c9 Add padding to QR code images 2026-01-29 09:57:52 +01:00
Rosa Gutierrez c059e0b216 Add JSON format support to session destroy for native API clients
This is so that native API clients can get the session record deleted
server-side. They still need to take care of clearing any cookies
they've set in web views.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 19:59:30 +01:00
Denis Švara a75d56d243 Merge branch 'main' into pins-api
* main: (127 commits)
  Align board name start so it doesn't get too big
  Clean up card/events header layout
  Sync email to Stripe when user changes email address (#2432)
  Fix IDOR in webhook activation endpoint (#2431)
  Add card reactions to API docs and reactions_url to card JSON (#2427)
  Remove unnecessary claude plan
  Allow boosts on cards (#2411)
  Revert "Fix notification click URL by using correct data property"
  Add migration to remove draft cards from search index
  Guard search indexing with searchable? check
  Forbid comments on draft cards
  prefactor: update search to use published cards
  Fix notification click URL by using correct data property
  Wait for service worker to be active before subscribing
  Fix stuck state when permission granted but no subscription
  Extract Card::Commentable
  Include arm64 build in Docker workflow
  Remove unnecessary `await` in push handler
  Correctly initialise WebPush connection (#2417)
  Update models, views, and fixtures for polymorphic reactions
  ...
2026-01-27 15:37:47 +01:00
Jeremy Daer 4efa4da809 Fix IDOR in webhook activation endpoint (#2431)
The webhook activation controller was using account-scoped lookup
instead of board-scoped lookup, allowing users to reactivate webhooks
on boards they don't have access to.

This was an oversight when board-scoping was added to the main webhooks
controller - the activations controller was missed in that update.

The fix adds the BoardScoped concern to properly restrict webhook
activation to boards the user has explicit access to.
2026-01-24 17:06:12 -08:00
Mike Dalessio cb61b36715 Allow boosts on cards (#2411)
* Add reactions association to Card model

Adds `has_many :reactions` to Card, matching the implementation in Comment.
This allows cards to be reacted to directly with emoji reactions.

The association:
- Orders reactions chronologically
- Uses polymorphic `:reactable` interface
- Deletes reactions when card is destroyed

Includes test coverage for the new association.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add reactions UI to card detail view

- Create Cards::ReactionsController with turbo stream responses
- Add card reactions views (reactions list, new form, menu partial)
- Position reaction button flush-right when no reactions exist
- Show reactions on bottom-left when present, with button inline
- Handle narrow viewports by flowing button below meta section
- Add controller tests for card reactions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add boost count to card preview

Display reaction count alongside comment count on card tiles in board
columns. Introduces a .card__counts wrapper to group both counts with
proper positioning on all viewport sizes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Consolidate reaction views for cards and comments

Extract shared views to app/views/reactions/ using polymorphic routing.
Both card and comment reactions now render the same partials, with a
helper to compute the correct path prefix for nested routes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix emoji picker width in card reactions

Override .card .popup { inline-size: 260px } for the reaction popup
so the emoji grid displays without horizontal scrollbar.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Hide boost count where comment count is hidden

Add .card__boosts alongside .card__comments in:
- Tray view (display: none)
- Cards with background images (opacity toggle on hover)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix reactions button overlap with zoom button in card footer

When a card has a background image, the footer contains both a reactions
button and a zoom button. Previously they would overlap because the
reactions button was absolutely positioned to the bottom-right.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Tighten up previews

* Move card reactions from meta partial to container

Fixes two issues:

1. Reactions were duplicated each time a card was assigned because the
   turbo stream replaced the meta div with the full perma/meta partial,
   which included reactions outside the replaced element.

2. Draft cards incorrectly showed the boost button because the meta
   partial was shared between published and draft containers.

Moving reactions to _container.html.erb (published cards only) fixes
both issues and keeps the meta partial focused on meta content.

Fixes https://app.fizzy.do/5986089/cards/3837
Fixes https://app.fizzy.do/5986089/cards/3835

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* WIP adjust perma page

* Position and style the footer

* Render the stamp in the card body instead of the footer

* Fix undefined local variable in public cards view

Changed `card` to `@card` when rendering the stamp partial.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Preload card reactions to avoid N+1 queries

Include reactions and their reacters in the preloaded scope, matching
what we already do for comments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add model tests for card and comment reaction cleanup

Test that:
- Creating a card reaction touches the card's last_active_at
- Reactions are deleted when their parent comment is destroyed
- Reactions are deleted when their parent card is destroyed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Minor improvements to reactions code

- Use size instead of count in boosts partial to avoid extra SQL query
  on already-loaded collection
- Add else clause to reaction_path_prefix_for to raise ArgumentError
  for unknown reactable types instead of silently returning nil

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Bump specificity of reaction popup in the card perma

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Andy Smith <andy@37signals.com>
2026-01-23 13:25:55 -05:00
Mike Dalessio 9aa15e9fb2 Forbid comments on draft cards
- Add Card#commentable? method that returns true only for published cards.
- CardsController#create ensures that the card is commentable.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:28:26 -05:00
Denis Švara 5384116f11 Rename the helper to user_pins and extract a pins_limit method. 2026-01-22 16:44:30 +01:00
Denis Švara 90a7b15db3 Move pins scope into a variable. Keep web limit to 20 and set JSON to 100. 2026-01-20 12:32:45 +01:00
Mike Dalessio 2527f07365 Fix "M" hotkey using stale user from fragment cache
Add a SelfAssignmentsController to resolve the current user at request
time, avoiding the use of an incorrect user id baked into a cached URL.

ref: #2211 originally added the "M" hotkey
ref: https://app.fizzy.do/5986089/cards/3722
2026-01-16 13:41:33 -05:00
Denis Švara e82dc08f6c Add JSON support for GET /my/pins with no pagination and tests. 2026-01-14 17:43:16 +01:00
Denis Švara 1d8b765bab Add JSON support for pin/unpin on the card endpoints and add tests. 2026-01-14 17:10:54 +01:00
Rosa Gutierrez 527ffc42b9 Use relative URLs where possible across all the app
Skipping API responses, where we need absolute URLs, and those that are
intended for sharing or external use:

- account/join_codes/show.html.erb - Join code URL for sharing
- boards/edit/_publication.html.erb - Publication URL for sharing
- public/* views - Public page URLs and og:url meta tags
- pwa/manifest.json.erb - PWA manifest needs absolute URLs

For this, we had to replace `url_for` used with Active Storage variants
and previews with the specific path helper (for Active Storage
representations).
2026-01-14 11:01:42 +01:00
secretpray 6895135977 Fix deprecation warnings for skip_before_action :verify_authenticity_token
Replace deprecated `skip_before_action :verify_authenticity_token` with
`skip_forgery_protection` to resolve Rails deprecation warnings.

Changes:
- app/controllers/notifications/unsubscribes_controller.rb
- saas/app/controllers/stripe/webhooks_controller.rb
2026-01-12 18:44:35 +02:00
Stanko Krtalić eceb6c9b43 Add self-service account deletion (#2246)
* Add self-service account deletion

* Disable access to cancelled accounts & implement Stripe interactions

* Add tests

* Fix failing tests

* Remove cancelled accounts from lists

* Fix incorrect redirect

* Use _path instead of _url for consistency

* Don't track how far the inicieration job got

We still want the step tracking so that the job can be interrupted. But, since the scope is idempotent, we don't need to track how far it got.

* Specify the exact time when the data will be deleted

* Fix crash due to unadvancable cursor

* Rename up_for_incineration to due_for_incineration

* Regenrate the schema

* Fix incorrect path check

* Migrate the SQLite schema

* Only show the cancel button on cancellable accounts

* Check that a subscirption method exists before calling it

* Ignore job failures due to missing records when an account gets deleted

* Skip sending notifications on cancelled accounts

* Use collbacks to integrate

* Add a blank line between queue_as and discard_on

* Break checks into methods

* Inline methods

* Rename Account::IncinerateJob

* Run migrations

* Migrate SQLite
2026-01-12 14:21:05 +01:00
Jorge Manrubia f58a1aeb31 Use a dedicated resource for showing cards in draft mode
Mobile team needs this. Also, this lets us get rid from some conditionals for handling
both modes with a single template.

https://3.basecamp.com/2914079/buckets/44843469/messages/9437287330
2026-01-08 10:40:21 +01:00
Jorge Manrubia 6d05ab428e Remove engagements
We are not using this anymore!
2026-01-07 16:43:33 +01:00
Jorge Manrubia fb6bcaa18e Merge pull request #2227 from scart88/main
Redirect back to account settings page when the user role is changed …
2026-01-07 16:27:05 +01:00
Jorge Manrubia 6e765a114c Merge pull request #2294 from basecamp/refresh-drafts
Make sure new card drafts are refreshed when reused
2026-01-07 15:49:46 +01:00
Rosa Gutierrez 5f390f72df Support local installations where the app is loaded over HTTP
In this case requests won't be performed from a secure context [1] and
the browser won't send the Sec-Fetch-Site header. This means non-GET
requests will be rejected because CSRF protection will fail.

With this change, we allow these requests with missing Sec-Fetch-Site
headers if:
- They happen over HTTP
- The app is not configured to force SSL

The Origin check happens in any case.

[1] https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Secure_Contexts#potentially_trustworthy_origins
2026-01-05 18:50:14 +01:00