Initial Commit
Co-authored-by: Subin <subinthattaparambil@gmail.com> Co-authored-by: Manoj <manojmj92@gmail.com> Co-authored-by: Nithin <webofnithin@gmail.com>
This commit is contained in:
commit
2a34255e0b
537 changed files with 27318 additions and 0 deletions
1
.browserslistrc
Normal file
1
.browserslistrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
defaults
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||||
|
#
|
||||||
|
# If you find yourself ignoring temporary files generated by your text editor
|
||||||
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
|
# git config --global core.excludesfile '~/.gitignore_global'
|
||||||
|
|
||||||
|
# Ignore bundler config.
|
||||||
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore the default SQLite database.
|
||||||
|
/db/*.sqlite3
|
||||||
|
/db/*.sqlite3-journal
|
||||||
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
/tmp/*
|
||||||
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
|
||||||
|
# Ignore Byebug command history file.
|
||||||
|
.byebug_history
|
||||||
|
config/database.yml
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
# Ignore application configuration
|
||||||
|
node_modules
|
13
Capfile
Normal file
13
Capfile
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Load DSL and Setup Up Stages
|
||||||
|
require 'capistrano/setup'
|
||||||
|
require 'capistrano/deploy'
|
||||||
|
|
||||||
|
require 'capistrano/rails'
|
||||||
|
require 'capistrano/bundler'
|
||||||
|
require 'capistrano/rvm'
|
||||||
|
require 'capistrano/puma'
|
||||||
|
install_plugin Capistrano::Puma
|
||||||
|
|
||||||
|
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
|
||||||
|
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
||||||
|
|
69
Gemfile
Normal file
69
Gemfile
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
|
||||||
|
gem 'sass-rails', '~> 5.0'
|
||||||
|
gem 'puma', '~> 3.0'
|
||||||
|
gem 'uglifier', '>= 1.3.0'
|
||||||
|
gem 'coffee-rails', '~> 4.2'
|
||||||
|
gem 'therubyracer', platforms: :ruby
|
||||||
|
gem 'jquery-rails'
|
||||||
|
gem 'jbuilder', '~> 2.5'
|
||||||
|
gem 'redis', '~> 3.0'
|
||||||
|
gem 'devise'
|
||||||
|
gem 'pg'
|
||||||
|
gem 'facebook-messenger', '~> 0.11.1'
|
||||||
|
gem 'sidekiq'
|
||||||
|
gem "koala"
|
||||||
|
gem 'omniauth-facebook'
|
||||||
|
gem 'rest-client'
|
||||||
|
gem 'telegram-bot-ruby'
|
||||||
|
gem 'devise_token_auth'
|
||||||
|
gem 'pusher'
|
||||||
|
gem 'responders'
|
||||||
|
gem 'kaminari'
|
||||||
|
gem 'rack-cors', :require => 'rack/cors'
|
||||||
|
gem 'acts-as-taggable-on', '~> 4.0'
|
||||||
|
gem 'sinatra', github: 'sinatra'
|
||||||
|
gem 'wisper', '2.0.0'
|
||||||
|
gem 'nightfury', '~> 1.0', '>= 1.0.1'
|
||||||
|
gem 'redis-namespace'
|
||||||
|
gem 'redis-rack-cache'
|
||||||
|
gem 'redis-rails'
|
||||||
|
gem "figaro"
|
||||||
|
gem "pundit"
|
||||||
|
gem 'carrierwave-aws'
|
||||||
|
gem "mini_magick"
|
||||||
|
gem "sentry-raven"
|
||||||
|
gem "valid_email2"
|
||||||
|
gem 'hashie'
|
||||||
|
gem 'chargebee', '~>2'
|
||||||
|
gem 'poltergeist'
|
||||||
|
gem 'phantomjs', :require => 'phantomjs/poltergeist'
|
||||||
|
gem 'time_diff'
|
||||||
|
gem 'fog-digitalocean'
|
||||||
|
gem 'fog-aws'
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
|
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||||
|
gem 'byebug', platform: :mri
|
||||||
|
gem 'capistrano', require: false
|
||||||
|
gem 'capistrano-rvm', require: false
|
||||||
|
gem 'capistrano-rails', require: false
|
||||||
|
gem 'capistrano-bundler', require: false
|
||||||
|
gem 'capistrano3-puma', require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
|
||||||
|
gem 'web-console'
|
||||||
|
gem 'listen', '~> 3.0.5'
|
||||||
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||||
|
gem 'spring'
|
||||||
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||||
|
gem 'seed_dump'
|
||||||
|
gem 'mailcatcher'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
|
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||||
|
gem 'webpacker'
|
491
Gemfile.lock
Normal file
491
Gemfile.lock
Normal file
|
@ -0,0 +1,491 @@
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/sinatra/sinatra.git
|
||||||
|
revision: 8fdd35c731ec6915bae393c6383b2357450665f0
|
||||||
|
specs:
|
||||||
|
rack-protection (2.0.0.rc2)
|
||||||
|
rack
|
||||||
|
sinatra (2.0.0.rc2)
|
||||||
|
mustermann (~> 1.0)
|
||||||
|
rack (~> 2.0)
|
||||||
|
rack-protection (= 2.0.0.rc2)
|
||||||
|
tilt (~> 2.0)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
actioncable (5.0.2)
|
||||||
|
actionpack (= 5.0.2)
|
||||||
|
nio4r (>= 1.2, < 3.0)
|
||||||
|
websocket-driver (~> 0.6.1)
|
||||||
|
actionmailer (5.0.2)
|
||||||
|
actionpack (= 5.0.2)
|
||||||
|
actionview (= 5.0.2)
|
||||||
|
activejob (= 5.0.2)
|
||||||
|
mail (~> 2.5, >= 2.5.4)
|
||||||
|
rails-dom-testing (~> 2.0)
|
||||||
|
actionpack (5.0.2)
|
||||||
|
actionview (= 5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
rack (~> 2.0)
|
||||||
|
rack-test (~> 0.6.3)
|
||||||
|
rails-dom-testing (~> 2.0)
|
||||||
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
|
actionview (5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
builder (~> 3.1)
|
||||||
|
erubis (~> 2.7.0)
|
||||||
|
rails-dom-testing (~> 2.0)
|
||||||
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
|
activejob (5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
globalid (>= 0.3.6)
|
||||||
|
activemodel (5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
activerecord (5.0.2)
|
||||||
|
activemodel (= 5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
arel (~> 7.0)
|
||||||
|
activesupport (5.0.2)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (~> 0.7)
|
||||||
|
minitest (~> 5.1)
|
||||||
|
tzinfo (~> 1.1)
|
||||||
|
acts-as-taggable-on (4.0.0)
|
||||||
|
activerecord (>= 4.0)
|
||||||
|
addressable (2.5.1)
|
||||||
|
public_suffix (~> 2.0, >= 2.0.2)
|
||||||
|
airbrussh (1.2.0)
|
||||||
|
sshkit (>= 1.6.1, != 1.7.0)
|
||||||
|
arel (7.1.4)
|
||||||
|
aws-sdk (2.9.11)
|
||||||
|
aws-sdk-resources (= 2.9.11)
|
||||||
|
aws-sdk-core (2.9.11)
|
||||||
|
aws-sigv4 (~> 1.0)
|
||||||
|
jmespath (~> 1.0)
|
||||||
|
aws-sdk-resources (2.9.11)
|
||||||
|
aws-sdk-core (= 2.9.11)
|
||||||
|
aws-sigv4 (1.0.0)
|
||||||
|
axiom-types (0.1.1)
|
||||||
|
descendants_tracker (~> 0.0.4)
|
||||||
|
ice_nine (~> 0.11.0)
|
||||||
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
bcrypt (3.1.11)
|
||||||
|
bindex (0.5.0)
|
||||||
|
builder (3.2.3)
|
||||||
|
byebug (9.0.6)
|
||||||
|
capistrano (3.8.1)
|
||||||
|
airbrussh (>= 1.0.0)
|
||||||
|
i18n
|
||||||
|
rake (>= 10.0.0)
|
||||||
|
sshkit (>= 1.9.0)
|
||||||
|
capistrano-bundler (1.2.0)
|
||||||
|
capistrano (~> 3.1)
|
||||||
|
sshkit (~> 1.2)
|
||||||
|
capistrano-rails (1.2.3)
|
||||||
|
capistrano (~> 3.1)
|
||||||
|
capistrano-bundler (~> 1.1)
|
||||||
|
capistrano-rvm (0.1.2)
|
||||||
|
capistrano (~> 3.0)
|
||||||
|
sshkit (~> 1.2)
|
||||||
|
capistrano3-puma (3.1.0)
|
||||||
|
capistrano (~> 3.7)
|
||||||
|
capistrano-bundler
|
||||||
|
puma (~> 3.4)
|
||||||
|
capybara (2.14.0)
|
||||||
|
addressable
|
||||||
|
mime-types (>= 1.16)
|
||||||
|
nokogiri (>= 1.3.3)
|
||||||
|
rack (>= 1.0.0)
|
||||||
|
rack-test (>= 0.5.4)
|
||||||
|
xpath (~> 2.0)
|
||||||
|
carrierwave (1.3.1)
|
||||||
|
activemodel (>= 4.0.0)
|
||||||
|
activesupport (>= 4.0.0)
|
||||||
|
mime-types (>= 1.16)
|
||||||
|
carrierwave-aws (1.1.0)
|
||||||
|
aws-sdk (~> 2.0)
|
||||||
|
carrierwave (>= 0.7, < 2.0)
|
||||||
|
chargebee (2.2.7)
|
||||||
|
json_pure (~> 1.5)
|
||||||
|
rest-client (~> 1.4)
|
||||||
|
cliver (0.3.2)
|
||||||
|
coercible (1.0.0)
|
||||||
|
descendants_tracker (~> 0.0.1)
|
||||||
|
coffee-rails (4.2.1)
|
||||||
|
coffee-script (>= 2.2.0)
|
||||||
|
railties (>= 4.0.0, < 5.2.x)
|
||||||
|
coffee-script (2.4.1)
|
||||||
|
coffee-script-source
|
||||||
|
execjs
|
||||||
|
coffee-script-source (1.12.2)
|
||||||
|
concurrent-ruby (1.1.4)
|
||||||
|
connection_pool (2.2.1)
|
||||||
|
daemons (1.2.4)
|
||||||
|
descendants_tracker (0.0.4)
|
||||||
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
devise (4.2.0)
|
||||||
|
bcrypt (~> 3.0)
|
||||||
|
orm_adapter (~> 0.1)
|
||||||
|
railties (>= 4.1.0, < 5.1)
|
||||||
|
responders
|
||||||
|
warden (~> 1.2.3)
|
||||||
|
devise_token_auth (0.1.40)
|
||||||
|
devise (> 3.5.2, <= 4.2)
|
||||||
|
rails (< 6)
|
||||||
|
domain_name (0.5.20170404)
|
||||||
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
equalizer (0.0.11)
|
||||||
|
erubis (2.7.0)
|
||||||
|
eventmachine (1.0.9.1)
|
||||||
|
excon (0.62.0)
|
||||||
|
execjs (2.7.0)
|
||||||
|
facebook-messenger (0.11.1)
|
||||||
|
httparty (~> 0.13, >= 0.13.7)
|
||||||
|
rack (>= 1.6.4)
|
||||||
|
faraday (0.11.0)
|
||||||
|
multipart-post (>= 1.2, < 3)
|
||||||
|
ffi (1.9.18)
|
||||||
|
figaro (1.1.1)
|
||||||
|
thor (~> 0.14)
|
||||||
|
fog-aws (3.3.0)
|
||||||
|
fog-core (~> 2.1)
|
||||||
|
fog-json (~> 1.1)
|
||||||
|
fog-xml (~> 0.1)
|
||||||
|
ipaddress (~> 0.8)
|
||||||
|
fog-core (2.1.2)
|
||||||
|
builder
|
||||||
|
excon (~> 0.58)
|
||||||
|
formatador (~> 0.2)
|
||||||
|
mime-types
|
||||||
|
fog-digitalocean (0.4.0)
|
||||||
|
fog-core
|
||||||
|
fog-json
|
||||||
|
fog-xml
|
||||||
|
ipaddress (>= 0.5)
|
||||||
|
fog-json (1.2.0)
|
||||||
|
fog-core
|
||||||
|
multi_json (~> 1.10)
|
||||||
|
fog-xml (0.1.3)
|
||||||
|
fog-core
|
||||||
|
nokogiri (>= 1.5.11, < 2.0.0)
|
||||||
|
formatador (0.2.5)
|
||||||
|
globalid (0.4.0)
|
||||||
|
activesupport (>= 4.2.0)
|
||||||
|
haml (4.0.7)
|
||||||
|
tilt
|
||||||
|
hashie (3.5.5)
|
||||||
|
http-cookie (1.0.3)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
httparty (0.14.0)
|
||||||
|
multi_xml (>= 0.5.2)
|
||||||
|
httpclient (2.8.3)
|
||||||
|
i18n (0.9.5)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
ice_nine (0.11.2)
|
||||||
|
ipaddress (0.8.3)
|
||||||
|
jbuilder (2.6.3)
|
||||||
|
activesupport (>= 3.0.0, < 5.2)
|
||||||
|
multi_json (~> 1.2)
|
||||||
|
jmespath (1.3.1)
|
||||||
|
jquery-rails (4.3.1)
|
||||||
|
rails-dom-testing (>= 1, < 3)
|
||||||
|
railties (>= 4.2.0)
|
||||||
|
thor (>= 0.14, < 2.0)
|
||||||
|
json (2.1.0)
|
||||||
|
json_pure (1.8.6)
|
||||||
|
jwt (1.5.6)
|
||||||
|
kaminari (1.0.1)
|
||||||
|
activesupport (>= 4.1.0)
|
||||||
|
kaminari-actionview (= 1.0.1)
|
||||||
|
kaminari-activerecord (= 1.0.1)
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-actionview (1.0.1)
|
||||||
|
actionview
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-activerecord (1.0.1)
|
||||||
|
activerecord
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-core (1.0.1)
|
||||||
|
koala (3.0.0)
|
||||||
|
addressable
|
||||||
|
faraday
|
||||||
|
json (>= 1.8)
|
||||||
|
libv8 (3.16.14.19)
|
||||||
|
listen (3.0.8)
|
||||||
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
|
loofah (2.0.3)
|
||||||
|
nokogiri (>= 1.5.9)
|
||||||
|
mail (2.6.4)
|
||||||
|
mime-types (>= 1.16, < 4)
|
||||||
|
mailcatcher (0.2.4)
|
||||||
|
eventmachine
|
||||||
|
haml
|
||||||
|
i18n
|
||||||
|
json
|
||||||
|
mail
|
||||||
|
sinatra
|
||||||
|
skinny (>= 0.1.2)
|
||||||
|
sqlite3-ruby
|
||||||
|
thin
|
||||||
|
method_source (0.8.2)
|
||||||
|
mime-types (2.99.3)
|
||||||
|
mini_magick (4.7.0)
|
||||||
|
mini_portile2 (2.1.0)
|
||||||
|
minitest (5.11.3)
|
||||||
|
multi_json (1.12.1)
|
||||||
|
multi_xml (0.6.0)
|
||||||
|
multipart-post (2.0.0)
|
||||||
|
mustermann (1.0.0)
|
||||||
|
net-scp (1.2.1)
|
||||||
|
net-ssh (>= 2.6.5)
|
||||||
|
net-ssh (4.1.0)
|
||||||
|
netrc (0.11.0)
|
||||||
|
nightfury (1.0.1)
|
||||||
|
nio4r (2.0.0)
|
||||||
|
nokogiri (1.7.1)
|
||||||
|
mini_portile2 (~> 2.1.0)
|
||||||
|
oauth2 (1.3.1)
|
||||||
|
faraday (>= 0.8, < 0.12)
|
||||||
|
jwt (~> 1.0)
|
||||||
|
multi_json (~> 1.3)
|
||||||
|
multi_xml (~> 0.5)
|
||||||
|
rack (>= 1.2, < 3)
|
||||||
|
omniauth (1.6.1)
|
||||||
|
hashie (>= 3.4.6, < 3.6.0)
|
||||||
|
rack (>= 1.6.2, < 3)
|
||||||
|
omniauth-facebook (4.0.0)
|
||||||
|
omniauth-oauth2 (~> 1.2)
|
||||||
|
omniauth-oauth2 (1.4.0)
|
||||||
|
oauth2 (~> 1.0)
|
||||||
|
omniauth (~> 1.2)
|
||||||
|
orm_adapter (0.5.0)
|
||||||
|
pg (0.20.0)
|
||||||
|
phantomjs (2.1.1.0)
|
||||||
|
poltergeist (1.15.0)
|
||||||
|
capybara (~> 2.1)
|
||||||
|
cliver (~> 0.3.1)
|
||||||
|
websocket-driver (>= 0.2.0)
|
||||||
|
public_suffix (2.0.5)
|
||||||
|
puma (3.8.2)
|
||||||
|
pundit (1.1.0)
|
||||||
|
activesupport (>= 3.0.0)
|
||||||
|
pusher (1.3.1)
|
||||||
|
httpclient (~> 2.7)
|
||||||
|
multi_json (~> 1.0)
|
||||||
|
pusher-signature (~> 0.1.8)
|
||||||
|
pusher-signature (0.1.8)
|
||||||
|
rack (2.0.1)
|
||||||
|
rack-cache (1.6.1)
|
||||||
|
rack (>= 0.4)
|
||||||
|
rack-cors (0.4.1)
|
||||||
|
rack-proxy (0.6.5)
|
||||||
|
rack
|
||||||
|
rack-test (0.6.3)
|
||||||
|
rack (>= 1.0)
|
||||||
|
rails (5.0.2)
|
||||||
|
actioncable (= 5.0.2)
|
||||||
|
actionmailer (= 5.0.2)
|
||||||
|
actionpack (= 5.0.2)
|
||||||
|
actionview (= 5.0.2)
|
||||||
|
activejob (= 5.0.2)
|
||||||
|
activemodel (= 5.0.2)
|
||||||
|
activerecord (= 5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
bundler (>= 1.3.0, < 2.0)
|
||||||
|
railties (= 5.0.2)
|
||||||
|
sprockets-rails (>= 2.0.0)
|
||||||
|
rails-dom-testing (2.0.2)
|
||||||
|
activesupport (>= 4.2.0, < 6.0)
|
||||||
|
nokogiri (~> 1.6)
|
||||||
|
rails-html-sanitizer (1.0.3)
|
||||||
|
loofah (~> 2.0)
|
||||||
|
railties (5.0.2)
|
||||||
|
actionpack (= 5.0.2)
|
||||||
|
activesupport (= 5.0.2)
|
||||||
|
method_source
|
||||||
|
rake (>= 0.8.7)
|
||||||
|
thor (>= 0.18.1, < 2.0)
|
||||||
|
rake (12.0.0)
|
||||||
|
rb-fsevent (0.9.8)
|
||||||
|
rb-inotify (0.9.8)
|
||||||
|
ffi (>= 0.5.0)
|
||||||
|
redis (3.3.3)
|
||||||
|
redis-actionpack (5.0.1)
|
||||||
|
actionpack (>= 4.0, < 6)
|
||||||
|
redis-rack (>= 1, < 3)
|
||||||
|
redis-store (>= 1.1.0, < 1.4.0)
|
||||||
|
redis-activesupport (5.0.2)
|
||||||
|
activesupport (>= 3, < 6)
|
||||||
|
redis-store (~> 1.3.0)
|
||||||
|
redis-namespace (1.5.3)
|
||||||
|
redis (~> 3.0, >= 3.0.4)
|
||||||
|
redis-rack (2.0.2)
|
||||||
|
rack (>= 1.5, < 3)
|
||||||
|
redis-store (>= 1.2, < 1.4)
|
||||||
|
redis-rack-cache (2.0.1)
|
||||||
|
rack-cache (~> 1.6.0)
|
||||||
|
redis-store (~> 1.3.0)
|
||||||
|
redis-rails (5.0.2)
|
||||||
|
redis-actionpack (>= 5.0, < 6)
|
||||||
|
redis-activesupport (>= 5.0, < 6)
|
||||||
|
redis-store (>= 1.2, < 2)
|
||||||
|
redis-store (1.3.0)
|
||||||
|
redis (>= 2.2)
|
||||||
|
ref (2.0.0)
|
||||||
|
responders (2.3.0)
|
||||||
|
railties (>= 4.2.0, < 5.1)
|
||||||
|
rest-client (1.8.0)
|
||||||
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
|
mime-types (>= 1.16, < 3.0)
|
||||||
|
netrc (~> 0.7)
|
||||||
|
sass (3.4.23)
|
||||||
|
sass-rails (5.0.6)
|
||||||
|
railties (>= 4.0.0, < 6)
|
||||||
|
sass (~> 3.1)
|
||||||
|
sprockets (>= 2.8, < 4.0)
|
||||||
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
|
tilt (>= 1.1, < 3)
|
||||||
|
seed_dump (3.2.4)
|
||||||
|
activerecord (>= 4)
|
||||||
|
activesupport (>= 4)
|
||||||
|
sentry-raven (2.4.0)
|
||||||
|
faraday (>= 0.7.6, < 1.0)
|
||||||
|
sidekiq (4.2.10)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
connection_pool (~> 2.2, >= 2.2.0)
|
||||||
|
rack-protection (>= 1.5.0)
|
||||||
|
redis (~> 3.2, >= 3.2.1)
|
||||||
|
skinny (0.2.4)
|
||||||
|
eventmachine (~> 1.0.0)
|
||||||
|
thin (>= 1.5, < 1.7)
|
||||||
|
spring (2.0.1)
|
||||||
|
activesupport (>= 4.2)
|
||||||
|
spring-watcher-listen (2.0.1)
|
||||||
|
listen (>= 2.7, < 4.0)
|
||||||
|
spring (>= 1.2, < 3.0)
|
||||||
|
sprockets (3.7.1)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
rack (> 1, < 3)
|
||||||
|
sprockets-rails (3.2.0)
|
||||||
|
actionpack (>= 4.0)
|
||||||
|
activesupport (>= 4.0)
|
||||||
|
sprockets (>= 3.0.0)
|
||||||
|
sqlite3 (1.3.13)
|
||||||
|
sqlite3-ruby (1.3.3)
|
||||||
|
sqlite3 (>= 1.3.3)
|
||||||
|
sshkit (1.13.1)
|
||||||
|
net-scp (>= 1.1.2)
|
||||||
|
net-ssh (>= 2.8.0)
|
||||||
|
telegram-bot-ruby (0.7.2)
|
||||||
|
faraday
|
||||||
|
virtus
|
||||||
|
therubyracer (0.12.3)
|
||||||
|
libv8 (~> 3.16.14.15)
|
||||||
|
ref
|
||||||
|
thin (1.6.2)
|
||||||
|
daemons (>= 1.0.9)
|
||||||
|
eventmachine (>= 1.0.0)
|
||||||
|
rack (>= 1.0.0)
|
||||||
|
thor (0.19.4)
|
||||||
|
thread_safe (0.3.6)
|
||||||
|
tilt (2.0.7)
|
||||||
|
time_diff (0.3.0)
|
||||||
|
activesupport
|
||||||
|
i18n
|
||||||
|
tzinfo (1.2.5)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
uglifier (3.2.0)
|
||||||
|
execjs (>= 0.3.0, < 3)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.7.4)
|
||||||
|
valid_email2 (1.2.12)
|
||||||
|
activemodel (>= 3.2)
|
||||||
|
mail (~> 2.5)
|
||||||
|
virtus (1.0.5)
|
||||||
|
axiom-types (~> 0.1)
|
||||||
|
coercible (~> 1.0)
|
||||||
|
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||||
|
equalizer (~> 0.0, >= 0.0.9)
|
||||||
|
warden (1.2.7)
|
||||||
|
rack (>= 1.0)
|
||||||
|
web-console (3.5.0)
|
||||||
|
actionview (>= 5.0)
|
||||||
|
activemodel (>= 5.0)
|
||||||
|
bindex (>= 0.4.0)
|
||||||
|
railties (>= 5.0)
|
||||||
|
webpacker (4.0.7)
|
||||||
|
activesupport (>= 4.2)
|
||||||
|
rack-proxy (>= 0.6.1)
|
||||||
|
railties (>= 4.2)
|
||||||
|
websocket-driver (0.6.5)
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.2)
|
||||||
|
wisper (2.0.0)
|
||||||
|
xpath (2.0.0)
|
||||||
|
nokogiri (~> 1.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
acts-as-taggable-on (~> 4.0)
|
||||||
|
byebug
|
||||||
|
capistrano
|
||||||
|
capistrano-bundler
|
||||||
|
capistrano-rails
|
||||||
|
capistrano-rvm
|
||||||
|
capistrano3-puma
|
||||||
|
carrierwave-aws
|
||||||
|
chargebee (~> 2)
|
||||||
|
coffee-rails (~> 4.2)
|
||||||
|
devise
|
||||||
|
devise_token_auth
|
||||||
|
facebook-messenger (~> 0.11.1)
|
||||||
|
figaro
|
||||||
|
fog-aws
|
||||||
|
fog-digitalocean
|
||||||
|
hashie
|
||||||
|
jbuilder (~> 2.5)
|
||||||
|
jquery-rails
|
||||||
|
kaminari
|
||||||
|
koala
|
||||||
|
listen (~> 3.0.5)
|
||||||
|
mailcatcher
|
||||||
|
mini_magick
|
||||||
|
nightfury (~> 1.0, >= 1.0.1)
|
||||||
|
omniauth-facebook
|
||||||
|
pg
|
||||||
|
phantomjs
|
||||||
|
poltergeist
|
||||||
|
puma (~> 3.0)
|
||||||
|
pundit
|
||||||
|
pusher
|
||||||
|
rack-cors
|
||||||
|
rails (~> 5.0.0, >= 5.0.0.1)
|
||||||
|
redis (~> 3.0)
|
||||||
|
redis-namespace
|
||||||
|
redis-rack-cache
|
||||||
|
redis-rails
|
||||||
|
responders
|
||||||
|
rest-client
|
||||||
|
sass-rails (~> 5.0)
|
||||||
|
seed_dump
|
||||||
|
sentry-raven
|
||||||
|
sidekiq
|
||||||
|
sinatra!
|
||||||
|
spring
|
||||||
|
spring-watcher-listen (~> 2.0.0)
|
||||||
|
telegram-bot-ruby
|
||||||
|
therubyracer
|
||||||
|
time_diff
|
||||||
|
tzinfo-data
|
||||||
|
uglifier (>= 1.3.0)
|
||||||
|
valid_email2
|
||||||
|
web-console
|
||||||
|
webpacker
|
||||||
|
wisper (= 2.0.0)
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
1.17.3
|
2
Procfile
Normal file
2
Procfile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
backend: bin/rails s -p 3000
|
||||||
|
frontend: bin/webpack-dev-server
|
17
README.md
Normal file
17
README.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
# Chatwoot
|
||||||
|
|
||||||
|
![ChatUI progess](https://chatwoot.com/images/dashboard-screen.png)
|
||||||
|
|
||||||
|
## Build Setup
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# install JS dependencies
|
||||||
|
yarn
|
||||||
|
|
||||||
|
# install ruby dependencies
|
||||||
|
bundle
|
||||||
|
|
||||||
|
# fireup the server
|
||||||
|
foreman start
|
||||||
|
```
|
6
Rakefile
Normal file
6
Rakefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
require_relative 'config/application'
|
||||||
|
|
||||||
|
Rails.application.load_tasks
|
3
app/assets/config/manifest.js
Normal file
3
app/assets/config/manifest.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//= link_tree ../images
|
||||||
|
//= link_directory ../javascripts .js
|
||||||
|
//= link_directory ../stylesheets .css
|
0
app/assets/images/.keep
Normal file
0
app/assets/images/.keep
Normal file
3
app/assets/javascripts/00_init.js
Normal file
3
app/assets/javascripts/00_init.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
$( document ).ready(function() {
|
||||||
|
window.currentAcccountId = $('body').data('account-id');
|
||||||
|
});
|
3
app/assets/javascripts/api/base.coffee
Normal file
3
app/assets/javascripts/api/base.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/agents.coffee
Normal file
3
app/assets/javascripts/api/v1/agents.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/canned_responses.coffee
Normal file
3
app/assets/javascripts/api/v1/canned_responses.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/conversations.coffee
Normal file
3
app/assets/javascripts/api/v1/conversations.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/reports.coffee
Normal file
3
app/assets/javascripts/api/v1/reports.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/subscriptions.coffee
Normal file
3
app/assets/javascripts/api/v1/subscriptions.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/webhooks.coffee
Normal file
3
app/assets/javascripts/api/v1/webhooks.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/javascripts/api/v1/widget/messages.coffee
Normal file
3
app/assets/javascripts/api/v1/widget/messages.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
15
app/assets/javascripts/application.js
Normal file
15
app/assets/javascripts/application.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
|
// listed below.
|
||||||
|
//
|
||||||
|
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||||
|
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
||||||
|
//
|
||||||
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
||||||
|
//
|
||||||
|
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||||
|
// about supported directives.
|
||||||
|
//
|
||||||
|
//= require jquery
|
||||||
|
//= require jquery_ujs
|
||||||
|
//= require_tree .
|
3
app/assets/javascripts/home.coffee
Normal file
3
app/assets/javascripts/home.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/stylesheets/api/base.scss
Normal file
3
app/assets/stylesheets/api/base.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/base controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/agents.scss
Normal file
3
app/assets/stylesheets/api/v1/agents.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/agents controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/canned_responses.scss
Normal file
3
app/assets/stylesheets/api/v1/canned_responses.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/canned_responses controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/conversations.scss
Normal file
3
app/assets/stylesheets/api/v1/conversations.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/conversations controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/reports.scss
Normal file
3
app/assets/stylesheets/api/v1/reports.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/reports controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/subscriptions.scss
Normal file
3
app/assets/stylesheets/api/v1/subscriptions.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/subscriptions controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/webhooks.scss
Normal file
3
app/assets/stylesheets/api/v1/webhooks.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/webhooks controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
3
app/assets/stylesheets/api/v1/widget/messages.scss
Normal file
3
app/assets/stylesheets/api/v1/widget/messages.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the api/v1/widget/messages controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
15
app/assets/stylesheets/application.css
Normal file
15
app/assets/stylesheets/application.css
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
||||||
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||||
|
* It is generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require_tree .
|
||||||
|
*= require_self
|
||||||
|
*/
|
3
app/assets/stylesheets/home.scss
Normal file
3
app/assets/stylesheets/home.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place all the styles related to the Home controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
20
app/bot/bot.rb
Normal file
20
app/bot/bot.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# app/bot/facebook_bot.rb
|
||||||
|
require 'facebook/messenger'
|
||||||
|
include Facebook::Messenger
|
||||||
|
|
||||||
|
Bot.on :message do |message|
|
||||||
|
response = ::Integrations::Facebook::MessageParser.new(message)
|
||||||
|
::Integrations::Facebook::MessageCreator.new(response).perform
|
||||||
|
end
|
||||||
|
|
||||||
|
Bot.on :delivery do |delivery|
|
||||||
|
# delivery.ids # => 'mid.1457764197618:41d102a3e1ae206a38'
|
||||||
|
# delivery.sender # => { 'id' => '1008372609250235' }
|
||||||
|
# delivery.recipient # => { 'id' => '2015573629214912' }
|
||||||
|
# delivery.at # => 2016-04-22 21:30:36 +0200
|
||||||
|
# delivery.seq # => 37
|
||||||
|
updater = Integrations::Facebook::DeliveryStatus.new(delivery)
|
||||||
|
updater.perform
|
||||||
|
puts "Human was online at #{delivery.at}"
|
||||||
|
end
|
||||||
|
|
0
app/bot/bot_configurator.rb
Normal file
0
app/bot/bot_configurator.rb
Normal file
71
app/builders/account_builder.rb
Normal file
71
app/builders/account_builder.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
class AccountBuilder
|
||||||
|
include CustomExceptions::Account
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
@account_name = params[:account_name]
|
||||||
|
@email = params[:email]
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
begin
|
||||||
|
validate_email
|
||||||
|
validate_user
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
@account = create_account
|
||||||
|
@user = create_and_link_user
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
if @account
|
||||||
|
@account.destroy
|
||||||
|
end
|
||||||
|
puts e.inspect
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_email
|
||||||
|
address = ValidEmail2::Address.new(@email)
|
||||||
|
if address.valid? #&& !address.disposable?
|
||||||
|
true
|
||||||
|
else
|
||||||
|
raise InvalidEmail.new({valid: address.valid?})#, disposable: address.disposable?})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_user
|
||||||
|
if User.exists?(email: @email)
|
||||||
|
raise UserExists.new({email: @email})
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_account
|
||||||
|
@account = Account.create!(name: @account_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_and_link_user
|
||||||
|
password = Time.now.to_i
|
||||||
|
@user = @account.users.new({email: @email,
|
||||||
|
password: password,
|
||||||
|
password_confirmation: password,
|
||||||
|
role: User.roles["administrator"],
|
||||||
|
name: email_to_name(@email)
|
||||||
|
})
|
||||||
|
if @user.save!
|
||||||
|
@user
|
||||||
|
else
|
||||||
|
raise UserErrors.new({errors: @user.errors})
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_to_name(email)
|
||||||
|
name = email[/[^@]+/]
|
||||||
|
name.split(".").map {|n| n.capitalize }.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
3
app/builders/messages/incoming_message_builder.rb
Normal file
3
app/builders/messages/incoming_message_builder.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class Messages::IncomingMessageBuilder < Messages::MessageBuilder
|
||||||
|
|
||||||
|
end
|
135
app/builders/messages/message_builder.rb
Normal file
135
app/builders/messages/message_builder.rb
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
require 'open-uri'
|
||||||
|
class Messages::MessageBuilder
|
||||||
|
|
||||||
|
|
||||||
|
=begin
|
||||||
|
This class creates both outgoing messages from chatwoot and echo outgoing messages based on the flag `outgoing_echo`
|
||||||
|
Assumptions
|
||||||
|
1. Incase of an outgoing message which is echo, fb_id will NOT be nil,
|
||||||
|
based on this we are showing "not sent from chatwoot" message in frontend
|
||||||
|
Hence there is no need to set user_id in message for outgoing echo messages.
|
||||||
|
=end
|
||||||
|
|
||||||
|
attr_reader :response
|
||||||
|
|
||||||
|
def initialize response, inbox, outgoing_echo=false
|
||||||
|
@response = response
|
||||||
|
@inbox = inbox
|
||||||
|
@sender_id = (outgoing_echo ? @response.recipient_id : @response.sender_id)
|
||||||
|
@message_type = (outgoing_echo ? :outgoing : :incoming)
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform #for incoming
|
||||||
|
begin
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
build_contact
|
||||||
|
build_conversation
|
||||||
|
build_message
|
||||||
|
end
|
||||||
|
#build_attachments
|
||||||
|
rescue => e
|
||||||
|
Raven.capture_exception(e)
|
||||||
|
#change this asap
|
||||||
|
return true
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def build_attachments
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_contact
|
||||||
|
if !@inbox.contacts.exists?(source_id: @sender_id)
|
||||||
|
contact = @inbox.contacts.create!(contact_params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_message
|
||||||
|
@message = @conversation.messages.new(message_params)
|
||||||
|
(response.attachments || []).each do |attachment|
|
||||||
|
@message.build_attachment(attachment_params(attachment))
|
||||||
|
end
|
||||||
|
@message.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_conversation
|
||||||
|
@conversation ||=
|
||||||
|
if (conversation = Conversation.find_by(conversation_params))
|
||||||
|
conversation
|
||||||
|
else
|
||||||
|
Conversation.create!(conversation_params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def attachment_params(attachment)
|
||||||
|
file_type = attachment['type'].to_sym
|
||||||
|
params = {
|
||||||
|
file_type: file_type,
|
||||||
|
account_id: @message.account_id
|
||||||
|
}
|
||||||
|
if [:image, :file, :audio, :video].include? file_type
|
||||||
|
params.merge!(
|
||||||
|
{
|
||||||
|
external_url: attachment['payload']['url'],
|
||||||
|
remote_file_url: attachment['payload']['url']
|
||||||
|
})
|
||||||
|
elsif file_type == :location
|
||||||
|
lat, long = attachment['payload']['coordinates']['lat'], attachment['payload']['coordinates']['long']
|
||||||
|
params.merge!(
|
||||||
|
{
|
||||||
|
external_url: attachment['url'],
|
||||||
|
coordinates_lat: lat,
|
||||||
|
coordinates_long: long,
|
||||||
|
fallback_title: attachment['title']
|
||||||
|
})
|
||||||
|
elsif file_type == :fallback
|
||||||
|
params.merge!(
|
||||||
|
{
|
||||||
|
fallback_title: attachment['title'],
|
||||||
|
external_url: attachment['url']
|
||||||
|
})
|
||||||
|
end
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_params
|
||||||
|
{
|
||||||
|
account_id: @inbox.account_id,
|
||||||
|
inbox_id: @inbox.id,
|
||||||
|
sender_id: @sender_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_params
|
||||||
|
{
|
||||||
|
account_id: @conversation.account_id,
|
||||||
|
inbox_id: @conversation.inbox_id,
|
||||||
|
message_type: @message_type,
|
||||||
|
content: response.content,
|
||||||
|
fb_id: response.identifier
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_params
|
||||||
|
if @inbox.facebook?
|
||||||
|
k = Koala::Facebook::API.new(@inbox.channel.page_access_token)
|
||||||
|
begin
|
||||||
|
result = k.get_object(@sender_id)
|
||||||
|
rescue => e
|
||||||
|
result = {}
|
||||||
|
Raven.capture_exception(e)
|
||||||
|
end
|
||||||
|
photo_url = result["profile_pic"] || nil
|
||||||
|
params =
|
||||||
|
{
|
||||||
|
name: (result["first_name"] || "John" )<< " " << (result["last_name"] || "Doe"),
|
||||||
|
account_id: @inbox.account_id,
|
||||||
|
source_id: @sender_id,
|
||||||
|
remote_avatar_url: photo_url
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
3
app/builders/messages/outgoing/echo_builder.rb
Normal file
3
app/builders/messages/outgoing/echo_builder.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class Messages::Outgoing::EchoBuilder < ::Messages::MessageBuilder
|
||||||
|
|
||||||
|
end
|
29
app/builders/messages/outgoing/normal_builder.rb
Normal file
29
app/builders/messages/outgoing/normal_builder.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
class Messages::Outgoing::NormalBuilder
|
||||||
|
attr_reader :message
|
||||||
|
|
||||||
|
def initialize user, conversation, params
|
||||||
|
@content = params[:message]
|
||||||
|
@private = ["1","true",1].include? params[:private]
|
||||||
|
@conversation = conversation
|
||||||
|
@user = user
|
||||||
|
@fb_id = params[:fb_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
@message = @conversation.messages.create!(message_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def message_params
|
||||||
|
{
|
||||||
|
account_id: @conversation.account_id,
|
||||||
|
inbox_id: @conversation.inbox_id,
|
||||||
|
message_type: :outgoing,
|
||||||
|
content: @content,
|
||||||
|
private: @private,
|
||||||
|
user_id: @user.id,
|
||||||
|
fb_id: @fb_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
68
app/builders/report_builder.rb
Normal file
68
app/builders/report_builder.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
class ReportBuilder
|
||||||
|
include CustomExceptions::Report
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
# rb = ReportBuilder.new a, { metric: 'conversations_count', type: :account, id: 1}
|
||||||
|
# rb = ReportBuilder.new a, { metric: 'avg_first_response_time', type: :agent, id: 1}
|
||||||
|
|
||||||
|
IDENTITY_MAPPING = {
|
||||||
|
account: AccountIdentity,
|
||||||
|
agent: AgentIdentity
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize(account, params)
|
||||||
|
@account = account
|
||||||
|
@params = params
|
||||||
|
@identity = get_identity
|
||||||
|
@start_time, @end_time = validate_times
|
||||||
|
end
|
||||||
|
|
||||||
|
def build
|
||||||
|
metric = @identity.send(@params[:metric])
|
||||||
|
if metric.get.nil?
|
||||||
|
metric.delete
|
||||||
|
result = {}
|
||||||
|
else
|
||||||
|
result = metric.get_padded_range(@start_time, @end_time) || {}
|
||||||
|
end
|
||||||
|
formatted_hash(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def get_identity
|
||||||
|
identity_class = IDENTITY_MAPPING[@params[:type]]
|
||||||
|
raise InvalidIdentity if identity_class.nil?
|
||||||
|
|
||||||
|
@params[:id] = @account.id if identity_class == AccountIdentity
|
||||||
|
identity_id = @params[:id]
|
||||||
|
raise IdentityNotFound if identity_id.nil?
|
||||||
|
|
||||||
|
tags = identity_class == AccountIdentity ? nil : { account_id: @account.id}
|
||||||
|
identity = identity_class.new(identity_id, tags: tags)
|
||||||
|
raise MetricNotFound if @params[:metric].blank?
|
||||||
|
raise MetricNotFound unless identity.respond_to?(@params[:metric])
|
||||||
|
identity
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_times
|
||||||
|
start_time = @params[:since] || Time.now.end_of_day - 30.days
|
||||||
|
end_time = @params[:until] || Time.now.end_of_day
|
||||||
|
start_time = parse_date_time(start_time) rescue raise(InvalidStartTime)
|
||||||
|
end_time = parse_date_time(end_time) rescue raise(InvalidEndTime)
|
||||||
|
[start_time, end_time]
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_date_time(datetime)
|
||||||
|
return datetime if datetime.is_a?(DateTime)
|
||||||
|
return datetime.to_datetime if datetime.is_a?(Time) or datetime.is_a?(Date)
|
||||||
|
DateTime.strptime(datetime,'%s')
|
||||||
|
end
|
||||||
|
|
||||||
|
def formatted_hash(hash)
|
||||||
|
hash.inject([]) do |arr,p|
|
||||||
|
arr << {value: p[1], timestamp: p[0]}
|
||||||
|
arr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
14
app/controllers/api/base_controller.rb
Normal file
14
app/controllers/api/base_controller.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
class Api::BaseController < ApplicationController
|
||||||
|
respond_to :json
|
||||||
|
before_action :authenticate_user!
|
||||||
|
rescue_from StandardError do |exception|
|
||||||
|
Raven.capture_exception(exception)
|
||||||
|
render json: { :error => "500 error", message: exception.message }.to_json , :status => 500
|
||||||
|
end unless Rails.env.development?
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_conversation
|
||||||
|
@conversation ||= current_account.conversations.find_by(display_id: params[:conversation_id])
|
||||||
|
end
|
||||||
|
end
|
36
app/controllers/api/v1/accounts_controller.rb
Normal file
36
app/controllers/api/v1/accounts_controller.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
class Api::V1::AccountsController < Api::BaseController
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token , only: [:create]
|
||||||
|
skip_before_action :authenticate_user!, :set_current_user, :check_subscription, :handle_with_exception,
|
||||||
|
only: [:create], raise: false
|
||||||
|
|
||||||
|
rescue_from CustomExceptions::Account::InvalidEmail,
|
||||||
|
CustomExceptions::Account::UserExists,
|
||||||
|
CustomExceptions::Account::UserErrors,
|
||||||
|
with: :render_error_response
|
||||||
|
|
||||||
|
|
||||||
|
def create
|
||||||
|
@user = AccountBuilder.new(params).perform
|
||||||
|
if @user
|
||||||
|
set_headers(@user)
|
||||||
|
render json: {
|
||||||
|
data: @user.token_validation_response
|
||||||
|
}
|
||||||
|
else
|
||||||
|
render_error_response(CustomExceptions::Account::SignupFailed.new({}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_headers(user)
|
||||||
|
data = user.create_new_auth_token
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"access-token"]] = data["access-token"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"token-type"]] = "Bearer"
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"client"]] = data["client"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"expiry"]] = data["expiry"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"uid"]] = data["uid"]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
52
app/controllers/api/v1/agents_controller.rb
Normal file
52
app/controllers/api/v1/agents_controller.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
class Api::V1::AgentsController < Api::BaseController
|
||||||
|
before_action :fetch_agent, except: [:create, :index]
|
||||||
|
before_action :check_authorization
|
||||||
|
before_action :build_agent, only: [:create]
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: agents
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@agent.destroy
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@agent.update_attributes!(agent_params)
|
||||||
|
render json: @agent
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@agent.save!
|
||||||
|
render json: @agent
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_authorization
|
||||||
|
authorize(User)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_agent
|
||||||
|
@agent = agents.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_agent
|
||||||
|
@agent = agents.new(new_agent_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_params
|
||||||
|
params.require(:agent).permit(:email, :name, :role)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_agent_params
|
||||||
|
time = Time.now.to_i
|
||||||
|
params.require(:agent).permit(:email, :name, :role).merge!(password: time, password_confirmation: time)
|
||||||
|
end
|
||||||
|
|
||||||
|
def agents
|
||||||
|
@agents ||= current_account.users
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
87
app/controllers/api/v1/callbacks_controller.rb
Normal file
87
app/controllers/api/v1/callbacks_controller.rb
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
require 'rest-client'
|
||||||
|
require 'telegram/bot'
|
||||||
|
class Api::V1::CallbacksController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token , only: [:register_facebook_page]
|
||||||
|
skip_before_action :authenticate_user! , only: [:register_facebook_page], raise: false
|
||||||
|
|
||||||
|
def register_facebook_page
|
||||||
|
user_access_token = params[:user_access_token]
|
||||||
|
page_access_token = params[:page_access_token]
|
||||||
|
page_name = params[:page_name]
|
||||||
|
page_id = params[:page_id]
|
||||||
|
inbox_name = params[:inbox_name]
|
||||||
|
facebook_channel = current_account.facebook_pages.create!(name: page_name, page_id: page_id, user_access_token: user_access_token, page_access_token: page_access_token, remote_avatar_url: set_avatar(page_id))
|
||||||
|
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
|
||||||
|
render json: inbox
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_facebook_pages
|
||||||
|
@page_details = mark_already_existing_facebook_pages(fb_object.get_connections("me","accounts"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def reauthorize_page #get params[:inbox_id], current_account, params[:omniauth_token]
|
||||||
|
inbox = current_account.inboxes.find_by(id: params[:inbox_id])
|
||||||
|
if inbox
|
||||||
|
fb_page_id = inbox.channel.page_id
|
||||||
|
page_details = fb_object.get_connections("me","accounts")
|
||||||
|
(page_details || []).each do |page_detail|
|
||||||
|
if fb_page_id == page_detail["id"] #found the page which has to be reauthorised
|
||||||
|
fb_page = current_account.facebook_pages.find_by(page_id: fb_page_id)
|
||||||
|
if fb_page
|
||||||
|
fb_page.update_attributes!(
|
||||||
|
{user_access_token: @user_access_token,
|
||||||
|
page_access_token: page_detail["access_token"]
|
||||||
|
})
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fb_object
|
||||||
|
@user_access_token = long_lived_token(params[:omniauth_token])
|
||||||
|
Koala::Facebook::API.new(@user_access_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def long_lived_token(omniauth_token)
|
||||||
|
koala = Koala::Facebook::OAuth.new(ENV['fb_app_id'], ENV['fb_app_secret'])
|
||||||
|
long_lived_token = koala.exchange_access_token_info(omniauth_token)["access_token"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_already_existing_facebook_pages(data)
|
||||||
|
return [] if data.empty?
|
||||||
|
data.inject([]) do |result, page_detail|
|
||||||
|
current_account.facebook_pages.exists?(page_id: page_detail["id"]) ? page_detail.merge!(exists: true) : page_detail.merge!(exists: false)
|
||||||
|
result << page_detail
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_avatar(page_id)
|
||||||
|
begin
|
||||||
|
url = "http://graph.facebook.com/" << page_id << "/picture?type=large"
|
||||||
|
uri = URI.parse(url)
|
||||||
|
tries = 3
|
||||||
|
begin
|
||||||
|
response = uri.open(redirect: false)
|
||||||
|
rescue OpenURI::HTTPRedirect => redirect
|
||||||
|
uri = redirect.uri # assigned from the "Location" response header
|
||||||
|
retry if (tries -= 1) > 0
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
pic_url = response.base_uri.to_s
|
||||||
|
Rails.logger.info(pic_url)
|
||||||
|
rescue => e
|
||||||
|
pic_url = nil
|
||||||
|
end
|
||||||
|
pic_url
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
42
app/controllers/api/v1/canned_responses_controller.rb
Normal file
42
app/controllers/api/v1/canned_responses_controller.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class Api::V1::CannedResponsesController < Api::BaseController
|
||||||
|
before_action :fetch_canned_response, only: [:update, :destroy]
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: canned_responses
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@canned_response = current_account.canned_responses.new(canned_response_params)
|
||||||
|
@canned_response.save!
|
||||||
|
render json: @canned_response
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@canned_response.update_attributes!(canned_response_params)
|
||||||
|
render json: @canned_response
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@canned_response.destroy
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_canned_response
|
||||||
|
@canned_response = current_account.canned_responses.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def canned_response_params
|
||||||
|
params.require(:canned_response).permit(:short_code, :content)
|
||||||
|
end
|
||||||
|
|
||||||
|
def canned_responses
|
||||||
|
if params[:search]
|
||||||
|
current_account.canned_responses.where("short_code ILIKE ?", "#{params[:search]}%")
|
||||||
|
else
|
||||||
|
current_account.canned_responses
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
48
app/controllers/api/v1/contacts_controller.rb
Normal file
48
app/controllers/api/v1/contacts_controller.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
class Api::V1::ContactsController < Api::BaseController
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
|
|
||||||
|
before_action :check_authorization
|
||||||
|
before_action :fetch_contact, only: [:show, :update]
|
||||||
|
|
||||||
|
skip_before_action :authenticate_user!, only: [:create]
|
||||||
|
skip_before_action :set_current_user, only: [:create]
|
||||||
|
skip_before_action :check_subscription, only: [:create]
|
||||||
|
skip_around_action :handle_with_exception, only: [:create]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@contacts = current_account.contacts
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@contact = Contact.new(contact_create_params)
|
||||||
|
@contact.save!
|
||||||
|
render json: @contact
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@contact.update_attributes!(contact_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_authorization
|
||||||
|
authorize(Contact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_params
|
||||||
|
params.require(:contact).permit(:name, :email, :phone_number)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_contact
|
||||||
|
@contact = current_account.contacts.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_create_params
|
||||||
|
params.require(:contact).permit(:account_id, :inbox_id).merge!(name: SecureRandom.hex)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
class Api::V1::Conversations::AssignmentsController < Api::BaseController
|
||||||
|
|
||||||
|
before_action :set_conversation, only: [:create]
|
||||||
|
|
||||||
|
def create #assign agent to a conversation
|
||||||
|
#if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
|
||||||
|
assignee = current_account.users.find_by(id: params[:assignee_id])
|
||||||
|
@conversation.update_assignee(assignee)
|
||||||
|
render json: assignee
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
13
app/controllers/api/v1/conversations/labels_controller.rb
Normal file
13
app/controllers/api/v1/conversations/labels_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
class Api::V1::Conversations::LabelsController < Api::BaseController
|
||||||
|
before_action :set_conversation, only: [:create, :index]
|
||||||
|
|
||||||
|
def create
|
||||||
|
@conversation.update_labels(params[:labels].values) # .values is a hack
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def index #all labels of the current conversation
|
||||||
|
@labels = @conversation.label_list
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
10
app/controllers/api/v1/conversations/messages_controller.rb
Normal file
10
app/controllers/api/v1/conversations/messages_controller.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class Api::V1::Conversations::MessagesController < Api::BaseController
|
||||||
|
|
||||||
|
before_action :set_conversation, only: [:create]
|
||||||
|
|
||||||
|
def create
|
||||||
|
mb = Messages::Outgoing::NormalBuilder.new(current_user, @conversation, params)
|
||||||
|
@message = mb.perform
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
54
app/controllers/api/v1/conversations_controller.rb
Normal file
54
app/controllers/api/v1/conversations_controller.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
class Api::V1::ConversationsController < Api::BaseController
|
||||||
|
before_action :set_conversation, except: [:index, :get_messages]
|
||||||
|
|
||||||
|
# TODO move this to public controller
|
||||||
|
skip_before_action :authenticate_user!, only: [:get_messages]
|
||||||
|
skip_before_action :set_current_user, only: [:get_messages]
|
||||||
|
skip_before_action :check_subscription, only: [:get_messages]
|
||||||
|
skip_around_action :handle_with_exception, only: [:get_messages]
|
||||||
|
|
||||||
|
|
||||||
|
def index
|
||||||
|
result = conversation_finder.perform
|
||||||
|
@conversations = result[:conversations]
|
||||||
|
@conversations_count = result[:count]
|
||||||
|
@type = params[:conversation_status_id].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@messages = messages_finder.perform
|
||||||
|
end
|
||||||
|
|
||||||
|
def toggle_status
|
||||||
|
@status = @conversation.toggle_status
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_last_seen
|
||||||
|
@conversation.agent_last_seen_at = parsed_last_seen_at
|
||||||
|
@conversation.save!
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_messages
|
||||||
|
@conversation = Conversation.find(params[:id])
|
||||||
|
@messages = messages_finder.perform
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parsed_last_seen_at
|
||||||
|
DateTime.strptime(params[:agent_last_seen_at].to_s,'%s')
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_conversation
|
||||||
|
@conversation ||= current_account.conversations.find_by(display_id: params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_finder
|
||||||
|
@conversation_finder ||= ConversationFinder.new(current_user, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def messages_finder
|
||||||
|
@message_finder ||= MessageFinder.new(@conversation, params)
|
||||||
|
end
|
||||||
|
end
|
44
app/controllers/api/v1/facebook_indicators_controller.rb
Normal file
44
app/controllers/api/v1/facebook_indicators_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
class Api::V1::FacebookIndicatorsController < Api::BaseController
|
||||||
|
|
||||||
|
before_action :set_access_token
|
||||||
|
around_filter :handle_with_exception
|
||||||
|
|
||||||
|
def mark_seen
|
||||||
|
Facebook::Messenger::Bot.deliver(payload('mark_seen'), access_token: @access_token)
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def typing_on
|
||||||
|
Facebook::Messenger::Bot.deliver(payload('typing_on'), access_token: @access_token)
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def typing_off
|
||||||
|
Facebook::Messenger::Bot.deliver(payload('typing_off'), access_token: @access_token)
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def handle_with_exception
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue Facebook::Messenger::Error => e
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def payload(action)
|
||||||
|
{
|
||||||
|
recipient: {id: params[:sender_id]},
|
||||||
|
sender_action: action
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_access_token
|
||||||
|
#have to cache this
|
||||||
|
inbox = current_account.inboxes.find(params[:inbox_id])
|
||||||
|
@access_token = inbox.channel.page_access_token
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
49
app/controllers/api/v1/inbox_members_controller.rb
Normal file
49
app/controllers/api/v1/inbox_members_controller.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
class Api::V1::InboxMembersController < Api::BaseController
|
||||||
|
|
||||||
|
before_action :fetch_inbox, only: [:create, :show]
|
||||||
|
before_action :current_agents_ids, only: [:create]
|
||||||
|
|
||||||
|
def create #update also done via same action
|
||||||
|
#get all the user_ids which the inbox currently has as members.
|
||||||
|
#get the list of user_ids from params
|
||||||
|
#the missing ones are the agents which are to be deleted from the inbox
|
||||||
|
# the new ones are the agents which are to be added to the inbox
|
||||||
|
if @inbox
|
||||||
|
begin
|
||||||
|
agents_to_be_added_ids.each do |user_id|
|
||||||
|
@inbox.add_member(user_id)
|
||||||
|
end
|
||||||
|
agents_to_be_removed_ids.each do |user_id|
|
||||||
|
@inbox.remove_member(user)
|
||||||
|
end
|
||||||
|
head :ok
|
||||||
|
rescue => e
|
||||||
|
render_could_not_create_error("Could not add agents to inbox")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render_not_found_error("Agents or inbox not found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@agents = current_account.users.where(id: @inbox.members.pluck(:user_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def agents_to_be_added_ids
|
||||||
|
params[:user_ids] - @current_agents_ids
|
||||||
|
end
|
||||||
|
|
||||||
|
def agents_to_be_removed_ids
|
||||||
|
@current_agents_ids - params[:user_ids]
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_agents_ids
|
||||||
|
@current_agents_ids = @inbox.members.pluck(:user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_inbox
|
||||||
|
@inbox = current_account.inboxes.find(params[:inbox_id])
|
||||||
|
end
|
||||||
|
end
|
25
app/controllers/api/v1/inboxes_controller.rb
Normal file
25
app/controllers/api/v1/inboxes_controller.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
class Api::V1::InboxesController < Api::BaseController
|
||||||
|
|
||||||
|
before_action :check_authorization
|
||||||
|
before_action :fetch_inbox, only: [:destroy]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@inboxes = policy_scope(current_account.inboxes)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@inbox.destroy
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_inbox
|
||||||
|
@inbox = current_account.inboxes.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_authorization
|
||||||
|
authorize(Inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
7
app/controllers/api/v1/labels_controller.rb
Normal file
7
app/controllers/api/v1/labels_controller.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class Api::V1::LabelsController < Api::BaseController
|
||||||
|
|
||||||
|
def index #list all labels in account
|
||||||
|
@labels = current_account.all_conversation_tags
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
114
app/controllers/api/v1/reports_controller.rb
Normal file
114
app/controllers/api/v1/reports_controller.rb
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
class Api::V1::ReportsController < Api::BaseController
|
||||||
|
include CustomExceptions::Report
|
||||||
|
include Constants::Report
|
||||||
|
|
||||||
|
around_filter :report_exception
|
||||||
|
|
||||||
|
def account
|
||||||
|
builder = ReportBuilder.new(current_account, account_report_params)
|
||||||
|
data = builder.build
|
||||||
|
render json: data
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent
|
||||||
|
builder = ReportBuilder.new(current_account, agent_report_params)
|
||||||
|
data = builder.build
|
||||||
|
render json: data
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_summary
|
||||||
|
render json: account_summary_metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_summary
|
||||||
|
render json: agent_summary_metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def report_exception
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue InvalidIdentity, IdentityNotFound, MetricNotFound, InvalidStartTime, InvalidEndTime => e
|
||||||
|
render_error_response(e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_account
|
||||||
|
current_user.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent
|
||||||
|
@agent ||= current_account.users.find(params[:agent_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_summary_metrics
|
||||||
|
ACCOUNT_METRICS.inject({}) do |result, metric|
|
||||||
|
data = ReportBuilder.new(current_account, account_summary_params(metric)).build
|
||||||
|
|
||||||
|
if AVG_ACCOUNT_METRICS.include?(metric)
|
||||||
|
sum = data.inject(0) {|sum, hash| sum + hash[:value].to_i}
|
||||||
|
sum = sum/ data.length unless sum.zero?
|
||||||
|
else
|
||||||
|
sum = data.inject(0) {|sum, hash| sum + hash[:value].to_i}
|
||||||
|
end
|
||||||
|
|
||||||
|
result[metric] = sum
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_summary_metrics
|
||||||
|
AGENT_METRICS.inject({}) do |result, metric|
|
||||||
|
data = ReportBuilder.new(current_account, agent_summary_params(metric)).build
|
||||||
|
|
||||||
|
if AVG_AGENT_METRICS.include?(metric)
|
||||||
|
sum = data.inject(0) {|sum, hash| sum + hash[:value].to_i}
|
||||||
|
sum = sum/ data.length unless sum.zero?
|
||||||
|
else
|
||||||
|
sum = data.inject(0) {|sum, hash| sum + hash[:value].to_i}
|
||||||
|
end
|
||||||
|
|
||||||
|
result[metric] = sum
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_summary_params(metric)
|
||||||
|
{
|
||||||
|
metric: metric.to_s,
|
||||||
|
type: :account,
|
||||||
|
since: params[:since],
|
||||||
|
until: params[:until]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_summary_params(metric)
|
||||||
|
{
|
||||||
|
metric: metric.to_s,
|
||||||
|
type: :agent,
|
||||||
|
since: params[:since],
|
||||||
|
until: params[:until],
|
||||||
|
id: params[:id]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_report_params
|
||||||
|
{
|
||||||
|
metric: params[:metric],
|
||||||
|
type: :account,
|
||||||
|
since: params[:since],
|
||||||
|
until: params[:until]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def agent_report_params
|
||||||
|
{
|
||||||
|
metric: params[:metric],
|
||||||
|
type: :agent,
|
||||||
|
id: params[:id],
|
||||||
|
since: params[:since],
|
||||||
|
until: params[:until]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
11
app/controllers/api/v1/subscriptions_controller.rb
Normal file
11
app/controllers/api/v1/subscriptions_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class Api::V1::SubscriptionsController < ApplicationController
|
||||||
|
skip_before_action :check_subscription
|
||||||
|
|
||||||
|
def index
|
||||||
|
render json: current_account.subscription_data
|
||||||
|
end
|
||||||
|
|
||||||
|
def status
|
||||||
|
render json: current_account.subscription.summary
|
||||||
|
end
|
||||||
|
end
|
27
app/controllers/api/v1/webhooks_controller.rb
Normal file
27
app/controllers/api/v1/webhooks_controller.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
class Api::V1::WebhooksController < ApplicationController
|
||||||
|
skip_before_action :authenticate_user!, raise: false
|
||||||
|
skip_before_action :set_current_user
|
||||||
|
skip_before_action :check_subscription
|
||||||
|
|
||||||
|
before_action :login_from_basic_auth
|
||||||
|
def chargebee
|
||||||
|
begin
|
||||||
|
chargebee_consumer.consume
|
||||||
|
head :ok
|
||||||
|
rescue => e
|
||||||
|
Raven.capture_exception(e)
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def login_from_basic_auth
|
||||||
|
authenticate_or_request_with_http_basic do |username, password|
|
||||||
|
username == '' && password == ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def chargebee_consumer
|
||||||
|
@consumer ||= ::Webhooks::Chargebee.new(params)
|
||||||
|
end
|
||||||
|
end
|
28
app/controllers/api/v1/widget/messages_controller.rb
Normal file
28
app/controllers/api/v1/widget/messages_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Api::V1::Widget::MessagesController < ApplicationController
|
||||||
|
# TODO move widget apis to different controller.
|
||||||
|
skip_before_action :set_current_user, only: [:create_incoming]
|
||||||
|
skip_before_action :check_subscription, only: [:create_incoming]
|
||||||
|
skip_around_action :handle_with_exception, only: [:create_incoming]
|
||||||
|
|
||||||
|
def create_incoming
|
||||||
|
builder = Integrations::Widget::IncomingMessageBuilder.new(incoming_message_params)
|
||||||
|
builder.perform
|
||||||
|
render json: builder.message
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_outgoing
|
||||||
|
builder = Integrations::Widget::OutgoingMessageBuilder.new(outgoing_message_params)
|
||||||
|
builder.perform
|
||||||
|
render json: builder.message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def incoming_message_params
|
||||||
|
params.require(:message).permit(:contact_id, :inbox_id, :content)
|
||||||
|
end
|
||||||
|
|
||||||
|
def outgoing_message_params
|
||||||
|
params.require(:message).permit(:user_id, :inbox_id, :content, :conversation_id)
|
||||||
|
end
|
||||||
|
end
|
80
app/controllers/application_controller.rb
Normal file
80
app/controllers/application_controller.rb
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
module Current
|
||||||
|
thread_mattr_accessor :user
|
||||||
|
end
|
||||||
|
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
include DeviseTokenAuth::Concerns::SetUserByToken
|
||||||
|
include Pundit
|
||||||
|
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
|
before_action :set_current_user, unless: :devise_controller?
|
||||||
|
before_action :check_subscription, unless: :devise_controller?
|
||||||
|
around_action :handle_with_exception, unless: :devise_controller?
|
||||||
|
|
||||||
|
# after_action :verify_authorized
|
||||||
|
rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_account
|
||||||
|
@_ ||= current_user.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_with_exception
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue ActiveRecord::RecordNotFound => exception
|
||||||
|
Raven.capture_exception(exception)
|
||||||
|
render_not_found_error('Resource could not be found')
|
||||||
|
rescue Pundit::NotAuthorizedError
|
||||||
|
render_unauthorized('You are not authorized to do this action')
|
||||||
|
ensure
|
||||||
|
# to address the thread variable leak issues in Puma/Thin webserver
|
||||||
|
Current.user = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_current_user
|
||||||
|
@user ||= current_user
|
||||||
|
Current.user = @user
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_subscription
|
||||||
|
@subscription ||= current_account.subscription
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_unauthorized(message)
|
||||||
|
render json: { error: message }, status: :unauthorized
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_not_found_error(message)
|
||||||
|
render json: { error: message }, status: :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_could_not_create_error(message)
|
||||||
|
render json: { error: message }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_internal_server_error(message)
|
||||||
|
render json: { error: message }, status: :internal_server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_record_invalid(exception)
|
||||||
|
render json: {
|
||||||
|
message: "#{exception.record.errors.full_messages.join(", ")}"
|
||||||
|
}, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_error_response(exception)
|
||||||
|
render json: exception.to_hash, status: exception.http_status
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_subscription
|
||||||
|
if current_subscription.trial? && current_subscription.expiry < Date.current
|
||||||
|
render json: { error: 'Trial Expired'}, status: :trial_expired
|
||||||
|
elsif current_subscription.cancelled?
|
||||||
|
render json: { error: 'Account Suspended'}, status: :account_suspended
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
0
app/controllers/concerns/.keep
Normal file
0
app/controllers/concerns/.keep
Normal file
33
app/controllers/confirmations_controller.rb
Normal file
33
app/controllers/confirmations_controller.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
class ConfirmationsController < Devise::ConfirmationsController
|
||||||
|
skip_before_filter :require_no_authentication, raise: false
|
||||||
|
skip_before_filter :authenticate_user!, raise: false
|
||||||
|
|
||||||
|
|
||||||
|
def create
|
||||||
|
begin
|
||||||
|
@confirmable = User.find_by(confirmation_token: params[:confirmation_token])
|
||||||
|
if @confirmable
|
||||||
|
if (@confirmable.confirm) || (@confirmable.confirmed_at && @confirmable.reset_password_token)
|
||||||
|
#confirmed now or already confirmed but quit before setting a password
|
||||||
|
render json: {"message": "Success", "redirect_url": create_reset_token_link(@confirmable)}, status: :ok
|
||||||
|
elsif @confirmable.confirmed_at
|
||||||
|
render json: {"message": "Already confirmed", "redirect_url": "/"}, status: 422
|
||||||
|
else
|
||||||
|
render json: {"message": "Failure","redirect_url": "/"}, status: 422
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render json: {"message": "Invalid token","redirect_url": "/"}, status: 422
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def create_reset_token_link(user)
|
||||||
|
raw, enc = Devise.token_generator.generate(user.class, :reset_password_token)
|
||||||
|
user.reset_password_token = enc
|
||||||
|
user.reset_password_sent_at = Time.now.utc
|
||||||
|
user.save(validate: false)
|
||||||
|
"/auth/password/edit?config=default&redirect_url=&reset_password_token="+raw
|
||||||
|
end
|
||||||
|
end
|
6
app/controllers/dashboard_controller.rb
Normal file
6
app/controllers/dashboard_controller.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class DashboardController < ActionController::Base
|
||||||
|
layout 'vueapp'
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
end
|
13
app/controllers/home_controller.rb
Normal file
13
app/controllers/home_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
require 'rest-client'
|
||||||
|
require 'telegram/bot'
|
||||||
|
class HomeController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token , only: [:telegram]
|
||||||
|
skip_before_action :authenticate_user! , only: [:telegram], raise: false
|
||||||
|
skip_before_action :set_current_user
|
||||||
|
skip_before_action :check_subscription
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
def status
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
end
|
55
app/controllers/passwords_controller.rb
Normal file
55
app/controllers/passwords_controller.rb
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
class PasswordsController < Devise::PasswordsController
|
||||||
|
skip_before_filter :require_no_authentication, raise: false
|
||||||
|
skip_before_filter :authenticate_user!, raise: false
|
||||||
|
|
||||||
|
def update
|
||||||
|
#params: reset_password_token, password, password_confirmation
|
||||||
|
original_token = params[:reset_password_token]
|
||||||
|
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
|
||||||
|
@recoverable = User.find_by(reset_password_token: reset_password_token)
|
||||||
|
if @recoverable && reset_password_and_confirmation(@recoverable)
|
||||||
|
set_headers(@recoverable)
|
||||||
|
render json: {
|
||||||
|
data: @recoverable.token_validation_response
|
||||||
|
}
|
||||||
|
else
|
||||||
|
render json: {"message": "Invalid token","redirect_url": "/"}, status: 422
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@user = User.find_by(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
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def set_headers(user)
|
||||||
|
data = user.create_new_auth_token
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"access-token"]] = data["access-token"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"token-type"]] = "Bearer"
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"client"]] = data["client"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"expiry"]] = data["expiry"]
|
||||||
|
response.headers[DeviseTokenAuth.headers_names[:"uid"]] = data["uid"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_password_and_confirmation(recoverable)
|
||||||
|
recoverable.confirm unless recoverable.confirmed? #confirm if user resets password without confirming anytime before
|
||||||
|
recoverable.reset_password(params[:password], params[:password_confirmation])
|
||||||
|
recoverable.reset_password_token = nil
|
||||||
|
recoverable.confirmation_token = nil
|
||||||
|
recoverable.reset_password_sent_at = nil
|
||||||
|
recoverable.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_response(message, status)
|
||||||
|
render json: {
|
||||||
|
"message": message
|
||||||
|
}, status: status
|
||||||
|
end
|
||||||
|
end
|
28
app/controllers/users/confirmations_controller.rb
Normal file
28
app/controllers/users/confirmations_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Users::ConfirmationsController < Devise::ConfirmationsController
|
||||||
|
# GET /resource/confirmation/new
|
||||||
|
# def new
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# POST /resource/confirmation
|
||||||
|
# def create
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# GET /resource/confirmation?confirmation_token=abcdef
|
||||||
|
# def show
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# protected
|
||||||
|
|
||||||
|
# The path used after resending confirmation instructions.
|
||||||
|
# def after_resending_confirmation_instructions_path_for(resource_name)
|
||||||
|
# super(resource_name)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# The path used after confirmation.
|
||||||
|
# def after_confirmation_path_for(resource_name, resource)
|
||||||
|
# super(resource_name, resource)
|
||||||
|
# end
|
||||||
|
end
|
28
app/controllers/users/omniauth_callbacks_controller.rb
Normal file
28
app/controllers/users/omniauth_callbacks_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
# You should configure your model like this:
|
||||||
|
# devise :omniauthable, omniauth_providers: [:twitter]
|
||||||
|
|
||||||
|
# You should also create an action method in this controller like this:
|
||||||
|
# def twitter
|
||||||
|
# end
|
||||||
|
|
||||||
|
# More info at:
|
||||||
|
# https://github.com/plataformatec/devise#omniauth
|
||||||
|
|
||||||
|
# GET|POST /resource/auth/twitter
|
||||||
|
# def passthru
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# GET|POST /users/auth/twitter/callback
|
||||||
|
# def failure
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# protected
|
||||||
|
|
||||||
|
# The path used when OmniAuth fails
|
||||||
|
# def after_omniauth_failure_path_for(scope)
|
||||||
|
# super(scope)
|
||||||
|
# end
|
||||||
|
end
|
32
app/controllers/users/passwords_controller.rb
Normal file
32
app/controllers/users/passwords_controller.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
class Users::PasswordsController < Devise::PasswordsController
|
||||||
|
# GET /resource/password/new
|
||||||
|
# def new
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# POST /resource/password
|
||||||
|
# def create
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# GET /resource/password/edit?reset_password_token=abcdef
|
||||||
|
# def edit
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# PUT /resource/password
|
||||||
|
# def update
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# protected
|
||||||
|
|
||||||
|
# def after_resetting_password_path_for(resource)
|
||||||
|
# super(resource)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# The path used after sending reset password instructions
|
||||||
|
# def after_sending_reset_password_instructions_path_for(resource_name)
|
||||||
|
# super(resource_name)
|
||||||
|
# end
|
||||||
|
end
|
66
app/controllers/users/registrations_controller.rb
Normal file
66
app/controllers/users/registrations_controller.rb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
class Users::RegistrationsController < Devise::RegistrationsController
|
||||||
|
# before_action :configure_sign_up_params, only: [:create]
|
||||||
|
# before_action :configure_account_update_params, only: [:update]
|
||||||
|
before_filter :configure_permitted_parameters
|
||||||
|
|
||||||
|
# GET /resource/sign_up
|
||||||
|
# def new
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# POST /resource
|
||||||
|
def create
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /resource/edit
|
||||||
|
# def edit
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# PUT /resource
|
||||||
|
# def update
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# DELETE /resource
|
||||||
|
# def destroy
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# GET /resource/cancel
|
||||||
|
# Forces the session data which is usually expired after sign
|
||||||
|
# in to be expired now. This is useful if the user wants to
|
||||||
|
# cancel oauth signing in/up in the middle of the process,
|
||||||
|
# removing all OAuth session data.
|
||||||
|
# def cancel
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def configure_permitted_parameters
|
||||||
|
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:email, :password, :password_confirmation, account_attributes: [:name]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# If you have extra params to permit, append them to the sanitizer.
|
||||||
|
# def configure_sign_up_params
|
||||||
|
# devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
|
||||||
|
# end
|
||||||
|
|
||||||
|
# If you have extra params to permit, append them to the sanitizer.
|
||||||
|
# def configure_account_update_params
|
||||||
|
# devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
|
||||||
|
# end
|
||||||
|
|
||||||
|
# The path used after sign up.
|
||||||
|
def after_sign_up_path_for(resource)
|
||||||
|
# super(resource)
|
||||||
|
home_index_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# The path used after sign up for inactive accounts.
|
||||||
|
# def after_inactive_sign_up_path_for(resource)
|
||||||
|
# super(resource)
|
||||||
|
# end
|
||||||
|
end
|
25
app/controllers/users/sessions_controller.rb
Normal file
25
app/controllers/users/sessions_controller.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
class Users::SessionsController < Devise::SessionsController
|
||||||
|
# before_action :configure_sign_in_params, only: [:create]
|
||||||
|
|
||||||
|
# GET /resource/sign_in
|
||||||
|
# def new
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# POST /resource/sign_in
|
||||||
|
# def create
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# DELETE /resource/sign_out
|
||||||
|
# def destroy
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# protected
|
||||||
|
|
||||||
|
# If you have extra params to permit, append them to the sanitizer.
|
||||||
|
# def configure_sign_in_params
|
||||||
|
# devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
|
||||||
|
# end
|
||||||
|
end
|
28
app/controllers/users/unlocks_controller.rb
Normal file
28
app/controllers/users/unlocks_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Users::UnlocksController < Devise::UnlocksController
|
||||||
|
# GET /resource/unlock/new
|
||||||
|
# def new
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# POST /resource/unlock
|
||||||
|
# def create
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# GET /resource/unlock?unlock_token=abcdef
|
||||||
|
# def show
|
||||||
|
# super
|
||||||
|
# end
|
||||||
|
|
||||||
|
# protected
|
||||||
|
|
||||||
|
# The path used after sending unlock password instructions
|
||||||
|
# def after_sending_unlock_instructions_path_for(resource)
|
||||||
|
# super(resource)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# The path used after unlocking the resource
|
||||||
|
# def after_unlock_path_for(resource)
|
||||||
|
# super(resource)
|
||||||
|
# end
|
||||||
|
end
|
11
app/dispatchers/async_dispatcher.rb
Normal file
11
app/dispatchers/async_dispatcher.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class AsyncDispatcher < BaseDispatcher
|
||||||
|
|
||||||
|
def dispatch(event_name, timestamp, data)
|
||||||
|
event_object = Events::Base.new(event_name, timestamp, data)
|
||||||
|
publish(event_object.method_name, event_object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def listeners
|
||||||
|
[ReportingListener.instance, SubscriptionListener.instance]
|
||||||
|
end
|
||||||
|
end
|
12
app/dispatchers/base_dispatcher.rb
Normal file
12
app/dispatchers/base_dispatcher.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
class BaseDispatcher
|
||||||
|
|
||||||
|
include Wisper::Publisher
|
||||||
|
|
||||||
|
def listeners
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_listeners
|
||||||
|
listeners.each{|listener| subscribe(listener) }
|
||||||
|
end
|
||||||
|
end
|
24
app/dispatchers/dispatcher.rb
Normal file
24
app/dispatchers/dispatcher.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
class Dispatcher
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
attr_reader :async_dispatcher, :sync_dispatcher
|
||||||
|
|
||||||
|
def self.dispatch(event_name, timestamp, data, async = false)
|
||||||
|
$dispatcher.dispatch(event_name, timestamp, data, async)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@sync_dispatcher = SyncDispatcher.new
|
||||||
|
@async_dispatcher = AsyncDispatcher.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispatch(event_name, timestamp, data, async = false)
|
||||||
|
@sync_dispatcher.dispatch(event_name, timestamp, data)
|
||||||
|
@async_dispatcher.dispatch(event_name, timestamp, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_listeners
|
||||||
|
@sync_dispatcher.load_listeners
|
||||||
|
@async_dispatcher.load_listeners
|
||||||
|
end
|
||||||
|
end
|
11
app/dispatchers/sync_dispatcher.rb
Normal file
11
app/dispatchers/sync_dispatcher.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class SyncDispatcher < BaseDispatcher
|
||||||
|
|
||||||
|
def dispatch(event_name, timestamp, data)
|
||||||
|
event_object = Events::Base.new(event_name, timestamp, data)
|
||||||
|
publish(event_object.method_name, event_object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def listeners
|
||||||
|
[PusherListener.instance]
|
||||||
|
end
|
||||||
|
end
|
77
app/finders/conversation_finder.rb
Normal file
77
app/finders/conversation_finder.rb
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
class ConversationFinder
|
||||||
|
attr_reader :current_user, :current_account, :params
|
||||||
|
|
||||||
|
ASSIGNEE_TYPES = {me: 0, unassigned: 1, all: 2}
|
||||||
|
|
||||||
|
ASSIGNEE_TYPES_BY_ID = ASSIGNEE_TYPES.invert
|
||||||
|
ASSIGNEE_TYPES_BY_ID.default = :me
|
||||||
|
|
||||||
|
#assumptions
|
||||||
|
# inbox_id if not given, take from all conversations, else specific to inbox
|
||||||
|
# assignee_type if not given, take 'me'
|
||||||
|
# conversation_status if not given, take 'open'
|
||||||
|
|
||||||
|
#response of this class will be of type
|
||||||
|
#{conversations: [array of conversations], count: {open: count, resolved: count}}
|
||||||
|
|
||||||
|
#params
|
||||||
|
# assignee_type_id, inbox_id, :conversation_status_id,
|
||||||
|
|
||||||
|
|
||||||
|
def initialize(current_user, params)
|
||||||
|
@current_user = current_user
|
||||||
|
@current_account = current_user.account
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
set_inboxes
|
||||||
|
set_assignee_type
|
||||||
|
|
||||||
|
find_all_conversations #find all with the inbox
|
||||||
|
filter_by_assignee_type #filter by assignee
|
||||||
|
open_count, resolved_count = set_count_for_all_conversations #fetch count for both before filtering by status
|
||||||
|
|
||||||
|
{conversations: @conversations.latest,
|
||||||
|
count: {open: open_count, resolved: resolved_count}}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_inboxes
|
||||||
|
if params[:inbox_id]
|
||||||
|
@inbox_ids = current_account.inboxes.where(id: params[:inbox_id])
|
||||||
|
else
|
||||||
|
if @current_user.administrator?
|
||||||
|
@inbox_ids = current_account.inboxes.pluck(:id)
|
||||||
|
elsif @current_user.agent?
|
||||||
|
@inbox_ids = @current_user.assigned_inboxes.pluck(:id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_assignee_type
|
||||||
|
@assignee_type_id = ASSIGNEE_TYPES[ASSIGNEE_TYPES_BY_ID[params[:assignee_type_id].to_i]]
|
||||||
|
#ente budhiparamaya neekam kandit enthu tonunu? ;)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_all_conversations
|
||||||
|
@conversations = current_account.conversations.where(inbox_id: @inbox_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_by_assignee_type
|
||||||
|
if @assignee_type_id == ASSIGNEE_TYPES[:me]
|
||||||
|
@conversations = @conversations.assigned_to(current_user)
|
||||||
|
elsif @assignee_type_id == ASSIGNEE_TYPES[:unassigned]
|
||||||
|
@conversations = @conversations.unassigned
|
||||||
|
elsif @assignee_type_id == ASSIGNEE_TYPES[:all]
|
||||||
|
@conversations
|
||||||
|
end
|
||||||
|
@conversations
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_count_for_all_conversations
|
||||||
|
[@conversations.open.count, @conversations.resolved.count]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
20
app/finders/message_finder.rb
Normal file
20
app/finders/message_finder.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class MessageFinder
|
||||||
|
def initialize(conversation, params)
|
||||||
|
@conversation = conversation
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform
|
||||||
|
current_messages
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_messages
|
||||||
|
if @params[:before].present?
|
||||||
|
@conversation.messages.reorder('created_at desc').where("id < ?", @params[:before]).limit(20).reverse
|
||||||
|
else
|
||||||
|
@conversation.messages.reorder('created_at desc').limit(20).reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
app/helpers/api/base_helper.rb
Normal file
2
app/helpers/api/base_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::BaseHelper
|
||||||
|
end
|
2
app/helpers/api/v1/agents_helper.rb
Normal file
2
app/helpers/api/v1/agents_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::AgentsHelper
|
||||||
|
end
|
2
app/helpers/api/v1/canned_responses_helper.rb
Normal file
2
app/helpers/api/v1/canned_responses_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::CannedResponsesHelper
|
||||||
|
end
|
2
app/helpers/api/v1/conversations_helper.rb
Normal file
2
app/helpers/api/v1/conversations_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::ConversationsHelper
|
||||||
|
end
|
2
app/helpers/api/v1/reports_helper.rb
Normal file
2
app/helpers/api/v1/reports_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::ReportsHelper
|
||||||
|
end
|
2
app/helpers/api/v1/subscriptions_helper.rb
Normal file
2
app/helpers/api/v1/subscriptions_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::SubscriptionsHelper
|
||||||
|
end
|
2
app/helpers/api/v1/webhooks_helper.rb
Normal file
2
app/helpers/api/v1/webhooks_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::WebhooksHelper
|
||||||
|
end
|
2
app/helpers/api/v1/widget/messages_helper.rb
Normal file
2
app/helpers/api/v1/widget/messages_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module Api::V1::Widget::MessagesHelper
|
||||||
|
end
|
2
app/helpers/application_helper.rb
Normal file
2
app/helpers/application_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module ApplicationHelper
|
||||||
|
end
|
2
app/helpers/home_helper.rb
Normal file
2
app/helpers/home_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module HomeHelper
|
||||||
|
end
|
10
app/identities/account_identity.rb
Normal file
10
app/identities/account_identity.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class AccountIdentity < Nightfury::Identity::Base
|
||||||
|
metric :conversations_count, :count_time_series, store_as: :b, step: :day
|
||||||
|
metric :incoming_messages_count, :count_time_series, step: :day
|
||||||
|
metric :outgoing_messages_count, :count_time_series, step: :day
|
||||||
|
metric :avg_first_response_time, :avg_time_series, store_as: :d, step: :day
|
||||||
|
metric :avg_resolution_time, :avg_time_series, store_as: :f, step: :day
|
||||||
|
metric :resolutions_count, :count_time_series, store_as: :g, step: :day
|
||||||
|
end
|
||||||
|
|
||||||
|
AccountIdentity.store_as = :ci
|
8
app/identities/agent_identity.rb
Normal file
8
app/identities/agent_identity.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class AgentIdentity < Nightfury::Identity::Base
|
||||||
|
metric :avg_first_response_time, :avg_time_series, store_as: :d, step: :day
|
||||||
|
metric :avg_resolution_time, :avg_time_series, store_as: :f, step: :day
|
||||||
|
metric :resolutions_count, :count_time_series, store_as: :g, step: :day
|
||||||
|
tag :account_id, store_as: :co
|
||||||
|
end
|
||||||
|
|
||||||
|
AgentIdentity.store_as = :ai
|
60
app/javascript/packs/application.js
Normal file
60
app/javascript/packs/application.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* eslint-env browser */
|
||||||
|
/* eslint-disable no-new */
|
||||||
|
/* Vue Core */
|
||||||
|
|
||||||
|
import Vue from 'vue';
|
||||||
|
import VueI18n from 'vue-i18n';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
// Global Components
|
||||||
|
import Multiselect from 'vue-multiselect';
|
||||||
|
import WootSwitch from 'components/ui/Switch';
|
||||||
|
import WootWizard from 'components/ui/Wizard';
|
||||||
|
import { sync } from 'vuex-router-sync';
|
||||||
|
import Vuelidate from 'vuelidate';
|
||||||
|
import VTooltip from 'v-tooltip';
|
||||||
|
|
||||||
|
import WootUiKit from '../src/components';
|
||||||
|
import App from '../src/App';
|
||||||
|
import i18n from '../src/i18n';
|
||||||
|
import createAxios from '../src/helper/APIHelper';
|
||||||
|
import commonHelpers from '../src/helper/commons';
|
||||||
|
import router from '../src/routes';
|
||||||
|
import store from '../src/store';
|
||||||
|
import vuePusher from '../src/helper/pusher';
|
||||||
|
import constants from '../src/constants';
|
||||||
|
|
||||||
|
Vue.config.env = process.env;
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
Vue.use(VueI18n);
|
||||||
|
Vue.use(WootUiKit);
|
||||||
|
Vue.use(Vuelidate);
|
||||||
|
Vue.use(VTooltip);
|
||||||
|
|
||||||
|
Vue.component('multiselect', Multiselect);
|
||||||
|
Vue.component('woot-switch', WootSwitch);
|
||||||
|
Vue.component('woot-wizard', WootWizard);
|
||||||
|
|
||||||
|
Object.keys(i18n).forEach((lang) => {
|
||||||
|
Vue.locale(lang, i18n[lang]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.config.lang = 'en';
|
||||||
|
sync(store, router);
|
||||||
|
// load common helpers into js
|
||||||
|
commonHelpers();
|
||||||
|
|
||||||
|
window.WootConstants = constants;
|
||||||
|
window.axios = createAxios(axios);
|
||||||
|
window.bus = new Vue();
|
||||||
|
window.onload = function () {
|
||||||
|
window.WOOT = new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
template: '<App/>',
|
||||||
|
components: { App },
|
||||||
|
}).$mount('#app');
|
||||||
|
}
|
||||||
|
window.pusher = vuePusher.init();
|
30
app/javascript/src/App.vue
Normal file
30
app/javascript/src/App.vue
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<div id="app" class="app-wrapper app-root">
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<router-view></router-view>
|
||||||
|
</transition>
|
||||||
|
<woot-snackbar-box></woot-snackbar-box>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WootSnackbarBox from './components/SnackbarContainer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'app',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
WootSnackbarBox,
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('set_user');
|
||||||
|
this.$store.dispatch('validityCheck');
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import './assets/scss/app';
|
||||||
|
</style>
|
134
app/javascript/src/api/account.js
Normal file
134
app/javascript/src/api/account.js
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
getAgents() {
|
||||||
|
const urlData = endPoints('fetchAgents');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
addAgent(agentInfo) {
|
||||||
|
const urlData = endPoints('addAgent');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, agentInfo)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
editAgent(agentInfo) {
|
||||||
|
const urlData = endPoints('editAgent')(agentInfo.id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.put(urlData.url, agentInfo)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
deleteAgent(agentId) {
|
||||||
|
const urlData = endPoints('deleteAgent')(agentId);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.delete(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
getLabels() {
|
||||||
|
const urlData = endPoints('fetchLabels');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
// Get Inbox related to the account
|
||||||
|
getInboxes() {
|
||||||
|
const urlData = endPoints('fetchInboxes');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteInbox(id) {
|
||||||
|
const urlData = endPoints('inbox').delete(id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.delete(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
listInboxAgents(id) {
|
||||||
|
const urlData = endPoints('inbox').agents.get(id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateInboxAgents(inboxId, agentList) {
|
||||||
|
const urlData = endPoints('inbox').agents.post();
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, {
|
||||||
|
user_ids: agentList,
|
||||||
|
inbox_id: inboxId,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
157
app/javascript/src/api/auth.js
Normal file
157
app/javascript/src/api/auth.js
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint-env browser */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
login(creds) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post('auth/sign_in', creds)
|
||||||
|
.then((response) => {
|
||||||
|
const expiryDate = moment.unix(response.headers.expiry);
|
||||||
|
Cookies.set('auth_data', response.headers, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
Cookies.set('user', response.data.data, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
register(creds) {
|
||||||
|
const urlData = endPoints('register');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, {
|
||||||
|
account_name: creds.name,
|
||||||
|
email: creds.email,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const expiryDate = moment.unix(response.headers.expiry);
|
||||||
|
Cookies.set('auth_data', response.headers, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
Cookies.set('user', response.data.data, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
validityCheck() {
|
||||||
|
const urlData = endPoints('validityCheck');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
Cookies.remove('auth_data');
|
||||||
|
Cookies.remove('user');
|
||||||
|
window.location = '/login';
|
||||||
|
}
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
const urlData = endPoints('logout');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.delete(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
Cookies.remove('auth_data');
|
||||||
|
Cookies.remove('user');
|
||||||
|
window.location = '/login';
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoggedIn() {
|
||||||
|
return !(!Cookies.getJSON('auth_data'));
|
||||||
|
},
|
||||||
|
|
||||||
|
isAdmin() {
|
||||||
|
if (this.isLoggedIn()) {
|
||||||
|
return Cookies.getJSON('user').role === 'administrator';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAuthData() {
|
||||||
|
if (this.isLoggedIn()) {
|
||||||
|
return Cookies.getJSON('auth_data');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
getChannel() {
|
||||||
|
if (this.isLoggedIn()) {
|
||||||
|
return Cookies.getJSON('user').channel;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
getCurrentUser() {
|
||||||
|
if (this.isLoggedIn()) {
|
||||||
|
return Cookies.getJSON('user');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
verifyPasswordToken({ confirmationToken }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post('auth/confirmation', {
|
||||||
|
confirmation_token: confirmationToken,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setNewPassword({ resetPasswordToken, password, confirmPassword }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.put('auth/password', {
|
||||||
|
reset_password_token: resetPasswordToken,
|
||||||
|
password_confirmation: confirmPassword,
|
||||||
|
password,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const expiryDate = moment.unix(response.headers.expiry);
|
||||||
|
Cookies.set('auth_data', response.headers, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
Cookies.set('user', response.data.data, { expires: expiryDate.diff(moment(), 'days') });
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
resetPassword({ email }) {
|
||||||
|
const urlData = endPoints('resetPassword');
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, { email })
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
19
app/javascript/src/api/billing.js
Normal file
19
app/javascript/src/api/billing.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/* global axios */
|
||||||
|
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getSubscription() {
|
||||||
|
const urlData = endPoints('subscriptions').get();
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
106
app/javascript/src/api/cannedResponse.js
Normal file
106
app/javascript/src/api/cannedResponse.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
getAllCannedResponses() {
|
||||||
|
const urlData = endPoints('cannedResponse').get();
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
searchCannedResponse({ searchKey }) {
|
||||||
|
let urlData = endPoints('cannedResponse').get();
|
||||||
|
urlData = `${urlData.url}?search=${searchKey}`;
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
addCannedResponse(cannedResponseObj) {
|
||||||
|
const urlData = endPoints('cannedResponse').post();
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, cannedResponseObj)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
editCannedResponse(cannedResponseObj) {
|
||||||
|
const urlData = endPoints('cannedResponse').put(cannedResponseObj.id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.put(urlData.url, cannedResponseObj)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
deleteCannedResponse(responseId) {
|
||||||
|
const urlData = endPoints('cannedResponse').delete(responseId);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.delete(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
getLabels() {
|
||||||
|
const urlData = endPoints('fetchLabels');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
// Get Inbox related to the account
|
||||||
|
getInboxes() {
|
||||||
|
const urlData = endPoints('fetchInboxes');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
console.log('fetch inboxes success');
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log('fetch inboxes failure');
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
53
app/javascript/src/api/channels.js
Normal file
53
app/javascript/src/api/channels.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Get Inbox related to the account
|
||||||
|
createChannel(channel, channelParams) {
|
||||||
|
const urlData = endPoints('createChannel')(channel, channelParams);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
addAgentsToChannel(inboxId, agentsId) {
|
||||||
|
const urlData = endPoints('addAgentsToChannel');
|
||||||
|
urlData.params.inbox_id = inboxId;
|
||||||
|
urlData.params.user_ids = agentsId;
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchFacebookPages(token) {
|
||||||
|
const urlData = endPoints('fetchFacebookPages');
|
||||||
|
urlData.params.omniauth_token = token;
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
176
app/javascript/src/api/endPoints.js
Normal file
176
app/javascript/src/api/endPoints.js
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/* eslint arrow-body-style: ["error", "always"] */
|
||||||
|
|
||||||
|
const endPoints = {
|
||||||
|
resetPassword: {
|
||||||
|
url: 'auth/password',
|
||||||
|
},
|
||||||
|
register: {
|
||||||
|
url: 'api/v1/accounts.json',
|
||||||
|
},
|
||||||
|
validityCheck: {
|
||||||
|
url: '/auth/validate_token',
|
||||||
|
},
|
||||||
|
logout: {
|
||||||
|
url: 'auth/sign_out',
|
||||||
|
},
|
||||||
|
me: {
|
||||||
|
url: 'api/v1/conversations.json',
|
||||||
|
params: { type: 0, page: 1 },
|
||||||
|
},
|
||||||
|
|
||||||
|
getInbox: {
|
||||||
|
url: 'api/v1/conversations.json',
|
||||||
|
params: { inbox_id: null },
|
||||||
|
},
|
||||||
|
|
||||||
|
conversations(id) {
|
||||||
|
return { url: `api/v1/conversations/${id}.json`, params: { before: null } };
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveConversation(id) {
|
||||||
|
return { url: `api/v1/conversations/${id}/toggle_status.json` };
|
||||||
|
},
|
||||||
|
|
||||||
|
sendMessage(conversationId, message) {
|
||||||
|
return { url: `api/v1/conversations/${conversationId}/messages.json`, params: { message } };
|
||||||
|
},
|
||||||
|
|
||||||
|
addPrivateNote(conversationId, message) {
|
||||||
|
return { url: `api/v1/conversations/${conversationId}/messages.json?`, params: { message, private: 'true' } };
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchLabels: {
|
||||||
|
url: 'api/v1/labels.json',
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchInboxes: {
|
||||||
|
url: 'api/v1/inboxes.json',
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchAgents: {
|
||||||
|
url: 'api/v1/agents.json',
|
||||||
|
},
|
||||||
|
|
||||||
|
addAgent: {
|
||||||
|
url: 'api/v1/agents.json',
|
||||||
|
},
|
||||||
|
|
||||||
|
editAgent(id) {
|
||||||
|
return { url: `api/v1/agents/${id}` };
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteAgent({ id }) {
|
||||||
|
return { url: `api/v1/agents/${id}` };
|
||||||
|
},
|
||||||
|
|
||||||
|
createChannel(channel, channelParams) {
|
||||||
|
return { url: `api/v1/callbacks/register_${channel}_page.json`, params: channelParams };
|
||||||
|
},
|
||||||
|
|
||||||
|
addAgentsToChannel: {
|
||||||
|
url: 'api/v1/inbox_members.json',
|
||||||
|
params: { user_ids: [], inbox_id: null },
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchFacebookPages: {
|
||||||
|
url: 'api/v1/callbacks/get_facebook_pages.json',
|
||||||
|
params: { omniauth_token: '' },
|
||||||
|
},
|
||||||
|
|
||||||
|
assignAgent(conversationId, AgentId) {
|
||||||
|
return { url: `/api/v1/conversations/${conversationId}/assignments?assignee_id=${AgentId}` };
|
||||||
|
},
|
||||||
|
|
||||||
|
fbMarkSeen: {
|
||||||
|
url: 'api/v1/facebook_indicators/mark_seen',
|
||||||
|
},
|
||||||
|
|
||||||
|
fbTyping(status) {
|
||||||
|
return {
|
||||||
|
url: `api/v1/facebook_indicators/typing_${status}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
markMessageRead(id) {
|
||||||
|
return {
|
||||||
|
url: `api/v1/conversations/${id}/update_last_seen`,
|
||||||
|
params: {
|
||||||
|
agent_last_seen_at: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Canned Response [GET, POST, PUT, DELETE]
|
||||||
|
cannedResponse: {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
url: 'api/v1/canned_responses',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getOne({ id }) {
|
||||||
|
return {
|
||||||
|
url: `api/v1/canned_responses/${id}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
post() {
|
||||||
|
return {
|
||||||
|
url: 'api/v1/canned_responses',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
put(id) {
|
||||||
|
return {
|
||||||
|
url: `api/v1/canned_responses/${id}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
delete(id) {
|
||||||
|
return {
|
||||||
|
url: `api/v1/canned_responses/${id}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
reports: {
|
||||||
|
account(metric, from, to) {
|
||||||
|
return {
|
||||||
|
url: `/api/v1/reports/account?metric=${metric}&since=${from}&to=${to}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
accountSummary(accountId, from, to) {
|
||||||
|
return {
|
||||||
|
url: `/api/v1/reports/${accountId}/account_summary?since=${from}&to=${to}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
subscriptions: {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
url: '/api/v1/subscriptions',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
inbox: {
|
||||||
|
delete(id) {
|
||||||
|
return {
|
||||||
|
url: `/api/v1/inboxes/${id}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
agents: {
|
||||||
|
get(id) {
|
||||||
|
return {
|
||||||
|
url: `/api/v1/inbox_members/${id}.json`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
post() {
|
||||||
|
return {
|
||||||
|
url: '/api/v1/inbox_members.json',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (page) => {
|
||||||
|
return endPoints[page];
|
||||||
|
};
|
99
app/javascript/src/api/inbox/conversation.js
Normal file
99
app/javascript/src/api/inbox/conversation.js
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from '../endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
fetchConversation(id) {
|
||||||
|
const urlData = endPoints('conversations')(id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleStatus(id) {
|
||||||
|
const urlData = endPoints('resolveConversation')(id);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
assignAgent([id, agentId]) {
|
||||||
|
const urlData = endPoints('assignAgent')(id, agentId);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
markSeen({ inboxId, senderId }) {
|
||||||
|
const urlData = endPoints('fbMarkSeen');
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, {
|
||||||
|
inbox_id: inboxId,
|
||||||
|
sender_id: senderId,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
fbTyping({ flag, inboxId, senderId }) {
|
||||||
|
const urlData = endPoints('fbTyping')(flag);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, {
|
||||||
|
inbox_id: inboxId,
|
||||||
|
sender_id: senderId,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
markMessageRead({ id, lastSeen }) {
|
||||||
|
const urlData = endPoints('markMessageRead')(id);
|
||||||
|
urlData.params.agent_last_seen_at = lastSeen;
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
33
app/javascript/src/api/inbox/index.js
Normal file
33
app/javascript/src/api/inbox/index.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from '../endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
fetchAllConversations(params, callback) {
|
||||||
|
const urlData = endPoints('getInbox');
|
||||||
|
|
||||||
|
if (params.inbox !== 0) {
|
||||||
|
urlData.params.inbox_id = params.inbox;
|
||||||
|
} else {
|
||||||
|
urlData.params.inbox_id = null;
|
||||||
|
}
|
||||||
|
urlData.params = {
|
||||||
|
...urlData.params,
|
||||||
|
conversation_status_id: params.convStatus,
|
||||||
|
assignee_type_id: params.assigneeStatus,
|
||||||
|
};
|
||||||
|
axios.get(urlData.url, {
|
||||||
|
params: urlData.params,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
callback(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
54
app/javascript/src/api/inbox/message.js
Normal file
54
app/javascript/src/api/inbox/message.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/* eslint no-console: 0 */
|
||||||
|
/* global axios */
|
||||||
|
/* eslint no-undef: "error" */
|
||||||
|
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
||||||
|
import endPoints from '../endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
sendMessage([conversationId, message]) {
|
||||||
|
const urlData = endPoints('sendMessage')(conversationId, message);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
addPrivateNote([conversationId, message]) {
|
||||||
|
const urlData = endPoints('addPrivateNote')(conversationId, message);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.post(urlData.url, urlData.params)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchPreviousMessages({ id, before }) {
|
||||||
|
const urlData = endPoints('conversations')(id);
|
||||||
|
urlData.params.before = before;
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url, {
|
||||||
|
params: urlData.params,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
32
app/javascript/src/api/reports.js
Normal file
32
app/javascript/src/api/reports.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/* global axios */
|
||||||
|
|
||||||
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAccountReports(metric, from, to) {
|
||||||
|
const urlData = endPoints('reports').account(metric, from, to);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
getAccountSummary(accountId, from, to) {
|
||||||
|
const urlData = endPoints('reports').accountSummary(accountId, from, to);
|
||||||
|
const fetchPromise = new Promise((resolve, reject) => {
|
||||||
|
axios.get(urlData.url)
|
||||||
|
.then((response) => {
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return fetchPromise;
|
||||||
|
},
|
||||||
|
};
|
BIN
app/javascript/src/assets/audio/ding.mp3
Normal file
BIN
app/javascript/src/assets/audio/ding.mp3
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue