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