diff --git a/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb b/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb new file mode 100644 index 000000000..795d7f2a9 --- /dev/null +++ b/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb @@ -0,0 +1,55 @@ +module Api::V1::Accounts::Concerns::WhatsappHealthManagement + extend ActiveSupport::Concern + + included do + skip_before_action :check_authorization, only: [:health, :register_webhook] + before_action :check_admin_authorization?, only: [:register_webhook] + before_action :validate_whatsapp_cloud_channel, only: [:health, :register_webhook] + end + + def sync_templates + return render status: :unprocessable_entity, json: { error: 'Template sync is only available for WhatsApp channels' } unless whatsapp_channel? + + trigger_template_sync + render status: :ok, json: { message: 'Template sync initiated successfully' } + rescue StandardError => e + render status: :internal_server_error, json: { error: e.message } + end + + def health + health_data = Whatsapp::HealthService.new(@inbox.channel).fetch_health_status + render json: health_data + rescue StandardError => e + Rails.logger.error "[INBOX HEALTH] Error fetching health data: #{e.message}" + render json: { error: e.message }, status: :unprocessable_entity + end + + def register_webhook + Whatsapp::WebhookSetupService.new(@inbox.channel).register_callback + + render json: { message: 'Webhook registered successfully' }, status: :ok + rescue StandardError => e + Rails.logger.error "[INBOX WEBHOOK] Webhook registration failed: #{e.message}" + render json: { error: e.message }, status: :unprocessable_entity + end + + private + + def validate_whatsapp_cloud_channel + return if @inbox.channel.is_a?(Channel::Whatsapp) && @inbox.channel.provider == 'whatsapp_cloud' + + render json: { error: 'Health data only available for WhatsApp Cloud API channels' }, status: :bad_request + end + + def whatsapp_channel? + @inbox.whatsapp? || (@inbox.twilio? && @inbox.channel.whatsapp?) + end + + def trigger_template_sync + if @inbox.whatsapp? + Channels::Whatsapp::TemplatesSyncJob.perform_later(@inbox.channel) + elsif @inbox.twilio? && @inbox.channel.whatsapp? + Channels::Twilio::TemplatesSyncJob.perform_later(@inbox.channel) + end + end +end diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb index 322c7c7fe..4ca9a6af8 100644 --- a/app/controllers/api/v1/accounts/inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/inboxes_controller.rb @@ -4,8 +4,9 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController before_action :fetch_agent_bot, only: [:set_agent_bot] before_action :validate_limit, only: [:create] # we are already handling the authorization in fetch inbox - before_action :check_authorization, except: [:show, :health] - before_action :validate_whatsapp_cloud_channel, only: [:health] + before_action :check_authorization, except: [:show] + + include Api::V1::Accounts::Concerns::WhatsappHealthManagement def index @inboxes = policy_scope(Current.account.inboxes.order_by_name.includes(:channel, { avatar_attachment: [:blob] })) @@ -70,23 +71,6 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController render status: :ok, json: { message: I18n.t('messages.inbox_deletetion_response') } end - def sync_templates - return render status: :unprocessable_entity, json: { error: 'Template sync is only available for WhatsApp channels' } unless whatsapp_channel? - - trigger_template_sync - render status: :ok, json: { message: 'Template sync initiated successfully' } - rescue StandardError => e - render status: :internal_server_error, json: { error: e.message } - end - - def health - health_data = Whatsapp::HealthService.new(@inbox.channel).fetch_health_status - render json: health_data - rescue StandardError => e - Rails.logger.error "[INBOX HEALTH] Error fetching health data: #{e.message}" - render json: { error: e.message }, status: :unprocessable_entity - end - private def fetch_inbox @@ -98,12 +82,6 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController @agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot] end - def validate_whatsapp_cloud_channel - return if @inbox.channel.is_a?(Channel::Whatsapp) && @inbox.channel.provider == 'whatsapp_cloud' - - render json: { error: 'Health data only available for WhatsApp Cloud API channels' }, status: :bad_request - end - def create_channel return unless allowed_channel_types.include?(permitted_params[:channel][:type]) @@ -200,18 +178,6 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController def get_channel_attributes(channel_type) channel_type.constantize.const_defined?(:EDITABLE_ATTRS) ? channel_type.constantize::EDITABLE_ATTRS.presence : [] end - - def whatsapp_channel? - @inbox.whatsapp? || (@inbox.twilio? && @inbox.channel.whatsapp?) - end - - def trigger_template_sync - if @inbox.whatsapp? - Channels::Whatsapp::TemplatesSyncJob.perform_later(@inbox.channel) - elsif @inbox.twilio? && @inbox.channel.whatsapp? - Channels::Twilio::TemplatesSyncJob.perform_later(@inbox.channel) - end - end end Api::V1::Accounts::InboxesController.prepend_mod_with('Api::V1::Accounts::InboxesController') diff --git a/app/controllers/api/v1/widget/contacts_controller.rb b/app/controllers/api/v1/widget/contacts_controller.rb index 5138fe675..6c595ab59 100644 --- a/app/controllers/api/v1/widget/contacts_controller.rb +++ b/app/controllers/api/v1/widget/contacts_controller.rb @@ -19,7 +19,7 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController contact = @contact end - @contact_inbox.update(hmac_verified: true) if should_verify_hmac? && valid_hmac? + @contact_inbox.update(hmac_verified: true) if should_verify_hmac? identify_contact(contact) end diff --git a/app/javascript/dashboard/api/inboxHealth.js b/app/javascript/dashboard/api/inboxHealth.js index 181b041ba..b8f69fcfe 100644 --- a/app/javascript/dashboard/api/inboxHealth.js +++ b/app/javascript/dashboard/api/inboxHealth.js @@ -9,6 +9,10 @@ class InboxHealthAPI extends ApiClient { getHealthStatus(inboxId) { return axios.get(`${this.url}/${inboxId}/health`); } + + registerWebhook(inboxId) { + return axios.post(`${this.url}/${inboxId}/register_webhook`); + } } export default new InboxHealthAPI(); diff --git a/app/javascript/dashboard/components-next/HelpCenter/EmptyState/Portal/PortalEmptyState.vue b/app/javascript/dashboard/components-next/HelpCenter/EmptyState/Portal/PortalEmptyState.vue index b7a986254..192e1d5ee 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/EmptyState/Portal/PortalEmptyState.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/EmptyState/Portal/PortalEmptyState.vue @@ -26,6 +26,7 @@ const onPortalCreate = ({ slug: portalSlug, locale }) => {