Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f16c7e59b8 | ||
|
|
4d362da9f0 |
@@ -6,12 +6,8 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
|
||||
|
||||
def create
|
||||
@user = User.from_email(params[:email])
|
||||
if @user
|
||||
@user.send_reset_password_instructions
|
||||
build_response(I18n.t('messages.reset_password_success'), 200)
|
||||
else
|
||||
build_response(I18n.t('messages.reset_password_failure'), 404)
|
||||
end
|
||||
@user&.send_reset_password_instructions
|
||||
build_response(I18n.t('messages.reset_password'), 200)
|
||||
end
|
||||
|
||||
def update
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
class ReplyMailbox < ApplicationMailbox
|
||||
include IncomingEmailValidityHelper
|
||||
|
||||
attr_accessor :conversation, :processed_mail, :account
|
||||
attr_accessor :conversation, :processed_mail
|
||||
|
||||
before_processing :find_conversation
|
||||
|
||||
@@ -9,17 +7,12 @@ class ReplyMailbox < ApplicationMailbox
|
||||
# Return early if no conversation was found (e.g., notification emails, suspended accounts)
|
||||
return unless @conversation
|
||||
|
||||
decorate_mail
|
||||
unless incoming_email_from_valid_email?
|
||||
Rails.logger.info "Email #{mail.message_id} rejected - failed incoming email validity checks"
|
||||
return
|
||||
end
|
||||
|
||||
# Wrap everything in a transaction to ensure atomicity
|
||||
# This prevents orphan conversations if message/attachment creation fails
|
||||
# and ensures idempotency on job retry (conversation won't be duplicated)
|
||||
ActiveRecord::Base.transaction do
|
||||
persist_conversation_if_needed
|
||||
decorate_mail
|
||||
create_message
|
||||
add_attachments_to_message
|
||||
end
|
||||
@@ -29,7 +22,6 @@ class ReplyMailbox < ApplicationMailbox
|
||||
|
||||
def find_conversation
|
||||
@conversation = Mailbox::ConversationFinder.new(mail).find
|
||||
@account = @conversation&.account
|
||||
# Log when email is rejected
|
||||
Rails.logger.info "Email #{mail.message_id} rejected - no conversation found" unless @conversation
|
||||
end
|
||||
@@ -44,6 +36,6 @@ class ReplyMailbox < ApplicationMailbox
|
||||
end
|
||||
|
||||
def decorate_mail
|
||||
@processed_mail = MailPresenter.new(mail, @account)
|
||||
@processed_mail = MailPresenter.new(mail, @conversation.account)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -318,7 +318,9 @@ class Message < ApplicationRecord
|
||||
end
|
||||
|
||||
def update_contact_activity
|
||||
sender.update(last_activity_at: DateTime.now) if sender.is_a?(Contact)
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
sender.update_columns(last_activity_at: DateTime.now) if sender.is_a?(Contact)
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
|
||||
def update_waiting_since
|
||||
|
||||
@@ -174,31 +174,13 @@ class MailPresenter < SimpleDelegator
|
||||
end
|
||||
|
||||
def notification_email_from_chatwoot?
|
||||
sender_address = original_sender.to_s.downcase
|
||||
return false if sender_address.blank?
|
||||
|
||||
# Notification emails are sent via mailer sender email address.
|
||||
configured_sender = parse_mail_address(ENV.fetch('MAILER_SENDER_EMAIL', 'Chatwoot <accounts@chatwoot.com>'))&.address&.downcase
|
||||
return true if configured_sender.present? && sender_address.casecmp?(configured_sender)
|
||||
|
||||
reply_thread_email_from_chatwoot?(sender_address)
|
||||
# notification emails are send via mailer sender email address. so it should match
|
||||
configured_sender = Mail::Address.new(ENV.fetch('MAILER_SENDER_EMAIL', 'Chatwoot <accounts@chatwoot.com>')).address
|
||||
original_sender.to_s.casecmp?(configured_sender)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
REPLY_THREAD_SENDER_PATTERN = /^reply\+[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
||||
|
||||
def reply_thread_email_from_chatwoot?(sender_address)
|
||||
inbound_domain = @account&.inbound_email_domain.to_s.downcase
|
||||
return false if inbound_domain.blank?
|
||||
|
||||
local_part, domain = sender_address.split('@', 2)
|
||||
return false if local_part.blank? || domain.blank?
|
||||
return false unless domain.casecmp?(inbound_domain)
|
||||
|
||||
local_part.match?(REPLY_THREAD_SENDER_PATTERN)
|
||||
end
|
||||
|
||||
def parse_mail_address(email)
|
||||
return if email.blank?
|
||||
|
||||
|
||||
@@ -20,17 +20,12 @@ class Messages::SendEmailNotificationService
|
||||
|
||||
def should_send_email_notification?
|
||||
return false unless message.email_notifiable_message?
|
||||
return false if bot_sender_message?
|
||||
return false if message.conversation.contact.email.blank?
|
||||
return false unless message.account.within_email_rate_limit?
|
||||
|
||||
email_reply_enabled?
|
||||
end
|
||||
|
||||
def bot_sender_message?
|
||||
message.sender_type.in?(%w[AgentBot Captain::Assistant])
|
||||
end
|
||||
|
||||
def email_reply_enabled?
|
||||
inbox = message.inbox
|
||||
case inbox.channel.class.to_s
|
||||
|
||||
@@ -41,8 +41,7 @@ en:
|
||||
invalid_email: 'Please enter a valid email address'
|
||||
authentication_failed: 'Authentication failed. Please check your credentials and try again.'
|
||||
messages:
|
||||
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
|
||||
reset_password_failure: Uh ho! We could not find any user with the specified email.
|
||||
reset_password: Request for password reset is successful. A email with instructions will be sent to your email if it exists.
|
||||
reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
|
||||
login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
|
||||
saml_not_available: SAML authentication is not available in this installation.
|
||||
|
||||
Reference in New Issue
Block a user