Commit Graph

109 Commits

Author SHA1 Message Date
Rosa Gutierrez 196d685f8d Implement authorization for Active Storage endpoints
Consider blobs attached to any public records accessible to anyone with
the URL.
2025-12-25 21:22:36 +01:00
Jeremy Daer bd6c1cf34f Storage: harden reconcile for concurrent writes and fix board transfer (#2096)
* Storage: harden reconcile for concurrent writes and fix board transfer

Reconcile now uses two-cursor approach: captures cursor before and after
the storage scan, aborting if they differ (entries added during scan).
Job retries 3x with 1-minute waits and limits concurrency to 1 per owner.

Board transfer now correctly moves storage for card description embeds
and comment embeds, not just direct attachments. Uses batched queries
to handle cards with thousands of comments efficiently.

Also fixes N+1 queries in attachment grouping via lookup maps.

* Storage: fix per-attachment reconcile and enforce no blob reuse

The storage ledger tracks per-attachment (not per-blob) as a business
abstraction for quotas. This fixes reconcile to match that model and
adds enforcement to prevent blob reuse in tracked contexts.

* Reconcile now uses joins(:blob).sum() for per-attachment counting
* New validation prevents reusing blobs across tracked attachments
* Race-safe storage_total creation with create_or_find_by
* Job efficiency: skip find_by when object already available
* Backfill script checks per-attachment, not just per-blob
2025-12-19 13:07:32 -08:00
Jeremy Daer 761d0b4a76 Speedy, auditable, deadlock-resistant storage tracking (#2026)
An append-only storage ledger replaces deadlock-prone synchronous
counter updates and drift-prone async updates.

All content storage (card images, card/comment/board description embeds)
is tracked. Account exports (which expire) and avatars are not tracked.

Storage is accounted for by Account and Board. Both consume the same
event stream independently: no bubble-up storage bumps triggering
deadlocks due to lock sequencing. Each calculates its own total from
the underlying ledger with independent cursors and materialization.

* Storage::Entry: Append-only ledger recording attach/detach/transfer
  events with delta bytes. Single event stream indexed for both
  Account and Board cursor queries.

* Storage::Total: Polymorphic snapshot cache with cursor (last_entry_id)
  tracking which entries have been materialized.

* Storage::Totaled concern: Provides bytes_used (fast snapshot) and
  bytes_used_exact (snapshot + pending) query modes, plus
  materialize_storage! to roll up pending entries.

* Storage::Tracked concern: For models owning attachments (Card,
  Comment, Board). Provides board_for_storage_tracking for models
  where board is determined differently (Board returns self).
  Handles board transfers by recording transfer_out/transfer_in entries.

* Storage::AttachmentTracking: Hooks ActiveStorage::Attachment lifecycle
  to record attach/detach entries. Handles ActionText::RichText embeds
  by traversing to the actual model. Snapshots context in before_destroy
  to handle cascading deletes where parent record may be gone by
  after_destroy_commit.

* MaterializeJob: Rolls up pending entries into snapshot. Concurrency
  limited per owner to prevent duplicate work.

* ReconcileJob: On-demand reconciliation against actual attachment
  storage for support/debugging. Compares ledger total to real bytes
  from card images, card embeds, comment embeds, and board embeds.

Usage:
* account.bytes_used / board.bytes_used: fast, slightly stale bytesize
* account.bytes_used_exact / board.bytes_used_exact: real-time bytesize
* Storage::Entry: audit trail for debugging and point-in-time queries
2025-12-10 16:04:30 -08:00
Kevin McConnell d24726c1d3 Merge pull request #1768 from basecamp/data-exports
Add "data export" feature
2025-12-02 09:56:40 +00:00
Jorge Manrubia af0113c9ee After touch should trigger board touch too
The change in https://github.com/basecamp/fizzy/commit/c606de7b41f57734c4c859c5fc3d98c952b26d58#diff-1ecde9fb1ee2494e5d94e807f51f861928f77cfcfb0884889911b62a32c2ff4cL4-R23 was preventing marking cards as golden from broadcasting
2025-12-02 05:55:49 +01:00
Kevin McConnell e16cc21b0a Add "data export" feature
- Adds a button in Account Settings where you can request a ZIP export of your
  Fizzy data
- Export files are created in the background. When ready, a link to
  download them is sent to the requester.
- Exports expire after 24 hours. And are limited to 10 per day.
2025-12-01 15:23:26 +00:00
Mike Dalessio 50fe973105 Remove N+1 on the cards board. 2025-11-27 11:30:54 -05:00
David Heinemeier Hansson c606de7b41 Instead of trying to suppress from the wrong layer of the stick, just don't touch until published? 2025-11-18 15:35:40 +01:00
Jorge Manrubia 793aace69a Remove implicit order when filtering by closed cards
We set the order in the dedicated column controllers and we don't want this in place when using composable filters.

Also, this raises an error with the MySQL move when combined with assignees. Because we inject a .distinct there,
that messes up with the query select, resulting in having to add a redundant select to the closed sorting scope, so that
the closure.created_at is not lost.
2025-11-18 11:36:47 +01:00
Mike Dalessio e3ce26c53d rubocop: more style fixes 2025-11-17 14:54:42 -05:00
Stanko K.R. 0b8c51c2e9 Preload cards for columns 2025-11-17 16:16:19 +01:00
Mike Dalessio 3b09a92193 rubocop: style fixes 2025-11-17 09:12:41 -05:00
Stanko K.R. e539754e2c Preload cards 2025-11-17 09:12:41 -05:00
Mike Dalessio f5305b2fe6 Use Card number as the model param
instead of the UUID `id`
2025-11-17 09:12:41 -05:00
Stanko K.R. 15eeba82f5 Scope model methods by account 2025-11-17 09:12:41 -05:00
Donal McBreen fc56ad9d7c Combine uuid-old and uuid branch changes
- Switch to binary 16 for UUID keys
- Remove AccountScopedRecord base class, all model use binary uuids now
- Fix the search sql to serialize uuids properly
- Patch the MySQL schema dumper to output binary lengths
2025-11-17 09:12:34 -05:00
Mike Dalessio e4011ef211 Update primary keys on customer data to UUIDs
- schema changes to primary and foreign keys
- fixture changes
- customer data models subclass AccountScopedRecord
- import script updated
2025-11-17 09:12:30 -05:00
Stanko K.R. 32d96db7d9 Replace card id with card number 2025-11-17 09:12:15 -05:00
Stanko K.R. ba3f6b188e Add account_id to everything 2025-11-17 09:11:42 -05:00
Jorge Manrubia 0267223749 Re-enable live updates for card permas 2025-11-13 14:49:17 +01:00
Jorge Manrubia b78ca68b2b Restore streams since we rely on those for deleting boosts
https://app.fizzy.do/5986089/cards/2811
2025-11-05 21:31:08 +01:00
Jorge Manrubia 03a345609e Baseline replacing collection with board across code 2025-11-05 13:31:54 +01:00
Jorge Manrubia 42a8b52fa1 Don´t show draft banner if the card is not filled 2025-11-03 14:47:29 +01:00
Jorge Manrubia 37ce2c98d1 Refresh the cards perma dynamically with page refreshes 2025-11-03 13:04:21 +01:00
Jorge Manrubia 12d461f8d1 Use last_active_at column for sorting since we are now touching updated_at for invalidating 2025-10-31 10:41:20 +01:00
Jorge Manrubia 2a79cb413e Remove cacheable concern, inline cache helper 2025-10-31 10:38:52 +01:00
David Heinemeier Hansson c79d425e9d Easier to see orders when aligned 2025-10-30 14:45:01 +01:00
Jason Zimdars b20ddde5f6 Capture old_collection before it changes
Fixes: https://box-car.com/5986089/cards/2577 in which the old and new
collection name would always be the destination (current) collection
2025-10-29 10:31:19 -05:00
Jorge Manrubia acdc7706ce Add not now and closed as status filters
https://fizzy.37signals.com/5986089/cards/2342
2025-10-15 13:59:50 +02:00
Jason Zimdars fb865effac Activity bumps cards to the top of the stream 2025-10-13 15:39:14 -05:00
Jorge Manrubia 1d1a3da9cf Cleanup workflow/stages
This was pending after moving to columns
2025-10-07 11:10:10 +02:00
Jorge Manrubia e31ce965e6 Make sure you don't reopen cards 2025-10-03 16:36:09 +02:00
Jorge Manrubia 1dfb871c44 Don handle collection change on creation 2025-10-03 16:26:23 +02:00
Jorge Manrubia 4daeb2a33b Make sure we clear the column when moving cards to another collection
Or it will be become not accessible
2025-10-03 16:19:42 +02:00
Jorge Manrubia 08c6a38492 Remove falling back status that we don't support anymore 2025-10-02 10:30:14 +02:00
Jorge Manrubia 613f4b0110 Introduce triaging vocabulary 2025-09-24 16:35:16 +02:00
Jorge Manrubia 2f6a639682 Prepare entropy code for the new auto postpone approach 2025-09-24 16:22:42 +02:00
Jorge Manrubia b63a6b3e97 Add postponable concern 2025-09-24 14:37:14 +02:00
Jorge Manrubia cd025447f2 Add basic column models and script to migrate 2025-09-24 13:15:03 +02:00
Jorge Manrubia 1633c56a08 Move cache_key to the new cacheable concern 2025-09-19 16:51:11 +02:00
Jorge Manrubia 690734d8ab Fix several caching issues when viewing card permas 2025-09-18 10:58:09 +02:00
Jorge Manrubia 7f7ecf0346 Delete notifications when the user loses access to the collection 2025-09-10 10:24:20 +02:00
Jorge Manrubia b9c7ac225a Extract method 2025-09-10 10:07:58 +02:00
Jason Zimdars 4d6201b8dc Filter to just drafts 2025-09-02 19:32:19 -05:00
Jason Zimdars ce05d800d3 Filter by golden status 2025-08-28 13:39:02 -05:00
Jorge Manrubia c8569ce8b9 Split indexed_by into two filters: indexed by and sort by
https://3.basecamp.com/2914079/buckets/37331921/todos/8877489555#__recording_8987808963
2025-08-21 19:12:54 +02:00
Jorge Manrubia 18597aabad Make sure we sort closed cards by recency by default
So that the filter used for the insight context fetches the most recent cards when using indexed_by closed
2025-07-24 17:46:40 +02:00
Jorge Manrubia b2ee79bbd0 Format 2025-07-24 16:48:06 +02:00
Jorge Manrubia 5ad2d09eae Move the hack to fetch completed cards to the insight command logic
We need to rework this as part of the filters revamp. "indexed_by" being
a mix of filters keeps biting us.
2025-07-24 16:48:05 +02:00
Jorge Manrubia 8bb7783215 Extract "prompt generation" logic to models for events, comments and cards
So that we can reuse in other scenarios
2025-07-24 16:48:03 +02:00