Fizzy is now free. Remove the entire Stripe billing system,
subscription management, and card/storage limit enforcement.
Removes from saas/: Plan model, Account::Billing, Account::Subscription,
Account::Limited, Account::OverriddenLimits, Account::BillingWaiver,
all subscription/billing controllers and views, Stripe webhook handler,
card creation/publishing limit enforcement, admin account override UI,
usage report rake task, and all related tests.
Removes from main app: Fizzy.saas? guards for subscription panel,
SaaS card footer override, near-limit notices, and saas.css stylesheet.
Adds migration to drop billing tables from the SaaS database.
Non-billing SaaS features (push notifications, signup, authorization,
telemetry, console1984/audits1984) are preserved.
* Add saas:usage_report rake task and extract Subscription.paid scope
Add a rake task to generate a CSV usage report with per-account data:
Queenbee ID, sign up date, paid date, card count, storage used, and
last active date.
Extract the paid subscriptions query from Admin::StatsController into
an Account::Subscription.paid scope so both the controller and the
new rake task can share it.
* Add comped and account name columns to usage report
* Update saas/lib/tasks/fizzy/usage_report.rake
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Preload storage_total to avoid N+1 in usage report
* Batch last_active_at queries to avoid per-account aggregates
* Add tests for Account::Subscription.paid scope
* Update saas/app/models/account/subscription.rb
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Aggregate paid dates in SQL instead of loading all subscriptions
* Fix paid scope to derive plan keys from Plan.all instead of PLANS hash
* Move paid dates and comped lookups into per-batch queries
* Materialize batch IDs to avoid cross-database subquery
SaasRecord models live on a separate database (fizzy_saas) that doesn't
have the accounts table. Using batch.select(:id) generated a subquery
that ran on the saas database, causing a table-not-found error. Using
pluck(:id) materializes the IDs into an array instead.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Use ActionPushNative's new on_load hook to configure the database connection,
following the same pattern as Active Storage and Action Text:
ActiveSupport.on_load(:action_push_native_record) do
connects_to database: { writing: :saas, reading: :saas }
end
This allows ApplicationPushDevice to inherit directly from ActionPushNative::Device
without needing an intermediate abstract class.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use RESTful DELETE /devices/:id where :id can be token or database ID
- Remove redundant unregister collection route
- Remove old Users::DevicesController
- Return 404 when device not found instead of silently succeeding
- Return 422 for invalid platform via ActiveRecord validation
- Update action_push_native to main branch (includes validate: true on enum)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The prior commit added it to saas/Gemfile but the production Docker
build uses Gemfile.saas (via BUNDLE_GEMFILE). This ensures the XML
parameter parser is present in the actual runtime environment.
* Add GVL contention metrics via gvltools and Yabeda
Expose per-request and process-wide GVL wait time as Prometheus metrics
to diagnose suspected GVL contention from Solid Cable and Action Cable
threads in Puma workers.
Metrics: gvl_request_wait_seconds (histogram), gvl_waiting_threads
(gauge), gvl_global_timer_total_seconds (gauge).
* Add unit: :seconds to GVL histogram for consistency