diff --git a/db/migrate/20260709000000_enable_api_and_webhooks_for_existing_accounts.rb b/db/migrate/20260709000000_enable_api_and_webhooks_for_existing_accounts.rb new file mode 100644 index 000000000..8c503ad95 --- /dev/null +++ b/db/migrate/20260709000000_enable_api_and_webhooks_for_existing_accounts.rb @@ -0,0 +1,12 @@ +# Enable api_and_webhooks for existing accounts. +# Like 20260120121402_enable_captain_tasks_for_existing_accounts.rb, we don't need +# to update ACCOUNT_LEVEL_FEATURE_DEFAULTS or clear GlobalConfig cache because +# api_and_webhooks has `enabled: true` in features.yml - ConfigLoader handles the +# defaults on deploy automatically. +class EnableApiAndWebhooksForExistingAccounts < ActiveRecord::Migration[7.0] + def up + Account.find_in_batches(batch_size: 100) do |accounts| + accounts.each { |account| account.enable_features!('api_and_webhooks') } + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 5e506a475..aceb7b419 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2026_07_06_215758) do +ActiveRecord::Schema[7.1].define(version: 2026_07_09_000000) do # These extensions should be enabled to support this database enable_extension "pg_stat_statements" enable_extension "pg_trgm" diff --git a/lib/tasks/api_and_webhooks_migration.rake b/lib/tasks/api_and_webhooks_migration.rake new file mode 100644 index 000000000..a92aaa81e --- /dev/null +++ b/lib/tasks/api_and_webhooks_migration.rake @@ -0,0 +1,59 @@ +# Grandfather api_and_webhooks for existing accounts (cloud only). +# +# Adds api_and_webhooks to each account's manually_managed_features internal +# attribute so per-account billing reconciles never strip the feature from +# accounts that existed before the flag was introduced. Idempotent. +# +# Usage Examples: +# # Grandfather a single account +# ACCOUNT_ID=1 bundle exec rake api_and_webhooks:grandfather +# +# # Grandfather all accounts in the installation +# bundle exec rake api_and_webhooks:grandfather +# rubocop:disable Metrics/BlockLength +namespace :api_and_webhooks do + desc 'Grandfather api_and_webhooks via manually_managed_features for existing accounts' + task grandfather: :environment do + abort 'Aborted. This task requires the enterprise edition.' unless ChatwootApp.enterprise? + + account_id = ENV.fetch('ACCOUNT_ID', nil) + accounts = account_id.present? ? Account.where(id: account_id) : Account.all + + if account_id.blank? + print 'No ACCOUNT_ID specified. This will grandfather ALL accounts. Continue? [y/N] ' + abort 'Aborted.' unless $stdin.gets.chomp.casecmp('y').zero? + end + + if account_id.present? && accounts.empty? + puts "Error: Account with ID #{account_id} not found" + exit(1) + end + + total = accounts.count + puts "Grandfathering api_and_webhooks for #{total} account(s)..." + + updated = 0 + skipped = 0 + errored = 0 + + accounts.find_each do |account| + service = Internal::Accounts::InternalAttributesService.new(account) + features = service.manually_managed_features + + if features.include?('api_and_webhooks') + skipped += 1 + next + end + + service.manually_managed_features = features + ['api_and_webhooks'] + account.enable_features!('api_and_webhooks') + updated += 1 + rescue StandardError => e + errored += 1 + puts " Account #{account.id} — error: #{e.message}" + end + + puts "Done! Updated: #{updated}, Skipped: #{skipped}, Errored: #{errored}, Total: #{total}" + end +end +# rubocop:enable Metrics/BlockLength