Merge branch 'develop' into feature/cw-3191
This commit is contained in:
@@ -14,11 +14,7 @@
|
||||
@click="retrySendMessage"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-tooltip.top-start="messageToolTip"
|
||||
:class="bubbleClass"
|
||||
@contextmenu="openContextMenu($event)"
|
||||
>
|
||||
<div :class="bubbleClass" @contextmenu="openContextMenu($event)">
|
||||
<bubble-mail-head
|
||||
:email-attributes="contentAttributes.email"
|
||||
:cc="emailHeadAttributes.cc"
|
||||
@@ -90,7 +86,7 @@
|
||||
:id="data.id"
|
||||
:sender="data.sender"
|
||||
:story-sender="storySender"
|
||||
:external-error="externalError"
|
||||
:external-error="errorMessageTooltip"
|
||||
:story-id="`${storyId}`"
|
||||
:is-a-tweet="isATweet"
|
||||
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
||||
@@ -412,14 +408,11 @@ export default {
|
||||
}
|
||||
: false;
|
||||
},
|
||||
messageToolTip() {
|
||||
if (this.isMessageDeleted) {
|
||||
return false;
|
||||
}
|
||||
errorMessageTooltip() {
|
||||
if (this.isFailed) {
|
||||
return this.externalError || this.$t(`CONVERSATION.SEND_FAILED`);
|
||||
}
|
||||
return false;
|
||||
return '';
|
||||
},
|
||||
wrapClass() {
|
||||
return {
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
"SIDEBAR_SECTIONS": {
|
||||
"CUSTOM_ATTRIBUTES": "Custom Attributes",
|
||||
"CONTACT_LABELS": "Contact Labels",
|
||||
"PREVIOUS_CONVERSATIONS": "Previous Conversations"
|
||||
"PREVIOUS_CONVERSATIONS": "Previous Conversations",
|
||||
"NO_RECORDS_FOUND": "No attributes found"
|
||||
}
|
||||
},
|
||||
"EDIT_CONTACT": {
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
},
|
||||
"CONVERSATION_CUSTOM_ATTRIBUTES": {
|
||||
"ADD_BUTTON_TEXT": "Create attribute",
|
||||
"NO_RECORDS_FOUND": "No attributes found",
|
||||
"UPDATE": {
|
||||
"SUCCESS": "Attribute updated successfully",
|
||||
"ERROR": "Unable to update attribute. Please try again later"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-1/4 bg-white dark:bg-slate-900 border-slate-50 dark:border-slate-800/50 h-full text-sm overflow-y-auto relative"
|
||||
class="relative w-1/4 h-full overflow-y-auto text-sm bg-white dark:bg-slate-900 border-slate-50 dark:border-slate-800/50"
|
||||
:class="showAvatar ? 'border-l border-solid ' : 'border-r border-solid'"
|
||||
>
|
||||
<contact-info
|
||||
@@ -40,6 +40,9 @@
|
||||
attribute-class="conversation--attribute"
|
||||
attribute-from="contact_panel"
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
:empty-state-message="
|
||||
$t('CONTACT_PANEL.SIDEBAR_SECTIONS.NO_RECORDS_FOUND')
|
||||
"
|
||||
class="even"
|
||||
/>
|
||||
</accordion-item>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-300 border-slate-50 dark:border-slate-800/50 border-l rtl:border-l-0 rtl:border-r contact--panel overflow-y-auto"
|
||||
class="overflow-y-auto bg-white border-l dark:bg-slate-900 text-slate-900 dark:text-slate-300 border-slate-50 dark:border-slate-800/50 rtl:border-l-0 rtl:border-r contact--panel"
|
||||
>
|
||||
<contact-info
|
||||
:contact="contact"
|
||||
@@ -89,6 +89,9 @@
|
||||
class="even"
|
||||
attribute-from="conversation_contact_panel"
|
||||
:contact-id="contact.id"
|
||||
:empty-state-message="
|
||||
$t('CONVERSATION_CUSTOM_ATTRIBUTES.NO_RECORDS_FOUND')
|
||||
"
|
||||
/>
|
||||
</accordion-item>
|
||||
</div>
|
||||
|
||||
+10
@@ -17,6 +17,12 @@
|
||||
@delete="onDelete"
|
||||
@copy="onCopy"
|
||||
/>
|
||||
<p
|
||||
v-if="!displayedAttributes.length && emptyStateMessage"
|
||||
class="p-3 text-center"
|
||||
>
|
||||
{{ emptyStateMessage }}
|
||||
</p>
|
||||
<!-- Show more and show less buttons show it if the filteredAttributes length is greater than 5 -->
|
||||
<div v-if="filteredAttributes.length > 5" class="flex px-2 py-2">
|
||||
<woot-button
|
||||
@@ -59,6 +65,10 @@ export default {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
emptyStateMessage: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -59,6 +59,7 @@ class Message < ApplicationRecord
|
||||
}.to_json.freeze
|
||||
|
||||
before_validation :ensure_content_type
|
||||
before_validation :prevent_message_flooding
|
||||
before_save :ensure_processed_message_content
|
||||
before_save :ensure_in_reply_to
|
||||
|
||||
@@ -227,6 +228,18 @@ class Message < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def prevent_message_flooding
|
||||
# Added this to cover the validation specs in messages
|
||||
# We can revisit and see if we can remove this later
|
||||
return if conversation.blank?
|
||||
|
||||
# there are cases where automations can result in message loops, we need to prevent such cases.
|
||||
if conversation.messages.where('created_at >= ?', 1.minute.ago).count >= Limits.conversation_message_per_minute_limit
|
||||
Rails.logger.error "Too many message: Account Id - #{account_id} : Conversation id - #{conversation_id}"
|
||||
errors.add(:base, 'Too many messages')
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_processed_message_content
|
||||
text_content_quoted = content_attributes.dig(:email, :text_content, :quoted)
|
||||
html_content_quoted = content_attributes.dig(:email, :html_content, :quoted)
|
||||
|
||||
@@ -149,7 +149,7 @@ class Rack::Attack
|
||||
end
|
||||
|
||||
## Prevent abuse of contact search api
|
||||
throttle('/api/v1/accounts/:account_id/contacts/search', limit: 5, period: 1.minute) do |req|
|
||||
throttle('/api/v1/accounts/:account_id/contacts/search', limit: ENV.fetch('RATE_LIMIT_CONTACT_SEARCH', '100').to_i, period: 1.minute) do |req|
|
||||
match_data = %r{/api/v1/accounts/(?<account_id>\d+)/contacts/search}.match(req.path)
|
||||
match_data[:account_id] if match_data.present?
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ module Enterprise::Public::Api::V1::Portals::ArticlesController
|
||||
|
||||
def search_articles
|
||||
if @portal.account.feature_enabled?('help_center_embedding_search')
|
||||
@articles = @articles.vector_search(list_params) if list_params.present?
|
||||
@articles = @articles.vector_search(list_params) if list_params[:query].present?
|
||||
else
|
||||
super
|
||||
end
|
||||
|
||||
@@ -4,4 +4,8 @@ module Limits
|
||||
URL_LENGTH_LIMIT = 2048 # https://stackoverflow.com/questions/417142
|
||||
OUT_OF_OFFICE_MESSAGE_MAX_LENGTH = 10_000
|
||||
GREETING_MESSAGE_MAX_LENGTH = 10_000
|
||||
|
||||
def self.conversation_message_per_minute_limit
|
||||
ENV.fetch('CONVERSATION_MESSAGE_PER_MINUTE_LIMIT', '200').to_i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ RSpec.describe Message do
|
||||
end
|
||||
|
||||
describe 'length validations' do
|
||||
let(:message) { create(:message) }
|
||||
let!(:message) { create(:message) }
|
||||
|
||||
context 'when it validates name length' do
|
||||
it 'valid when within limit' do
|
||||
@@ -27,6 +27,17 @@ RSpec.describe Message do
|
||||
expect(message.errors[:processed_message_content]).to include('is too long (maximum is 150000 characters)')
|
||||
expect(message.errors[:content]).to include('is too long (maximum is 150000 characters)')
|
||||
end
|
||||
|
||||
it 'adds error in case of message flooding' do
|
||||
with_modified_env 'CONVERSATION_MESSAGE_PER_MINUTE_LIMIT': '2' do
|
||||
conversation = message.conversation
|
||||
create(:message, conversation: conversation)
|
||||
conv_new_message = build(:message, conversation: message.conversation)
|
||||
|
||||
expect(conv_new_message.valid?).to be false
|
||||
expect(conv_new_message.errors[:base]).to eq(['Too many messages'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user