chore: Automate SSL with Cloudflare (#12021)

This PR adds support for automatic SSL issuance using Cloudflare when a
custom domain is updated.

- Introduced a cloudflare configuration. If present, the system will
attempt to issue an SSL certificate via Cloudflare whenever a custom
domain is added or changed.
- SSL verification is handled using an HTTP challenge.
- The job will store the HTTP challenge response provided by Cloudflare
and serve it under the /.well-known/cf path automatically.

How to test:

- Create a Cloudflare zone for your domain and copy the Zone ID.
- Generate a Cloudflare API token with the required SSL certificate
permissions.
- Set the Fallback Origin under SSL -> Custom HostName to the Chatwoot
installation.
- Add or update a custom domain and verify that the SSL certificate is
automatically issued.

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
Pranav
2025-07-24 13:09:06 +04:00
committed by GitHub
parent 9acb0d86b5
commit 420be64c45
17 changed files with 507 additions and 3 deletions

View File

@@ -0,0 +1,59 @@
require 'rails_helper'
RSpec.describe Enterprise::Concerns::Portal do
describe '#enqueue_cloudflare_verification' do
let(:portal) { create(:portal, custom_domain: nil) }
context 'when custom_domain is changed' do
context 'when on chatwoot cloud' do
before do
allow(ChatwootApp).to receive(:chatwoot_cloud?).and_return(true)
end
it 'enqueues cloudflare verification job' do
expect do
portal.update(custom_domain: 'test.example.com')
end.to have_enqueued_job(Enterprise::CloudflareVerificationJob).with(portal.id)
end
end
context 'when not on chatwoot cloud' do
before do
allow(ChatwootApp).to receive(:chatwoot_cloud?).and_return(false)
end
it 'does not enqueue cloudflare verification job' do
expect do
portal.update(custom_domain: 'test.example.com')
end.not_to have_enqueued_job(Enterprise::CloudflareVerificationJob)
end
end
end
context 'when custom_domain is not changed' do
before do
allow(ChatwootApp).to receive(:chatwoot_cloud?).and_return(true)
portal.update(custom_domain: 'test.example.com')
end
it 'does not enqueue cloudflare verification job' do
expect do
portal.update(name: 'New Name')
end.not_to have_enqueued_job(Enterprise::CloudflareVerificationJob)
end
end
context 'when custom_domain is set to blank' do
before do
allow(ChatwootApp).to receive(:chatwoot_cloud?).and_return(true)
portal.update(custom_domain: 'test.example.com')
end
it 'does not enqueue cloudflare verification job' do
expect do
portal.update(custom_domain: '')
end.not_to have_enqueued_job(Enterprise::CloudflareVerificationJob)
end
end
end
end