From 4f08403c62f52458ee2606bbfc56e1c008bd5dd6 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Wed, 4 Feb 2026 22:34:28 -0600 Subject: [PATCH 1/3] CP-11565 - Update webhook secret to load for new accounts --- app/models/account.rb | 14 ++++++++++++++ bin/start_production | 7 +++++++ bin/start_staging | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/app/models/account.rb b/app/models/account.rb index d7d3c96d5..d397efd2e 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -57,6 +57,20 @@ class Account < ApplicationRecord validates :external_account_id, uniqueness: true, allow_nil: true + after_create :create_careerplug_webhook + + private + + def create_careerplug_webhook + return unless ENV['CAREERPLUG_WEBHOOK_SECRET'].present? + + webhook_urls.create!( + url: 'https://www.careerplug.com/api/docuseal/events', + events: %w[form.viewed form.started form.completed form.declined], + secret: { 'X-CareerPlug-Secret' => ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') } + ) + end + scope :active, -> { where(archived_at: nil) } def self.find_or_create_by_external_id(external_id, name, attributes = {}) diff --git a/bin/start_production b/bin/start_production index bad489746..626ede920 100755 --- a/bin/start_production +++ b/bin/start_production @@ -170,6 +170,7 @@ fetch_env_variables() { export SECURED_STORAGE_BUCKET=$(echo "$SECRET_JSON" | jq -r '.secured_storage_bucket') export SECURED_STORAGE_REGION=$(echo "$SECRET_JSON" | jq -r '.secured_storage_region') export ENCRYPTION_SECRET=$(echo "$SECRET_JSON" | jq -r '.ENCRYPTION_SECRET // empty') + export CAREERPLUG_WEBHOOK_SECRET=$(echo "$SECRET_JSON" | jq -r '.CAREERPLUG_WEBHOOK_SECRET // empty') # Validate that we got the values if [ "$DB_HOST" = "null" ] || [ "$REDIS_URL" = "null" ] || [ "$S3_ATTACHMENTS_BUCKET" = "null" ] || [ -z "$DB_HOST" ] || [ -z "$REDIS_URL" ] || [ -z "$S3_ATTACHMENTS_BUCKET" ]; then @@ -234,6 +235,12 @@ fetch_env_variables() { echo "✓ ENCRYPTION_SECRET written to .env.production" fi + # Add CareerPlug webhook secret if it exists + if [ -n "$CAREERPLUG_WEBHOOK_SECRET" ]; then + echo "CAREERPLUG_WEBHOOK_SECRET=$CAREERPLUG_WEBHOOK_SECRET" >> ./.env.production + echo "✓ CAREERPLUG_WEBHOOK_SECRET written to .env.production" + fi + echo "✓ Environment variables successfully retrieved and written to .env.production" } diff --git a/bin/start_staging b/bin/start_staging index 1c8512b4f..176c2a26d 100755 --- a/bin/start_staging +++ b/bin/start_staging @@ -174,6 +174,7 @@ fetch_env_variables() { export SECURED_STORAGE_BUCKET=$(echo "$SECRET_JSON" | jq -r '.secured_storage_bucket') export SECURED_STORAGE_REGION=$(echo "$SECRET_JSON" | jq -r '.secured_storage_region') export ENCRYPTION_SECRET=$(echo "$SECRET_JSON" | jq -r '.ENCRYPTION_SECRET // empty') + export CAREERPLUG_WEBHOOK_SECRET=$(echo "$SECRET_JSON" | jq -r '.CAREERPLUG_WEBHOOK_SECRET // empty') # Validate that we got the values @@ -239,6 +240,12 @@ fetch_env_variables() { echo "✓ ENCRYPTION_SECRET written to .env.staging" fi + # Add CareerPlug webhook secret if it exists + if [ -n "$CAREERPLUG_WEBHOOK_SECRET" ]; then + echo "CAREERPLUG_WEBHOOK_SECRET=$CAREERPLUG_WEBHOOK_SECRET" >> ./.env.staging + echo "✓ CAREERPLUG_WEBHOOK_SECRET written to .env.staging" + fi + echo "✓ Environment variables successfully retrieved and written to .env.staging" } From a6b11703a26c8ddedad34a3a7b4427b2c9644064 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Wed, 4 Feb 2026 22:39:30 -0600 Subject: [PATCH 2/3] CP-11565 - Rubocop fix --- app/models/account.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index d397efd2e..52d09ba37 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -59,18 +59,6 @@ class Account < ApplicationRecord after_create :create_careerplug_webhook - private - - def create_careerplug_webhook - return unless ENV['CAREERPLUG_WEBHOOK_SECRET'].present? - - webhook_urls.create!( - url: 'https://www.careerplug.com/api/docuseal/events', - events: %w[form.viewed form.started form.completed form.declined], - secret: { 'X-CareerPlug-Secret' => ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') } - ) - end - scope :active, -> { where(archived_at: nil) } def self.find_or_create_by_external_id(external_id, name, attributes = {}) @@ -86,4 +74,16 @@ def default_template_folder super || build_default_template_folder(name: TemplateFolder::DEFAULT_NAME, author_id: users.minimum(:id)).tap(&:save!) end + + private + + def create_careerplug_webhook + return if ENV['CAREERPLUG_WEBHOOK_SECRET'].blank? + + webhook_urls.create!( + url: 'https://www.careerplug.com/api/docuseal/events', + events: %w[form.viewed form.started form.completed form.declined], + secret: { 'X-CareerPlug-Secret' => ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') } + ) + end end From d8115dc59c710d34782435312f4f501c80c1f0db Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Mon, 9 Feb 2026 16:57:48 -0600 Subject: [PATCH 3/3] CP-11565 - Make it easier to configure webhooks --- lib/tasks/webhooks.rake | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 lib/tasks/webhooks.rake diff --git a/lib/tasks/webhooks.rake b/lib/tasks/webhooks.rake new file mode 100644 index 000000000..cd892c335 --- /dev/null +++ b/lib/tasks/webhooks.rake @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +namespace :webhooks do + desc 'Configure CareerPlug webhook secret from CAREERPLUG_WEBHOOK_SECRET env var' + task configure_careerplug: :environment do + secret = ENV.fetch('CAREERPLUG_WEBHOOK_SECRET') do + if Rails.env.development? + 'development_webhook_secret' + else + abort 'CAREERPLUG_WEBHOOK_SECRET environment variable is required' + end + end + + webhook_urls = WebhookUrl.where('url LIKE ? OR url LIKE ? OR url LIKE ?', + '%careerplug%', '%cpats%', '%localhost:3000%') + + if webhook_urls.any? + webhook_urls.find_each do |webhook_url| + webhook_url.update!(secret: { 'X-CareerPlug-Secret' => secret }) + puts "Updated webhook secret for #{webhook_url.url}" + end + puts "Updated #{webhook_urls.count} webhook URL(s)" + else + puts 'No CareerPlug webhook URLs found. Available webhooks:' + WebhookUrl.find_each { |w| puts " - #{w.id}: #{w.url}" } + end + end + + desc 'Set up development webhook URLs for all accounts (creates URLs + configures secret)' + task setup_development: :environment do + abort 'This task is only for development' unless Rails.env.development? + + url = 'http://localhost:3000/api/docuseal/events' + secret = { 'X-CareerPlug-Secret' => 'development_webhook_secret' } + events = %w[form.viewed form.started form.completed form.declined] + + created = 0 + updated = 0 + + Account.find_each do |account| + webhook_url = WebhookUrl.find_or_initialize_by(account: account, sha1: Digest::SHA1.hexdigest(url)) + + if webhook_url.new_record? + webhook_url.assign_attributes(url: url, events: events, secret: secret) + webhook_url.save! + created += 1 + puts "Created webhook URL for account #{account.id}: #{account.name}" + elsif webhook_url.secret != secret + webhook_url.update!(secret: secret) + updated += 1 + puts "Updated webhook secret for account #{account.id}: #{account.name}" + end + end + + puts "Done: #{created} created, #{updated} updated" + end +end