Declare read-only resources and the owner relationship once each, make
the resource hook helpers private, simplify token auth, and cache the
generated API docs for an hour.
- Introduced `PhotoResource`, `PlantingResource`, and `SeedResource` to manage respective models.
- Updated application configuration to include API base URL.
- Modified production and test environments to set API base URL dynamically.
- Removed outdated JSONAPI resources initializers and patches.
- Refactored routes to use resourceful routing for API endpoints.
- Updated request specs to reflect changes in API responses and relationships.
- Added helper methods for generating related links in request specs.
- Implemented `planting_alternate_name.js` to load alternate names for crops in the planting form based on user input.
- Added `tab_navigation.js` to manage tab navigation and update the URL hash accordingly.
Update map rendering in member and place views
- Modified `_map.html.haml` to include a custom marker icon for member maps.
- Updated `index.html.haml` and `show.html.haml` to pass `places_map_data` to the map sections.
Refactor asset management in Rails application
- Removed Sprockets from the application configuration and adjusted asset paths.
- Updated asset precompilation settings and removed deprecated asset configurations from environment files.
- Added new build scripts for CSS and updated package dependencies in `package.json` for Bootstrap, Chart.js, and others.
Ensure React and CSS bundles are built before running tests
- Updated `react_bundle.rb` to check for the existence of both JavaScript and CSS bundles and build them if missing.
The free Memcachier dev plan is timing out on every call in production
(58 IO::TimeoutError in a 14 minute log window). Each request pays the
0.5s socket timeout and then renders uncached anyway, so the cache is
currently pure overhead on a dyno that is already being OOM-killed.
Memcached is now used only when USE_MEMCACHED=true and MEMCACHIER_SERVERS
is present. Otherwise production uses a memory store capped at 32MB via
RAILS_CACHE_SIZE_MB, so deploying restores real caching without the
timeouts and without an addon upgrade.
The memory store is per process, so homepage_stats gets an expires_in to
bound staleness. It is the only fragment that relies on expire_fragment
rather than a versioned cache key.
Sessions are cookie-based and every other cached value is either keyed by
cache_key_with_version or carries a TTL, so nothing depends on the cache
being shared between processes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Updated config/environments/production.rb to set config.log_level using
ENV.fetch("RAILS_LOG_LEVEL", "info").to_sym. This prevents excessive SQL
query logging in production by default while allowing configuration via
the RAILS_LOG_LEVEL environment variable.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
* Keep Rack::Attack counters in process memory
Rack::Attack was storing its request counters in Rails.cache, which is
memcached in production. On 2026-09-19, memcached calls were timing out
in the production logs. When that happens, the counters stop working.
In the same log window, one IP made 753 requests. 717 of them
succeeded. The 15 per minute throttle and the 500 per day ban did not
stop it.
Rack::Attack now uses its own in-memory store, capped at 8 MB. Each
Puma worker keeps its own counters, so the limits apply per worker.
Refs #1640
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Add BLOCKED_IPS list to Rack::Attack
When one crawler overloads the site, we need a fast way to block it.
Rack::Attack now blocks every IP listed in the BLOCKED_IPS environment
variable. The list is comma separated.
Setting the variable does not need a code change. It also keeps IP
addresses out of the repository.
Refs #1640
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Lower the memcached socket timeout to 0.5 seconds
The production cache store waited up to 1.5 seconds for each memcached
call. Dalli's own default is 1 second. Dalli also retries after a
failure, so one bad cache call can block a Puma thread for longer than
the timeout.
On 2026-09-19, the production logs showed memcached timeouts every few
seconds. With only 5 threads per Puma worker, blocked threads make the
site slow. Slow requests hold memory for longer.
The timeout is now 0.5 seconds. A healthy memcached call takes a few
milliseconds. A call that takes 0.5 seconds has failed in practice, so the
request is better off skipping the cache.
Refs #1640
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Replace Sidekiq with Solid Queue
This commit transitions the background job processing from Sidekiq to
Solid Queue.
Changes:
- Replaced `sidekiq` gem with `solid_queue` in Gemfile.
- Updated `development.rb` and `production.rb` to use `:solid_queue` as
the queue adapter.
- Added Solid Queue database tables via a new migration.
- Configured Solid Queue in `config/queue.yml` and `config/recurring.yml`.
- Integrated Solid Queue supervisor as a Puma plugin in `config/puma.rb`.
- Removed separate worker process from `Procfile`.
- Removed Sidekiq-specific configuration files.
- Updated Gemfile.lock to support both `ruby` and `x86_64-linux` platforms.
Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
* Fix regression in gemfiles
---------
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
* For now, drop mapbox for OSM tiles
* For now, drop mapbox for OSM tiles
* Use OSM tiles
* Remove mapbox secrets for now. If we bring back mapbox; we should re-enstate
* Only show up to 200 plantings
* Update app/assets/javascripts/members.js.erb
* [CodeFactor] Apply fixes
* Update production.rb
---------
Co-authored-by: codefactor-io <support@codefactor.io>
* For now, drop mapbox for OSM tiles
* For now, drop mapbox for OSM tiles
* Use OSM tiles
* Remove mapbox secrets for now. If we bring back mapbox; we should re-enstate
* Update members.js.erb
* Change placeholder