diff --git a/app/controllers/installation/onboarding_controller.rb b/app/controllers/installation/onboarding_controller.rb
index 272b4e5eb..e9cad7581 100644
--- a/app/controllers/installation/onboarding_controller.rb
+++ b/app/controllers/installation/onboarding_controller.rb
@@ -23,18 +23,11 @@ class Installation::OnboardingController < ApplicationController
private
def onboarding_params
- params.permit(:subscribe_to_updates, user: [:name, :company, :email])
+ params.permit(user: [:name, :company, :email])
end
def finish_onboarding
::Redis::Alfred.delete(::Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING)
- return if onboarding_params[:subscribe_to_updates].blank?
-
- ChatwootHub.register_instance(
- onboarding_params.dig(:user, :company),
- onboarding_params.dig(:user, :name),
- onboarding_params.dig(:user, :email)
- )
end
def ensure_installation_onboarding
diff --git a/app/controllers/super_admin/settings_controller.rb b/app/controllers/super_admin/settings_controller.rb
index 685e6a8bd..18f560886 100644
--- a/app/controllers/super_admin/settings_controller.rb
+++ b/app/controllers/super_admin/settings_controller.rb
@@ -1,10 +1,3 @@
class SuperAdmin::SettingsController < SuperAdmin::ApplicationController
def show; end
-
- def refresh
- Internal::CheckNewVersionsJob.perform_now
- # rubocop:disable Rails/I18nLocaleTexts
- redirect_to super_admin_settings_path, notice: 'Instance status refreshed'
- # rubocop:enable Rails/I18nLocaleTexts
- end
end
diff --git a/app/jobs/internal/check_new_versions_job.rb b/app/jobs/internal/check_new_versions_job.rb
index a141385d0..b0d190838 100644
--- a/app/jobs/internal/check_new_versions_job.rb
+++ b/app/jobs/internal/check_new_versions_job.rb
@@ -2,18 +2,7 @@ class Internal::CheckNewVersionsJob < ApplicationJob
queue_as :scheduled_jobs
def perform
- return unless Rails.env.production?
-
- @instance_info = ChatwootHub.sync_with_hub
- update_version_info
- end
-
- private
-
- def update_version_info
- return if @instance_info['version'].blank?
-
- ::Redis::Alfred.set(::Redis::Alfred::LATEST_CHATWOOT_VERSION, @instance_info['version'])
+ # Version checks are intentionally local-only in the private fork.
end
end
diff --git a/app/jobs/internal/trigger_daily_scheduled_items_job.rb b/app/jobs/internal/trigger_daily_scheduled_items_job.rb
index b09193e92..61816559b 100644
--- a/app/jobs/internal/trigger_daily_scheduled_items_job.rb
+++ b/app/jobs/internal/trigger_daily_scheduled_items_job.rb
@@ -2,25 +2,7 @@ class Internal::TriggerDailyScheduledItemsJob < ApplicationJob
queue_as :scheduled_jobs
def perform
- # Schedule daily deferred jobs here so each installation can spread load
- # across the day without changing its slot on deploys or restarts.
- schedule_version_check
- end
-
- private
-
- def schedule_version_check
- return unless Rails.env.production?
-
- Internal::CheckNewVersionsJob.set(wait_until: version_check_run_at).perform_later
- end
-
- def version_check_run_at
- Time.current.utc.beginning_of_day + designated_minute.minutes
- end
-
- def designated_minute
- @designated_minute ||= Digest::MD5.hexdigest(ChatwootHub.installation_identifier).hex % 1440
+ # Daily local scheduled work is extended by feature modules when present.
end
end
diff --git a/app/views/installation/onboarding/index.html.erb b/app/views/installation/onboarding/index.html.erb
index 8b337d16c..d9cfb3b01 100644
--- a/app/views/installation/onboarding/index.html.erb
+++ b/app/views/installation/onboarding/index.html.erb
@@ -59,13 +59,6 @@
-
- <%= check_box_tag "subscribe_to_updates", 'true', true %>
-
-
-
diff --git a/app/views/super_admin/settings/show.html.erb b/app/views/super_admin/settings/show.html.erb
index fd424ed90..809fb0ff0 100644
--- a/app/views/super_admin/settings/show.html.erb
+++ b/app/views/super_admin/settings/show.html.erb
@@ -44,10 +44,6 @@
<%= SuperAdmin::FeaturesHelper.plan_details.html_safe %>
diff --git a/config/routes.rb b/config/routes.rb
index d0fc7da06..93986a984 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -730,9 +730,7 @@ Rails.application.routes.draw do
resources :platform_banners
resource :instance_status, only: [:show]
- resource :settings, only: [:show] do
- get :refresh, on: :collection
- end
+ resource :settings, only: [:show]
# resources that doesn't appear in primary navigation in super admin
resources :account_users, only: [:new, :create, :show, :destroy]
diff --git a/enterprise/app/jobs/enterprise/internal/check_new_versions_job.rb b/enterprise/app/jobs/enterprise/internal/check_new_versions_job.rb
index b1f5d62ec..77e9ae5a9 100644
--- a/enterprise/app/jobs/enterprise/internal/check_new_versions_job.rb
+++ b/enterprise/app/jobs/enterprise/internal/check_new_versions_job.rb
@@ -1,30 +1,2 @@
module Enterprise::Internal::CheckNewVersionsJob
- def perform
- super
- update_plan_info
- reconcile_premium_config_and_features
- end
-
- private
-
- def update_plan_info
- return if @instance_info.blank?
-
- update_installation_config(key: 'INSTALLATION_PRICING_PLAN', value: @instance_info['plan'])
- update_installation_config(key: 'INSTALLATION_PRICING_PLAN_QUANTITY', value: @instance_info['plan_quantity'])
- update_installation_config(key: 'CHATWOOT_SUPPORT_WEBSITE_TOKEN', value: @instance_info['chatwoot_support_website_token'])
- update_installation_config(key: 'CHATWOOT_SUPPORT_IDENTIFIER_HASH', value: @instance_info['chatwoot_support_identifier_hash'])
- update_installation_config(key: 'CHATWOOT_SUPPORT_SCRIPT_URL', value: @instance_info['chatwoot_support_script_url'])
- end
-
- def update_installation_config(key:, value:)
- config = InstallationConfig.find_or_initialize_by(name: key)
- config.value = value
- config.locked = true
- config.save!
- end
-
- def reconcile_premium_config_and_features
- Internal::ReconcilePlanConfigService.new.perform
- end
end
diff --git a/lib/chatwoot_hub.rb b/lib/chatwoot_hub.rb
index 95679ed73..0d94090fa 100644
--- a/lib/chatwoot_hub.rb
+++ b/lib/chatwoot_hub.rb
@@ -1,4 +1,3 @@
-# TODO: lets use HTTParty instead of RestClient
class ChatwootHub
DEFAULT_BASE_URL = 'https://hub.2.chatwoot.com'.freeze
@@ -6,22 +5,6 @@ class ChatwootHub
DEFAULT_BASE_URL
end
- def self.ping_url
- "#{base_url}/ping"
- end
-
- def self.registration_url
- "#{base_url}/instances"
- end
-
- def self.push_notification_url
- "#{base_url}/send_push"
- end
-
- def self.events_url
- "#{base_url}/events"
- end
-
def self.billing_base_url
"#{base_url}/billing"
end
@@ -55,79 +38,6 @@ class ChatwootHub
support_identifier_hash: InstallationConfig.find_by(name: 'CHATWOOT_SUPPORT_IDENTIFIER_HASH')&.value
}
end
-
- def self.instance_config
- {
- installation_identifier: installation_identifier,
- installation_version: Chatwoot.config[:version],
- installation_host: URI.parse(ENV.fetch('FRONTEND_URL', '')).host,
- installation_env: ENV.fetch('INSTALLATION_ENV', ''),
- edition: ENV.fetch('CW_EDITION', '')
- }
- end
-
- def self.instance_metrics
- {
- accounts_count: fetch_count(Account),
- users_count: fetch_count(User),
- inboxes_count: fetch_count(Inbox),
- conversations_count: fetch_count(Conversation),
- incoming_messages_count: fetch_count(Message.incoming),
- outgoing_messages_count: fetch_count(Message.outgoing),
- additional_information: {}
- }
- end
-
- def self.fetch_count(model)
- model.last&.id || 0
- end
-
- def self.sync_with_hub
- begin
- info = instance_config
- info = info.merge(instance_metrics) unless ENV['DISABLE_TELEMETRY']
- response = RestClient.post(ping_url, info.to_json, { content_type: :json, accept: :json })
- parsed_response = JSON.parse(response)
- rescue *ExceptionList::REST_CLIENT_EXCEPTIONS => e
- Rails.logger.error "Exception: #{e.message}"
- rescue StandardError => e
- ChatwootExceptionTracker.new(e).capture_exception
- end
- parsed_response
- end
-
- def self.register_instance(company_name, owner_name, owner_email)
- info = { company_name: company_name, owner_name: owner_name, owner_email: owner_email, subscribed_to_mailers: true }
- RestClient.post(registration_url, info.merge(instance_config).to_json, { content_type: :json, accept: :json })
- rescue *ExceptionList::REST_CLIENT_EXCEPTIONS => e
- Rails.logger.error "Exception: #{e.message}"
- rescue StandardError => e
- ChatwootExceptionTracker.new(e).capture_exception
- end
-
- def self.send_push(fcm_options)
- send_push_with_response(fcm_options)
- rescue *ExceptionList::REST_CLIENT_EXCEPTIONS => e
- Rails.logger.error "Exception: #{e.message}"
- rescue StandardError => e
- ChatwootExceptionTracker.new(e).capture_exception
- end
-
- def self.send_push_with_response(fcm_options)
- info = { fcm_options: fcm_options }
- RestClient.post(push_notification_url, info.merge(instance_config).to_json, { content_type: :json, accept: :json })
- end
-
- def self.emit_event(event_name, event_data)
- return if ENV['DISABLE_TELEMETRY']
-
- info = { event_name: event_name, event_data: event_data }
- RestClient.post(events_url, info.merge(instance_config).to_json, { content_type: :json, accept: :json })
- rescue *ExceptionList::REST_CLIENT_EXCEPTIONS => e
- Rails.logger.error "Exception: #{e.message}"
- rescue StandardError => e
- ChatwootExceptionTracker.new(e).capture_exception
- end
end
ChatwootHub.singleton_class.prepend_mod_with('ChatwootHub')
diff --git a/spec/controllers/installation/onboarding_controller_spec.rb b/spec/controllers/installation/onboarding_controller_spec.rb
index 37a2d8822..e1082d1cc 100644
--- a/spec/controllers/installation/onboarding_controller_spec.rb
+++ b/spec/controllers/installation/onboarding_controller_spec.rb
@@ -13,10 +13,11 @@ RSpec.describe 'Installation::Onboarding API', type: :request do
end
context 'when CHATWOOT_INSTALLATION_ONBOARDING redis key is set' do
- it 'returns onboarding page' do
+ it 'returns onboarding page without a subscription checkbox' do
Redis::Alfred.set(Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING, true)
get '/installation/onboarding'
expect(response).to have_http_status(:success)
+ expect(response.body).not_to include('subscribe_to_updates')
Redis::Alfred.delete(Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING)
end
end
@@ -28,7 +29,6 @@ RSpec.describe 'Installation::Onboarding API', type: :request do
before do
allow(AccountBuilder).to receive(:new).and_return(account_builder)
allow(account_builder).to receive(:perform).and_return(true)
- allow(ChatwootHub).to receive(:register_instance).and_return(true)
Redis::Alfred.set(Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING, true)
end
@@ -42,14 +42,9 @@ RSpec.describe 'Installation::Onboarding API', type: :request do
expect(Redis::Alfred.get(Redis::Alfred::CHATWOOT_INSTALLATION_ONBOARDING)).to be_nil
end
- it 'will not call register instance when checkboxes are unchecked' do
- post '/installation/onboarding', params: { user: {} }
- expect(ChatwootHub).not_to have_received(:register_instance)
- end
-
- it 'will call register instance when checkboxes are checked' do
+ it 'accepts the subscription parameter without remote registration' do
post '/installation/onboarding', params: { user: {}, subscribe_to_updates: 1 }
- expect(ChatwootHub).to have_received(:register_instance)
+ expect(response).to have_http_status(:redirect)
end
end
diff --git a/spec/controllers/super_admin/settings_controller_spec.rb b/spec/controllers/super_admin/settings_controller_spec.rb
new file mode 100644
index 000000000..178f16b93
--- /dev/null
+++ b/spec/controllers/super_admin/settings_controller_spec.rb
@@ -0,0 +1,13 @@
+require 'rails_helper'
+
+RSpec.describe SuperAdmin::SettingsController do
+ it 'does not expose a manual refresh action' do
+ expect(described_class.action_methods).not_to include('refresh')
+ end
+
+ it 'does not leave a manual refresh link in the settings view' do
+ template = Rails.root.join('app/views/super_admin/settings/show.html.erb').read
+
+ expect(template).not_to include('refresh_super_admin_settings_url')
+ end
+end
diff --git a/spec/enterprise/jobs/enterprise/internal/check_new_versions_job_spec.rb b/spec/enterprise/jobs/enterprise/internal/check_new_versions_job_spec.rb
index 48664c904..66b8598a2 100644
--- a/spec/enterprise/jobs/enterprise/internal/check_new_versions_job_spec.rb
+++ b/spec/enterprise/jobs/enterprise/internal/check_new_versions_job_spec.rb
@@ -8,25 +8,19 @@ RSpec.describe Internal::CheckNewVersionsJob do
before do
allow(Internal::ReconcilePlanConfigService).to receive(:new).and_return(reconsile_premium_config_service)
allow(reconsile_premium_config_service).to receive(:perform)
- allow(Rails.env).to receive(:production?).and_return(true)
end
- it 'updates the plan info' do
- data = { 'version' => '1.2.3', 'plan' => 'enterprise', 'plan_quantity' => 1, 'chatwoot_support_website_token' => '123',
- 'chatwoot_support_identifier_hash' => '123', 'chatwoot_support_script_url' => '123' }
- allow(ChatwootHub).to receive(:sync_with_hub).and_return(data)
+ it 'does not reconcile enterprise configuration from a remote response' do
job
- expect(InstallationConfig.find_by(name: 'INSTALLATION_PRICING_PLAN').value).to eq 'enterprise'
- expect(InstallationConfig.find_by(name: 'INSTALLATION_PRICING_PLAN_QUANTITY').value).to eq 1
- expect(InstallationConfig.find_by(name: 'CHATWOOT_SUPPORT_WEBSITE_TOKEN').value).to eq '123'
- expect(InstallationConfig.find_by(name: 'CHATWOOT_SUPPORT_IDENTIFIER_HASH').value).to eq '123'
- expect(InstallationConfig.find_by(name: 'CHATWOOT_SUPPORT_SCRIPT_URL').value).to eq '123'
+
+ expect(reconsile_premium_config_service).not_to have_received(:perform)
end
- it 'calls Internal::ReconcilePlanConfigService' do
- data = { 'version' => '1.2.3' }
- allow(ChatwootHub).to receive(:sync_with_hub).and_return(data)
- job
- expect(reconsile_premium_config_service).to have_received(:perform)
+ it 'does not update installation configuration' do
+ expect { job }.not_to(
+ change do
+ InstallationConfig.where(name: %w[INSTALLATION_PRICING_PLAN INSTALLATION_PRICING_PLAN_QUANTITY]).pluck(:name, :serialized_value)
+ end
+ )
end
end
diff --git a/spec/jobs/internal/check_new_versions_job_spec.rb b/spec/jobs/internal/check_new_versions_job_spec.rb
index 82424e202..57418ccf1 100644
--- a/spec/jobs/internal/check_new_versions_job_spec.rb
+++ b/spec/jobs/internal/check_new_versions_job_spec.rb
@@ -3,12 +3,11 @@ require 'rails_helper'
RSpec.describe Internal::CheckNewVersionsJob do
subject(:job) { described_class.perform_now }
- it 'updates the latest chatwoot version in redis' do
- data = { 'version' => '1.2.3' }
- allow(Rails.env).to receive(:production?).and_return(true)
- allow(ChatwootHub).to receive(:sync_with_hub).and_return(data)
+ it 'does not write a remote version result' do
+ allow(Redis::Alfred).to receive(:set)
+
job
- expect(ChatwootHub).to have_received(:sync_with_hub)
- expect(Redis::Alfred.get(Redis::Alfred::LATEST_CHATWOOT_VERSION)).to eq data['version']
+
+ expect(Redis::Alfred).not_to have_received(:set)
end
end
diff --git a/spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb b/spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb
index 8b7a6da1c..0f21b4436 100644
--- a/spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb
+++ b/spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb
@@ -3,14 +3,8 @@ require 'rails_helper'
RSpec.describe Internal::TriggerDailyScheduledItemsJob do
subject(:perform_job) { described_class.perform_now }
- let(:installation_id) { 'test-installation-id' }
- let(:designated_minute) { Digest::MD5.hexdigest(installation_id).hex % 1440 }
- let(:scheduled_time) { Time.current.utc.beginning_of_day + designated_minute.minutes }
- let(:configured_job) { instance_double(ActiveJob::ConfiguredJob, perform_later: true) }
-
before do
- allow(ChatwootHub).to receive(:installation_identifier).and_return(installation_id)
- allow(Internal::CheckNewVersionsJob).to receive(:set).and_return(configured_job)
+ allow(Internal::CheckNewVersionsJob).to receive(:set)
end
it 'enqueues the job' do
@@ -18,20 +12,9 @@ RSpec.describe Internal::TriggerDailyScheduledItemsJob do
.on_queue('scheduled_jobs')
end
- it 'schedules the version check at a stable minute in production' do
+ it 'does not enqueue the remote version check in production' do
allow(Rails.env).to receive(:production?).and_return(true)
- travel_to Time.zone.parse('2026-03-17 08:00:00 UTC') do
- perform_job
-
- expect(Internal::CheckNewVersionsJob).to have_received(:set).with(wait_until: scheduled_time)
- expect(configured_job).to have_received(:perform_later)
- end
- end
-
- it 'does not schedule the version check outside production' do
- allow(Rails.env).to receive(:production?).and_return(false)
-
perform_job
expect(Internal::CheckNewVersionsJob).not_to have_received(:set)
diff --git a/spec/lib/chatwoot_hub_spec.rb b/spec/lib/chatwoot_hub_spec.rb
index a1051e619..237de5bba 100644
--- a/spec/lib/chatwoot_hub_spec.rb
+++ b/spec/lib/chatwoot_hub_spec.rb
@@ -1,6 +1,12 @@
require 'rails_helper'
describe ChatwootHub do
+ it 'does not expose remote sync, registration, or event APIs' do
+ expect(described_class).not_to respond_to(:sync_with_hub)
+ expect(described_class).not_to respond_to(:register_instance)
+ expect(described_class).not_to respond_to(:emit_event)
+ end
+
describe '.base_url' do
it 'uses the static hub url' do
expect(described_class::DEFAULT_BASE_URL).to eq('https://hub.2.chatwoot.com')
@@ -13,67 +19,4 @@ describe ChatwootHub do
expect(installation_identifier).not_to be_nil
expect(described_class.installation_identifier).to eq installation_identifier
end
-
- context 'when fetching sync_with_hub' do
- it 'get latest version from chatwoot hub' do
- version = '1.1.1'
- allow(RestClient).to receive(:post).and_return({ version: version }.to_json)
- expect(described_class.sync_with_hub['version']).to eq version
- expect(RestClient).to have_received(:post).with(described_class.ping_url, described_class.instance_config
- .merge(described_class.instance_metrics).to_json, { content_type: :json, accept: :json })
- end
-
- it 'will not send instance metrics when telemetry is disabled' do
- version = '1.1.1'
- with_modified_env DISABLE_TELEMETRY: 'true' do
- allow(RestClient).to receive(:post).and_return({ version: version }.to_json)
- expect(described_class.sync_with_hub['version']).to eq version
- expect(RestClient).to have_received(:post).with(described_class.ping_url,
- described_class.instance_config.to_json, { content_type: :json, accept: :json })
- end
- end
-
- it 'returns nil when chatwoot hub is down' do
- allow(RestClient).to receive(:post).and_raise(ExceptionList::REST_CLIENT_EXCEPTIONS.sample)
- expect(described_class.sync_with_hub).to be_nil
- end
- end
-
- context 'when register instance' do
- let(:company_name) { 'test' }
- let(:owner_name) { 'test' }
- let(:owner_email) { 'test@test.com' }
-
- it 'sends info of registration' do
- info = { company_name: company_name, owner_name: owner_name, owner_email: owner_email, subscribed_to_mailers: true }
- allow(RestClient).to receive(:post)
- described_class.register_instance(company_name, owner_name, owner_email)
- expect(RestClient).to have_received(:post).with(described_class.registration_url,
- info.merge(described_class.instance_config).to_json, { content_type: :json, accept: :json })
- end
- end
-
- context 'when sending events' do
- let(:event_name) { 'sample_event' }
- let(:event_data) { { 'sample_data' => 'sample_data' } }
-
- it 'will send instance events' do
- info = { event_name: event_name, event_data: event_data }
- allow(RestClient).to receive(:post)
- described_class.emit_event(event_name, event_data)
- expect(RestClient).to have_received(:post).with(described_class.events_url,
- info.merge(described_class.instance_config).to_json, { content_type: :json, accept: :json })
- end
-
- it 'will not send instance events when telemetry is disabled' do
- with_modified_env DISABLE_TELEMETRY: 'true' do
- info = { event_name: event_name, event_data: event_data }
- allow(RestClient).to receive(:post)
- described_class.emit_event(event_name, event_data)
- expect(RestClient).not_to have_received(:post)
- .with(described_class.events_url,
- info.merge(described_class.instance_config).to_json, { content_type: :json, accept: :json })
- end
- end
- end
end