From 3edc636b7687dce4841eb86fa059e0ec5d0eeed9 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 1 Aug 2024 11:30:01 +0530 Subject: [PATCH 01/23] fix: Instagram outgoing attachments (#9863) Fixes https://linear.app/chatwoot/issue/CW-3497/instagram-outgoing-attachments-are-not-rendering It seems like Instagram outgoing attachment rendering has been broken for a long time. We couldn't identify the issue because the check below only inspects Instagram mentions. ``` metadata[:data_url] = metadata[:thumb_url] = external_url if message.instagram_story_mention? ``` We recently worked on adding the [Instagram CDN URL for attachments.](https://github.com/chatwoot/chatwoot/pull/9287) After that, it started using external URLs as attachment data URLs for both outgoing and incoming attachments. The rendering broken for all the outgoing attachments since there were no external URLs, making the data URL empty. Adding an incoming message check will solve the issue. --------- Co-authored-by: Sojan --- app/models/attachment.rb | 2 +- spec/models/attachment_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 78f77bcf6..b9ed804d3 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -81,7 +81,7 @@ class Attachment < ApplicationRecord height: file.metadata[:height] } - metadata[:data_url] = metadata[:thumb_url] = external_url if message.inbox.instagram? + metadata[:data_url] = metadata[:thumb_url] = external_url if message.inbox.instagram? && message.incoming? metadata end diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb index 64a506a92..6b05d26fb 100644 --- a/spec/models/attachment_spec.rb +++ b/spec/models/attachment_spec.rb @@ -44,9 +44,14 @@ RSpec.describe Attachment do }.to_json, headers: {}) end - it 'returns external url as data and thumb urls' do + it 'returns external url as data and thumb urls when message is incoming' do external_url = instagram_message.attachments.first.external_url expect(instagram_message.attachments.first.push_event_data[:data_url]).to eq external_url end + + it 'returns original attachment url as data url if the message is outgoing' do + message = create(:message, :instagram_story_mention, message_type: :outgoing) + expect(message.attachments.first.push_event_data[:data_url]).not_to eq message.attachments.first.external_url + end end end From 17a6df10a08542b68e6943dfaefc98f1ea6a2e54 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 1 Aug 2024 16:39:11 -0700 Subject: [PATCH 02/23] chore: Security advisory fix for rexml (#9872) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index be6a33af0..cf522bf39 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -634,7 +634,7 @@ GEM retriable (3.1.2) reverse_markdown (2.1.1) nokogiri - rexml (3.3.2) + rexml (3.3.4) strscan rspec-core (3.13.0) rspec-support (~> 3.13.0) From 829bb842fd8a7902e440b1d5e747f5cd94077922 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 1 Aug 2024 19:22:34 -0700 Subject: [PATCH 03/23] feat: Generate SSO URL in Chatwoot, move Captain to primary tab (#9871) - Generate SSO URL in Chatwoot, move Captain to the primary tab Co-authored-by: Pranav --- .env.example | 1 - .../integrations/captain_controller.rb | 20 +++++ app/javascript/dashboard/api/integrations.js | 4 + .../layout/config/sidebarItems/primaryMenu.js | 8 ++ app/javascript/dashboard/featureFlags.js | 1 + .../i18n/locale/en/integrations.json | 6 ++ .../dashboard/i18n/locale/en/settings.json | 7 +- .../dashboard/routes/dashboard/Captain.vue | 75 +++++++++++++++++++ .../routes/dashboard/dashboard.routes.js | 9 +++ .../dashboard/store/modules/integrations.js | 14 ++-- .../FluentIcon/dashboard-icons.json | 17 ++++- config/installation_config.yml | 11 +++ config/routes.rb | 5 ++ .../super_admin/app_configs_controller.rb | 2 + lib/integrations/captain/processor_service.rb | 3 +- .../integrations/captain_controller_spec.rb | 66 ++++++++++++++++ 16 files changed, 237 insertions(+), 12 deletions(-) create mode 100644 app/controllers/api/v1/accounts/integrations/captain_controller.rb create mode 100644 app/javascript/dashboard/routes/dashboard/Captain.vue create mode 100644 spec/controllers/api/v1/accounts/integrations/captain_controller_spec.rb diff --git a/.env.example b/.env.example index 81228f00a..b7ba0920d 100644 --- a/.env.example +++ b/.env.example @@ -258,4 +258,3 @@ AZURE_APP_SECRET= # contact_inboxes with no conversation older than 90 days will be removed # REMOVE_STALE_CONTACT_INBOX_JOB_STATUS=false -# CAPTAIN_API_URL=http://localhost:3001/api diff --git a/app/controllers/api/v1/accounts/integrations/captain_controller.rb b/app/controllers/api/v1/accounts/integrations/captain_controller.rb new file mode 100644 index 000000000..88547209c --- /dev/null +++ b/app/controllers/api/v1/accounts/integrations/captain_controller.rb @@ -0,0 +1,20 @@ +class Api::V1::Accounts::Integrations::CaptainController < Api::V1::Accounts::BaseController + before_action :check_admin_authorization? + before_action :fetch_hook + + def sso_url + params_string = + "token=#{URI.encode_www_form_component(@hook['settings']['access_token'])}" \ + "&email=#{URI.encode_www_form_component(@hook['settings']['account_email'])}" \ + "&account_id=#{URI.encode_www_form_component(@hook['settings']['account_id'])}" + + sso_url = "#{ENV.fetch('CAPTAIN_APP_URL', '')}/sso?#{params_string}" + render json: { sso_url: sso_url }, status: :ok + end + + private + + def fetch_hook + @hook = Current.account.hooks.find_by!(app_id: 'captain') + end +end diff --git a/app/javascript/dashboard/api/integrations.js b/app/javascript/dashboard/api/integrations.js index 2b816e603..975857ce1 100644 --- a/app/javascript/dashboard/api/integrations.js +++ b/app/javascript/dashboard/api/integrations.js @@ -32,6 +32,10 @@ class IntegrationsAPI extends ApiClient { deleteHook(hookId) { return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`); } + + fetchCaptainURL() { + return axios.get(`${this.baseUrl()}/integrations/captain/sso_url`); + } } export default new IntegrationsAPI(); diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js index 92b8765c6..f019933e0 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/primaryMenu.js @@ -17,6 +17,14 @@ const primaryMenuItems = accountId => [ toState: frontendURL(`accounts/${accountId}/dashboard`), toStateName: 'home', }, + { + icon: 'captain', + key: 'captain', + label: 'CAPTAIN', + featureFlag: FEATURE_FLAGS.CAPTAIN, + toState: frontendURL(`accounts/${accountId}/captain`), + toStateName: 'captain', + }, { icon: 'book-contacts', key: 'contacts', diff --git a/app/javascript/dashboard/featureFlags.js b/app/javascript/dashboard/featureFlags.js index 11d6c43de..54b560ed7 100644 --- a/app/javascript/dashboard/featureFlags.js +++ b/app/javascript/dashboard/featureFlags.js @@ -31,4 +31,5 @@ export const FEATURE_FLAGS = { INBOUND_EMAILS: 'inbound_emails', IP_LOOKUP: 'ip_lookup', LINEAR: 'linear_integration', + CAPTAIN: 'captain_integration', }; diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index 80742f881..47349d1b1 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -4,6 +4,12 @@ "DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.", "LEARN_MORE": "Learn more about integrations", "LOADING": "Fetching integrations", + "CAPTAIN": { + "DISABLED": "Captain is not enabled on your account.", + "CLICK_HERE_TO_CONFIGURE": "Click here to configure", + "LOADING_CONSOLE": "Loading Captain Console...", + "FAILED_TO_LOAD_CONSOLE": "Failed to load Captain Console. Please refresh and try again." + }, "WEBHOOK": { "SUBSCRIBED_EVENTS": "Subscribed Events", "FORM": { diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json index 445b7244e..dabbff89f 100644 --- a/app/javascript/dashboard/i18n/locale/en/settings.json +++ b/app/javascript/dashboard/i18n/locale/en/settings.json @@ -145,7 +145,11 @@ }, "AVAILABILITY": { "LABEL": "Availability", - "STATUSES_LIST": ["Online", "Busy", "Offline"], + "STATUSES_LIST": [ + "Online", + "Busy", + "Offline" + ], "SET_AVAILABILITY_SUCCESS": "Availability has been set successfully", "SET_AVAILABILITY_ERROR": "Couldn't set availability, please try again" }, @@ -235,6 +239,7 @@ "REPORTS": "Reports", "SETTINGS": "Settings", "CONTACTS": "Contacts", + "CAPTAIN": "Captain", "HOME": "Home", "AGENTS": "Agents", "AGENT_BOTS": "Bots", diff --git a/app/javascript/dashboard/routes/dashboard/Captain.vue b/app/javascript/dashboard/routes/dashboard/Captain.vue new file mode 100644 index 000000000..24ba89272 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/Captain.vue @@ -0,0 +1,75 @@ + + +