Addresses an issue where User#familiar_name assumed `name` was always
present, potentially raising an exception during view rendering. Now
User validates name presence, and User#familiar_name handles blank
strings without error, in case any existing invalid records exist.
* main:
Use decimals for ordered lists
Update cached fragments
Revert "Merge pull request #1865 from basecamp/public-avatar-caching"
Show only public cards on public boards
Swap order of avatar links
Limit length of full name during signup
Make layout bulletproof
Serve own avatar from its own endpoint
If we don't validate for length, then signups that overflow the database
columns will unnecessarily create and cancel a tenant. Adding a
validation means we can avoid this.
* main: (119 commits)
Bust comment view cache
Update lexxy to bring fixes from https://github.com/basecamp/lexxy/releases/tag/v0.1.24.beta
Fix unexpected remove empty line from README
Lightbox uses Stimulus target callbacks instead of data-action
Add test coverage for the image lightbox
Add tests for tenancy middleware and timezone cookie
Refactor: Simplify TimeWindowParser using Rails convenience methods
SMTP: support SMTPS on port 465 (#2132)
Bump fizzy-saas to retain fewer docker images (#2134)
Add test coverage for with_golden_first scope (#2130)
Refactor: improve query scope composition with merge syntax (#2131)
Validate avatar sizes
Introduce Vips configuration
Tailscale serve support (#2126)
Update tests with final method naming: record! -> record
CSP config to allow Minio in development
Update fizzy-saas to get employee restriction in staging
Drop staff restriction in beta and staging
Unused
Use new FIZZY_GH_TOKEN with limited access
...
- Replace `beginning_of_day..end_of_day` with `all_day`
- Replace `beginning_of_week..end_of_week` with `all_week`
- Replace `beginning_of_month..end_of_month` with `all_month`
- Replace `beginning_of_year..end_of_year` with `all_year`
These changes improve code readability by using idiomatic Rails
methods that accomplish the same thing in a more concise and
expressive way.
* Refactor: improve query scope composition with merge syntax
Replace manual WHERE clause concatenation with Rails' merge method
for more elegant and maintainable scope composition across Card,
Comment, and Filter models. This approach better follows Rails
conventions and improves code readability.
* Extend scope composition improvements to Card::Closeable
Apply the same nested hash syntax pattern to closures table references
in order and where clauses.
* Remove unnecessary outer braces from where clause
---------
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
Replace SQL string syntax with Rails range syntax for date filtering
in the ActivitySpike::Detector. This improves code readability and
follows Rails idioms.
Changed from:
.where("created_at >= ?", recent_period.seconds.ago)
To:
.where(created_at: recent_period.seconds.ago..)
This modernizes the codebase while maintaining the same functionality.
Simplifies the last_event method in ActivitySpike::Detector by using
the more idiomatic Rails pattern .order(:created_at).last instead of
.order(created_at: :desc).first. Both generate the same SQL query but
.last is more readable and conventional in Rails codebases.
* Allow Card#last_updated_at to be set
This is useful when doing an import from another system. I'm currently
working on a script to import our Github issues into Fizzy.
This is discussed in
https://github.com/basecamp/fizzy/pull/2056#discussion_r2609560246
* Add nil fallback and expand test coverage for last_active_at
Adds a safety fallback to Time.current if created_at is unexpectedly nil
during card creation.
Test coverage to verify:
* last_active_at defaults to created_at when not provided
* last_active_at can be updated via API on existing cards
* import workflow where last_active_at is restored after comments
* publishing doesn't overwrite explicit last_active_at values
---------
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
* main: (117 commits)
Explain that the upload URL is account-scope
Allow direct uploads via API
Storage: ignore jobs for now-deleted targets
API: Support `created_at` for API card and comment creation (#2056)
Enforce CSP (#2070)
CSP: full config with env vars per source (#2069)
Speedy, auditable, deadlock-resistant storage tracking (#2026)
Gitleaks: ignore legit non-sensitive API keys and tokens in docs/ and test/ (#2068)
Get gitleaks-audit green again
Bump actions/checkout from 4 to 6 (#2047)
Bump docker/login-action from 3.5.0 to 3.6.0 (#2046)
Bump docker/metadata-action from 5.8.0 to 5.10.0 (#2045)
Bump sigstore/cosign-installer from 3.9.2 to 4.0.0 (#2044)
make MySQL SSL mode configurable via env var (#2036)
Update tip text for turning a card into a Golden Ticket
Revert "Fix Lexxy prompt list padding by lowering rich-text specificity"
Fix Lexxy prompt list padding by lowering rich-text specificity
Improve phrasing
Fix crash due to missing partial
Fix status and filter mistakes
...
* Add support to set `created_at`
Discussed in
https://github.com/basecamp/fizzy/pull/1766#issuecomment-3637846074,
this allows users to import cards from another system with entries from
the past. For instance I'm importing all our Github issues (including
the closed onces) to Fizzy.
* Iron out published state tracking
---------
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
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
* main:
Remove semver-major-days from Dependabot on GH actions
Allow chromium unstable endpoint
Bump Rails to current ast-immediate-variants-process-locally branch
Rails seeded parallel tests (#2037)
Add blank slate to the main menu
Fixing Lexxy prompt menu spacing
Make SMTP config conditional on SMTP_ADDRESS
default to smtp
configurable actionmailer settings (ENV)
* main:
Stream response in webhook request manually to check size
Fix stale-read race when creating push subscriptions
Drop defunct Account upload attachments (#2030)
Process blob variants using local files
Allow configuring SSL in production with ENV vars
This addresses a DoS vulnerability where the response might be massive
leading to OOM errors, as the response is read in full in memory by
default.
To prevent this, we need to read the body in chunks, checking the
size of the chunks we've read and raising if we go over a certain limit.
I've set the limit to 100 KB because the responses to these requests
should be fairly small or even empty, and we only care about the status
code in the end.