* update transactions_controller
* fix FEEDBACK - Remove DISTINCT that breaks tag-filtered index queries
* fix FEEBACK from jjmata
* Ignore Brakeman EOLRails warning for Rails 7.2
Restore fingerprint-scoped ignore lost during merge from main.
* update transactions_controller
* fix FEEDBACK - Remove DISTINCT that breaks tag-filtered index queries
* fix FEEBACK from jjmata
* Ignore Brakeman EOLRails warning for Rails 7.2
Restore fingerprint-scoped ignore lost during merge from main.
* resolve review - Add .distinct to the tag filtering subquery
* fix(ci): skip scheduled preview cleanup on forks
Only run the hourly Cloudflare preview cleanup on we-promise/sure,
where the required secrets exist.
* Drop obsolete Rails EOL note and Brakeman EOLRails ignore
The EOLRails ignore and the accompanying migration-rule note were only
needed while the app ran Rails 7.2.3.1, whose support window closed
2026-08-09. Main has since moved to Rails 8.1.3, so the check no longer
warns and both changes are dead weight that only widen this PR's diff.
Keeps the PR focused on the transactions controller query optimization.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(security): sanitize exception messages in API responses (FIX-11)
Replace raw e.message/error.message interpolations in response bodies
with generic error strings, and log class+message server-side. Prevents
leaking internal exception details (stack traces, SQL fragments, record
data) to API clients.
Covers:
- API v1 accounts, categories (index/show), holdings, sync, trades,
transactions (index/show/create/update/destroy), valuations
(show/create/update): replace "Error: #{e.message}" with
"An unexpected error occurred".
- API v1 auth: device-registration rescue paths now log
"[Auth] Device registration failed: ..." and respond with
"Failed to register device".
- WebhooksController#plaid and #plaid_eu: log full error and respond
with "Invalid webhook".
- Settings::ProvidersController: generic user-facing flash alert,
detailed log line with error class + message.
Updates providers_controller_test assertion to match sanitized flash.
* fix(security): address CodeRabbit review
Major — partial-commit on device registration failure:
- Strengthened valid_device_info? to also run MobileDevice's model
validations up-front (device_type inclusion, attribute presence), not
just a flat "are the keys present?" check. A client that sends a bad
device_type ("windows", etc.) is now rejected at the API boundary
BEFORE signup commits any user/family/invite state.
- Wrapped the signup path (user.save + InviteCode.claim + MobileDevice
upsert + token issuance) in ActiveRecord::Base.transaction. A
post-save RecordInvalid from device registration (e.g., racing
uniqueness on device_id) now rolls back the user/invite/family so
clients don't see a partial-account state.
- Rescue branch logs the exception class + message ("#{e.class} - #{e.message}")
for better postmortem debugging, matching the providers controller
pattern.
Nit:
- Tightened providers_controller_test log expectation regex to assert on
both the exception class name AND the message ("StandardError - Database
error"), so a regression that drops either still fails the test.
Tests:
- New: "should reject signup with invalid device_type before committing
any state" — POST /api/v1/auth/signup with device_type="windows"
returns 400 AND asserts no User, MobileDevice, or Doorkeeper::AccessToken
row was created.
Note on SSO path (sso_exchange → issue_mobile_tokens, lines 173/225): the
device_info in those flows comes from Rails.cache (populated by an earlier
request that already passed valid_device_info?), so the pre-validation
covers it indirectly. Wrapping the full SSO account creation (user +
invitation + OidcIdentity + issue_mobile_tokens) in one transaction would
be a meaningful architectural cleanup but is out of scope for this
error-hygiene PR — filed it as a mental note for a follow-up.
* third party provider scoping
* Simplify logic and allow only admins to mange providers
* Broadcast fixes
* FIX tests and build
* Fixes
* Reviews
* Scope merchants
* DRY fixes
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
* Add split ui
* Add settings configuration for split transactions
- Adds a new settings section for appearance changes
- Also adds extra checks for delete and API calls
- Also adds checks for parent/child changes
* fixes
- split transactions dark mode fix
- add split transactions to context menu
* Update entry.rb
1. New validation split_child_date_matches_parent — prevents saving a split child with a date different from its parent. This is the root-cause fix that
protects all flows at once.
2. Bulk update guard — bulk_update! now strips :date from attributes when processing split children, preventing the validation from raising and silently
skipping the date change instead.
* N+1 fix for split_parent?
* Update entry.rb
Problem: In bulk_update!, when a split child has :date removed from attrs (line 432) and the remaining attrs is empty (e.g., the bulk update only
changed the date), entry.update! {} still ran as a no-op. But lock_saved_attributes! and mark_user_modified! at lines 443-444 executed unconditionally,
incorrectly marking untouched split children as user-modified and opting them out of future syncs.
Fix:
1. Added a changed flag to track whether any actual modification happened
2. Wrapped entry.update! in an if attrs.present? check so no-op updates are skipped
3. Gated lock_saved_attributes! and mark_user_modified! behind if changed, so they only run when the entry was actually modified (either via attribute
update or tag update)
* fixes
1. Indentation in show.html.erb Settings section — The split button block and delete block had extra indentation making them appear nested inside guard
blocks they weren't part of. Fixed to match actual nesting.
2. Skip @split_parents query when grouping is off — The controller now only loads split parent entries when show_split_grouped? is true, saving a query
with joins when the feature is disabled.
* fix: handle tags separately from entryable_attributes in bulk updates
Tags use a join table (taggings) rather than a direct column, which means
empty tag_ids clears all tags rather than meaning "no change". This caused
bulk category-only edits to accidentally clear existing tags.
This fix:
- Removes tag_ids from entryable_attributes in Entry.bulk_update!
- Adds update_tags parameter to explicitly control tag updates
- Uses params.key?(:tag_ids) in controller to detect explicit tag changes
- Preserves existing tags when tag_ids is not provided in the request
This is a cleaner architectural solution compared to tracking "touched"
state in the frontend, as it properly acknowledges the semantic difference
between column attributes and join table associations.
https://claude.ai/code/session_014CsmTwjteP4qJs6YZqCKnY
* fix: handle tags separately in API transaction updates
Apply the same pattern to the API endpoint: tags are now handled
separately from entryable_attributes to distinguish between "not
provided" (preserve existing tags) and "explicitly set to empty"
(clear all tags).
This allows API consumers to:
- Update other fields without affecting tags (omit tag_ids)
- Clear all tags (send tag_ids: [])
- Set specific tags (send tag_ids: [id1, id2])
https://claude.ai/code/session_014CsmTwjteP4qJs6YZqCKnY
* Proposed fix
* fix: improve tag handling in bulk updates for transactions
* fix: allow bulk edit to clear/preserve tags by omitting hidden multi-select field
* PR comments
* Dumb copy/paste error
* Linter
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
* OAuth
* Add API test routes and update Doorkeeper token handling for test environment
- Introduced API namespace with test routes for controller testing in the test environment.
- Updated Doorkeeper configuration to allow fallback to plain tokens in the test environment for easier testing.
- Modified schema to change resource_owner_id type from bigint to string.
* Implement API key authentication and enhance access control
- Replaced Doorkeeper OAuth authentication with a custom method supporting both OAuth and API keys in the BaseController.
- Added methods for API key authentication, including validation and logging.
- Introduced scope-based authorization for API keys in the TestController.
- Updated routes to include API key management endpoints.
- Enhanced logging for API access to include authentication method details.
- Added tests for API key functionality, including validation, scope checks, and access control enforcement.
* Add API key rate limiting and usage tracking
- Implemented rate limiting for API key authentication in BaseController.
- Added methods to check rate limits, render appropriate responses, and include rate limit headers in responses.
- Updated routes to include a new usage resource for tracking API usage.
- Enhanced tests to verify rate limit functionality, including exceeding limits and per-key tracking.
- Cleaned up Redis data in tests to ensure isolation between test cases.
* Add Jbuilder for JSON rendering and refactor AccountsController
- Added Jbuilder gem for improved JSON response handling.
- Refactored index action in AccountsController to utilize Jbuilder for rendering JSON.
- Removed manual serialization of accounts and streamlined response structure.
- Implemented a before_action in BaseController to enforce JSON format for all API requests.
* Add transactions resource to API routes
- Added routes for transactions, allowing index, show, create, update, and destroy actions.
- This enhancement supports comprehensive transaction management within the API.
* Enhance API authentication and onboarding handling
- Updated BaseController to skip onboarding requirements for API endpoints and added manual token verification for OAuth authentication.
- Improved error handling and logging for invalid access tokens.
- Introduced a method to set up the current context for API requests, ensuring compatibility with session-like behavior.
- Excluded API paths from onboarding redirects in the Onboardable concern.
- Updated database schema to change resource_owner_id type from bigint to string for OAuth access grants.
* Fix rubocop offenses
- Fix indentation and spacing issues
- Convert single quotes to double quotes
- Add spaces inside array brackets
- Fix comment alignment
- Add missing trailing newlines
- Correct else/end alignment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix API test failures and improve test reliability
- Fix ApiRateLimiterTest by removing mock users method and using fixtures
- Fix UsageControllerTest by removing mock users method and using fixtures
- Fix BaseControllerTest by using different users for multiple API keys
- Use unique display_key values with SecureRandom to avoid conflicts
- Fix double render issue in UsageController by returning after authorize_scope\!
- Specify controller name in routes for usage resource
- Remove trailing whitespace and empty lines per Rubocop
All tests now pass and linting is clean.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add API transactions controller warning to brakeman ignore
The account_id parameter in the API transactions controller is properly
validated on line 79: family.accounts.find(transaction_params[:account_id])
This ensures users can only create transactions in accounts belonging to
their family, making this a false positive.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Signed-off-by: Josh Pigford <josh@joshpigford.com>
Co-authored-by: Claude <noreply@anthropic.com>