feat: IP lookup (#1315)
- feature to store contact IP for accounts - IP lookup through geocoder gem - ability to do IP lookup through external APIs - add commit hook to prevent push to develop and master - migrations to fix default values for jsonb columns
This commit is contained in:
@@ -19,13 +19,16 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
||||
def create
|
||||
ActiveRecord::Base.transaction do
|
||||
@contact = Current.account.contacts.new(contact_params)
|
||||
set_ip
|
||||
@contact.save!
|
||||
@contact_inbox = build_contact_inbox
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@contact.update!(contact_update_params)
|
||||
@contact.assign_attributes(contact_update_params)
|
||||
set_ip
|
||||
@contact.save!
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: {
|
||||
message: e.record.errors.full_messages.join(', '),
|
||||
@@ -67,4 +70,11 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
||||
def fetch_contact
|
||||
@contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id])
|
||||
end
|
||||
|
||||
def set_ip
|
||||
return if @contact.account.feature_enabled?('ip_lookup')
|
||||
|
||||
@contact[:additional_attributes][:created_at_ip] ||= request.remote_ip
|
||||
@contact[:additional_attributes][:updated_at_ip] = request.remote_ip
|
||||
end
|
||||
end
|
||||
|
||||
57
app/jobs/contact_ip_lookup_job.rb
Normal file
57
app/jobs/contact_ip_lookup_job.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
require 'rubygems/package'
|
||||
|
||||
class ContactIpLookupJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(contact)
|
||||
return unless ensure_look_up_service
|
||||
|
||||
update_contact_location_from_ip(contact)
|
||||
rescue Errno::ETIMEDOUT => e
|
||||
Rails.logger.info "Exception: ip resolution failed : #{e.message}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_look_up_service
|
||||
return if ENV['IP_LOOKUP_SERVICE'].blank? || ENV['IP_LOOKUP_API_KEY'].blank?
|
||||
return true if ENV['IP_LOOKUP_SERVICE'].to_sym != :geoip2
|
||||
|
||||
ensure_look_up_db
|
||||
end
|
||||
|
||||
def update_contact_location_from_ip(contact)
|
||||
ip = get_contact_ip(contact)
|
||||
return if ip.blank?
|
||||
|
||||
contact.additional_attributes['city'] = Geocoder.search(ip).first.city
|
||||
contact.additional_attributes['country'] = Geocoder.search(ip).first.country
|
||||
contact.save!
|
||||
end
|
||||
|
||||
def get_contact_ip(contact)
|
||||
contact.additional_attributes['updated_at_ip'] || contact.additional_attributes['created_at_ip']
|
||||
end
|
||||
|
||||
def ensure_look_up_db
|
||||
return true if File.exist?(GeocoderConfiguration::LOOK_UP_DB)
|
||||
|
||||
setup_vendor_db
|
||||
end
|
||||
|
||||
def setup_vendor_db
|
||||
base_url = 'https://download.maxmind.com/app/geoip_download'
|
||||
source = URI.open("#{base_url}?edition_id=GeoLite2-City&suffix=tar.gz&license_key=#{ENV['IP_LOOKUP_API_KEY']}")
|
||||
tar_extract = Gem::Package::TarReader.new(Zlib::GzipReader.open(source))
|
||||
tar_extract.rewind
|
||||
|
||||
tar_extract.each do |entry|
|
||||
next unless entry.full_name.include?('GeoLite2-City.mmdb') && entry.file?
|
||||
|
||||
File.open GeocoderConfiguration::LOOK_UP_DB, 'wb' do |f|
|
||||
f.print entry.read
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -40,6 +40,7 @@ class Contact < ApplicationRecord
|
||||
before_validation :prepare_email_attribute
|
||||
after_create_commit :dispatch_create_event
|
||||
after_update_commit :dispatch_update_event
|
||||
after_commit :ip_lookup
|
||||
|
||||
def get_source_id(inbox_id)
|
||||
contact_inboxes.find_by!(inbox_id: inbox_id).source_id
|
||||
@@ -68,6 +69,12 @@ class Contact < ApplicationRecord
|
||||
}
|
||||
end
|
||||
|
||||
def ip_lookup
|
||||
return unless account.feature_enabled?('ip_lookup')
|
||||
|
||||
ContactIpLookupJob.perform_later(self)
|
||||
end
|
||||
|
||||
def prepare_email_attribute
|
||||
# So that the db unique constraint won't throw error when email is ''
|
||||
self.email = nil if email.blank?
|
||||
|
||||
Reference in New Issue
Block a user