mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	| @@ -1,4 +1 @@ | |||||||
| pusher_cluster= |  | ||||||
| pusher_key= |  | ||||||
|  |  | ||||||
| fb_app_id= | fb_app_id= | ||||||
|   | |||||||
| @@ -43,7 +43,6 @@ module.exports = { | |||||||
|   }, |   }, | ||||||
|   globals: { |   globals: { | ||||||
|     __WEBPACK_ENV__: true, |     __WEBPACK_ENV__: true, | ||||||
|     __PUSHER__: true, |  | ||||||
|     __FB_APP_ID__: true, |     __FB_APP_ID__: true, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ Lint/UselessAssignment: | |||||||
|   Exclude: |   Exclude: | ||||||
|     - 'app/controllers/api/v1/callbacks_controller.rb' |     - 'app/controllers/api/v1/callbacks_controller.rb' | ||||||
|     - 'app/controllers/api/v1/facebook_indicators_controller.rb' |     - 'app/controllers/api/v1/facebook_indicators_controller.rb' | ||||||
|     - 'app/listeners/pusher_listener.rb' |     - 'app/listeners/action_cable_listener.rb' | ||||||
|     - 'app/listeners/reporting_listener.rb' |     - 'app/listeners/reporting_listener.rb' | ||||||
|     - 'app/models/channel/facebook_page.rb' |     - 'app/models/channel/facebook_page.rb' | ||||||
|     - 'app/models/facebook_page.rb' |     - 'app/models/facebook_page.rb' | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -39,7 +39,6 @@ gem 'devise_token_auth', git: 'https://github.com/lynndylanhurley/devise_token_a | |||||||
| gem 'pundit' | gem 'pundit' | ||||||
|  |  | ||||||
| ##--- gems for pubsub service ---## | ##--- gems for pubsub service ---## | ||||||
| gem 'pusher' |  | ||||||
| gem 'wisper', '2.0.0' | gem 'wisper', '2.0.0' | ||||||
|  |  | ||||||
| ##--- gems for reporting ---## | ##--- gems for reporting ---## | ||||||
| @@ -72,6 +71,7 @@ group :development do | |||||||
| end | end | ||||||
|  |  | ||||||
| group :test do | group :test do | ||||||
|  |   gem 'action-cable-testing' | ||||||
|   gem 'mock_redis' |   gem 'mock_redis' | ||||||
|   gem 'shoulda-matchers' |   gem 'shoulda-matchers' | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @@ -113,6 +113,8 @@ GIT | |||||||
| GEM | GEM | ||||||
|   remote: https://rubygems.org/ |   remote: https://rubygems.org/ | ||||||
|   specs: |   specs: | ||||||
|  |     action-cable-testing (0.6.0) | ||||||
|  |       actioncable (>= 5.0) | ||||||
|     addressable (2.7.0) |     addressable (2.7.0) | ||||||
|       public_suffix (>= 2.0.2, < 5.0) |       public_suffix (>= 2.0.2, < 5.0) | ||||||
|     ast (2.4.0) |     ast (2.4.0) | ||||||
| @@ -161,13 +163,6 @@ GEM | |||||||
|     coderay (1.1.2) |     coderay (1.1.2) | ||||||
|     coercible (1.0.0) |     coercible (1.0.0) | ||||||
|       descendants_tracker (~> 0.0.1) |       descendants_tracker (~> 0.0.1) | ||||||
|     coffee-rails (5.0.0) |  | ||||||
|       coffee-script (>= 2.2.0) |  | ||||||
|       railties (>= 5.2.0) |  | ||||||
|     coffee-script (2.4.1) |  | ||||||
|       coffee-script-source |  | ||||||
|       execjs |  | ||||||
|     coffee-script-source (1.12.2) |  | ||||||
|     concurrent-ruby (1.1.5) |     concurrent-ruby (1.1.5) | ||||||
|     connection_pool (2.2.2) |     connection_pool (2.2.2) | ||||||
|     crass (1.0.5) |     crass (1.0.5) | ||||||
| @@ -211,7 +206,6 @@ GEM | |||||||
|     httparty (0.17.1) |     httparty (0.17.1) | ||||||
|       mime-types (~> 3.0) |       mime-types (~> 3.0) | ||||||
|       multi_xml (>= 0.5.2) |       multi_xml (>= 0.5.2) | ||||||
|     httpclient (2.8.3) |  | ||||||
|     i18n (1.7.0) |     i18n (1.7.0) | ||||||
|       concurrent-ruby (~> 1.0) |       concurrent-ruby (~> 1.0) | ||||||
|     ice_nine (0.11.2) |     ice_nine (0.11.2) | ||||||
| @@ -269,7 +263,6 @@ GEM | |||||||
|     minitest (5.12.2) |     minitest (5.12.2) | ||||||
|     mock_redis (0.22.0) |     mock_redis (0.22.0) | ||||||
|     msgpack (1.3.1) |     msgpack (1.3.1) | ||||||
|     multi_json (1.14.1) |  | ||||||
|     multi_xml (0.6.0) |     multi_xml (0.6.0) | ||||||
|     multipart-post (2.1.1) |     multipart-post (2.1.1) | ||||||
|     naught (1.1.0) |     naught (1.1.0) | ||||||
| @@ -292,11 +285,6 @@ GEM | |||||||
|     puma (3.12.1) |     puma (3.12.1) | ||||||
|     pundit (2.1.0) |     pundit (2.1.0) | ||||||
|       activesupport (>= 3.0.0) |       activesupport (>= 3.0.0) | ||||||
|     pusher (1.3.3) |  | ||||||
|       httpclient (~> 2.7) |  | ||||||
|       multi_json (~> 1.0) |  | ||||||
|       pusher-signature (~> 0.1.8) |  | ||||||
|     pusher-signature (0.1.8) |  | ||||||
|     rack (2.0.7) |     rack (2.0.7) | ||||||
|     rack-cache (1.9.0) |     rack-cache (1.9.0) | ||||||
|       rack (>= 0.4) |       rack (>= 0.4) | ||||||
| @@ -452,6 +440,7 @@ PLATFORMS | |||||||
|   ruby |   ruby | ||||||
|  |  | ||||||
| DEPENDENCIES | DEPENDENCIES | ||||||
|  |   action-cable-testing | ||||||
|   acts-as-taggable-on! |   acts-as-taggable-on! | ||||||
|   attr_extras |   attr_extras | ||||||
|   bootsnap |   bootsnap | ||||||
| @@ -459,7 +448,6 @@ DEPENDENCIES | |||||||
|   byebug |   byebug | ||||||
|   carrierwave-aws |   carrierwave-aws | ||||||
|   chargebee (~> 2) |   chargebee (~> 2) | ||||||
|   coffee-rails |  | ||||||
|   devise! |   devise! | ||||||
|   devise_token_auth! |   devise_token_auth! | ||||||
|   facebook-messenger |   facebook-messenger | ||||||
| @@ -480,7 +468,6 @@ DEPENDENCIES | |||||||
|   pry-rails |   pry-rails | ||||||
|   puma (~> 3.0) |   puma (~> 3.0) | ||||||
|   pundit |   pundit | ||||||
|   pusher |  | ||||||
|   rack-cors |   rack-cors | ||||||
|   rails (~> 6)! |   rails (~> 6)! | ||||||
|   redis |   redis | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								app/channels/application_cable/channel.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/channels/application_cable/channel.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | class ApplicationCable::Channel < ActionCable::Channel::Base | ||||||
|  | end | ||||||
							
								
								
									
										2
									
								
								app/channels/application_cable/connection.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/channels/application_cable/connection.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | class ApplicationCable::Connection < ActionCable::Connection::Base | ||||||
|  | end | ||||||
							
								
								
									
										5
									
								
								app/channels/room_channel.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/channels/room_channel.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | class RoomChannel < ApplicationCable::Channel | ||||||
|  |   def subscribed | ||||||
|  |     stream_from params[:pubsub_token] | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -5,6 +5,6 @@ class SyncDispatcher < BaseDispatcher | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def listeners |   def listeners | ||||||
|     [PusherListener.instance] |     [ActionCableListener.instance] | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| export default { | export default { | ||||||
|   APP_BASE_URL: '/', |   APP_BASE_URL: '/', | ||||||
|   PUSHER: __PUSHER__, |  | ||||||
|   get apiURL() { |   get apiURL() { | ||||||
|     return `${this.APP_BASE_URL}/`; |     return `${this.APP_BASE_URL}/`; | ||||||
|   }, |   }, | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								app/javascript/dashboard/helper/actionCable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								app/javascript/dashboard/helper/actionCable.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | import { createConsumer } from '@rails/actioncable'; | ||||||
|  |  | ||||||
|  | import AuthAPI from '../api/auth'; | ||||||
|  |  | ||||||
|  | class ActionCableConnector { | ||||||
|  |   constructor(app, pubsubToken) { | ||||||
|  |     const consumer = createConsumer(); | ||||||
|  |     consumer.subscriptions.create( | ||||||
|  |       { | ||||||
|  |         channel: 'RoomChannel', | ||||||
|  |         pubsub_token: pubsubToken, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         received: this.onReceived, | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  |     this.app = app; | ||||||
|  |     this.events = { | ||||||
|  |       'message.created': this.onMessageCreated, | ||||||
|  |       'conversation.created': this.onConversationCreated, | ||||||
|  |       'status_change:conversation': this.onStatusChange, | ||||||
|  |       'user:logout': this.onLogout, | ||||||
|  |       'page:reload': this.onReload, | ||||||
|  |       'assignee.changed': this.onAssigneeChanged, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   onAssigneeChanged = payload => { | ||||||
|  |     const { meta = {}, id } = payload; | ||||||
|  |     const { assignee } = meta || {}; | ||||||
|  |     if (id) { | ||||||
|  |       this.app.$store.dispatch('updateAssignee', { id, assignee }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   onConversationCreated = data => { | ||||||
|  |     this.app.$store.dispatch('addConversation', data); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   onLogout = () => AuthAPI.logout(); | ||||||
|  |  | ||||||
|  |   onMessageCreated = data => { | ||||||
|  |     this.app.$store.dispatch('addMessage', data); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   onReceived = ({ event, data } = {}) => { | ||||||
|  |     if (this.events[event]) { | ||||||
|  |       this.events[event](data); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   onReload = () => window.location.reload(); | ||||||
|  |  | ||||||
|  |   onStatusChange = data => { | ||||||
|  |     this.app.$store.dispatch('addConversation', data); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   init() { | ||||||
|  |     if (AuthAPI.isLoggedIn()) { | ||||||
|  |       const actionCable = new ActionCableConnector( | ||||||
|  |         window.WOOT, | ||||||
|  |         AuthAPI.getPubSubToken() | ||||||
|  |       ); | ||||||
|  |       return actionCable; | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| /* eslint-env browser */ |  | ||||||
| /* eslint no-console: 0 */ |  | ||||||
| import Pusher from 'pusher-js'; |  | ||||||
| import AuthAPI from '../api/auth'; |  | ||||||
| import CONSTANTS from '../constants'; |  | ||||||
|  |  | ||||||
| class VuePusher { |  | ||||||
|   constructor(apiKey, options) { |  | ||||||
|     this.app = options.app; |  | ||||||
|     this.pusher = new Pusher(apiKey, options); |  | ||||||
|     this.channels = []; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   subscribe(channelName) { |  | ||||||
|     const channel = this.pusher.subscribe(channelName); |  | ||||||
|     if (!this.channels.includes(channel)) { |  | ||||||
|       this.channels.push(channelName); |  | ||||||
|     } |  | ||||||
|     this.bindEvent(channel); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   unsubscribe(channelName) { |  | ||||||
|     this.pusher.unsubscribe(channelName); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bindEvent(channel) { |  | ||||||
|     channel.bind('message.created', data => { |  | ||||||
|       this.app.$store.dispatch('addMessage', data); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     channel.bind('conversation.created', data => { |  | ||||||
|       this.app.$store.dispatch('addConversation', data); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     channel.bind('status_change:conversation', data => { |  | ||||||
|       this.app.$store.dispatch('addConversation', data); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     channel.bind('assignee.changed', payload => { |  | ||||||
|       const { meta = {}, id } = payload; |  | ||||||
|       const { assignee } = meta || {}; |  | ||||||
|       if (id) { |  | ||||||
|         this.app.$store.dispatch('updateAssignee', { id, assignee }); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     channel.bind('user:logout', () => AuthAPI.logout()); |  | ||||||
|     channel.bind('page:reload', () => window.location.reload()); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* eslint no-param-reassign: ["error", { "props": false }] */ |  | ||||||
| export default { |  | ||||||
|   init() { |  | ||||||
|     // Log only if env is testing or development. |  | ||||||
|     Pusher.logToConsole = CONSTANTS.PUSHER.logToConsole || true; |  | ||||||
|     // Init Pusher |  | ||||||
|     const options = { |  | ||||||
|       encrypted: true, |  | ||||||
|       app: window.WOOT, |  | ||||||
|       cluster: CONSTANTS.PUSHER.cluster, |  | ||||||
|     }; |  | ||||||
|     const pusher = new VuePusher(CONSTANTS.PUSHER.token, options); |  | ||||||
|     // Add to global Obj |  | ||||||
|     if (AuthAPI.isLoggedIn()) { |  | ||||||
|       pusher.subscribe(AuthAPI.getPubSubToken()); |  | ||||||
|       return pusher.pusher; |  | ||||||
|     } |  | ||||||
|     return null; |  | ||||||
|   }, |  | ||||||
| }; |  | ||||||
| @@ -14,7 +14,6 @@ jest.mock('./login/login.routes', () => ({ | |||||||
| jest.mock('../constants', () => { | jest.mock('../constants', () => { | ||||||
|   return { |   return { | ||||||
|     APP_BASE_URL: '/', |     APP_BASE_URL: '/', | ||||||
|     PUSHER: false, |  | ||||||
|     get apiUrl() { |     get apiUrl() { | ||||||
|       return `${this.APP_BASE_URL}/`; |       return `${this.APP_BASE_URL}/`; | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import * as types from '../mutation-types'; | |||||||
| import router from '../../routes'; | import router from '../../routes'; | ||||||
| import authAPI from '../../api/auth'; | import authAPI from '../../api/auth'; | ||||||
| import createAxios from '../../helper/APIHelper'; | import createAxios from '../../helper/APIHelper'; | ||||||
| import vuePusher from '../../helper/pusher'; | import actionCable from '../../helper/actionCable'; | ||||||
| // initial state | // initial state | ||||||
| const state = { | const state = { | ||||||
|   currentUser: { |   currentUser: { | ||||||
| @@ -61,7 +61,7 @@ const actions = { | |||||||
|         .then(() => { |         .then(() => { | ||||||
|           commit(types.default.SET_CURRENT_USER); |           commit(types.default.SET_CURRENT_USER); | ||||||
|           window.axios = createAxios(axios); |           window.axios = createAxios(axios); | ||||||
|           window.pusher = vuePusher.init(Vue); |           actionCable.init(Vue); | ||||||
|           router.replace({ name: 'home' }); |           router.replace({ name: 'home' }); | ||||||
|           resolve(); |           resolve(); | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -168,7 +168,7 @@ const mutations = { | |||||||
|     _state.chatStatusFilter = data; |     _state.chatStatusFilter = data; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   // Update assignee on pusher message |   // Update assignee on action cable message | ||||||
|   [types.default.UPDATE_ASSIGNEE](_state, payload) { |   [types.default.UPDATE_ASSIGNEE](_state, payload) { | ||||||
|     const [chat] = _state.allConversations.filter(c => c.id === payload.id); |     const [chat] = _state.allConversations.filter(c => c.id === payload.id); | ||||||
|     chat.meta.assignee = payload.assignee; |     chat.meta.assignee = payload.assignee; | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ import createAxios from '../dashboard/helper/APIHelper'; | |||||||
| import commonHelpers from '../dashboard/helper/commons'; | import commonHelpers from '../dashboard/helper/commons'; | ||||||
| import router from '../dashboard/routes'; | import router from '../dashboard/routes'; | ||||||
| import store from '../dashboard/store'; | import store from '../dashboard/store'; | ||||||
| import vuePusher from '../dashboard/helper/pusher'; | import vueActionCable from '../dashboard/helper/actionCable'; | ||||||
| import constants from '../dashboard/constants'; | import constants from '../dashboard/constants'; | ||||||
|  |  | ||||||
| Vue.config.env = process.env; | Vue.config.env = process.env; | ||||||
| @@ -58,7 +58,7 @@ window.onload = () => { | |||||||
|     components: { App }, |     components: { App }, | ||||||
|     template: '<App/>', |     template: '<App/>', | ||||||
|   }).$mount('#app'); |   }).$mount('#app'); | ||||||
|   window.pusher = vuePusher.init(); |   vueActionCable.init(); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| if ('serviceWorker' in navigator) { | if ('serviceWorker' in navigator) { | ||||||
|   | |||||||
| @@ -1,46 +1,53 @@ | |||||||
| class PusherListener < BaseListener | class ActionCableListener < BaseListener | ||||||
|   include Events::Types |   include Events::Types | ||||||
| 
 | 
 | ||||||
|   def conversation_created(event) |   def conversation_created(event) | ||||||
|     conversation, account, timestamp = extract_conversation_and_account(event) |     conversation, account, timestamp = extract_conversation_and_account(event) | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
|     Pusher.trigger(members, CONVERSATION_CREATED, conversation.push_event_data) if members.present? |     send_to_members(members, CONVERSATION_CREATED, conversation.push_event_data) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def conversation_read(event) |   def conversation_read(event) | ||||||
|     conversation, account, timestamp = extract_conversation_and_account(event) |     conversation, account, timestamp = extract_conversation_and_account(event) | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
|     Pusher.trigger(members, CONVERSATION_READ, conversation.push_event_data) if members.present? |     send_to_members(members, CONVERSATION_READ, conversation.push_event_data) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def message_created(event) |   def message_created(event) | ||||||
|     message, account, timestamp = extract_message_and_account(event) |     message, account, timestamp = extract_message_and_account(event) | ||||||
|     conversation = message.conversation |     conversation = message.conversation | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
| 
 |     send_to_members(members, MESSAGE_CREATED, message.push_event_data) | ||||||
|     Pusher.trigger(members, MESSAGE_CREATED, message.push_event_data) if members.present? |  | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def conversation_reopened(event) |   def conversation_reopened(event) | ||||||
|     conversation, account, timestamp = extract_conversation_and_account(event) |     conversation, account, timestamp = extract_conversation_and_account(event) | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
|     Pusher.trigger(members, CONVERSATION_REOPENED, conversation.push_event_data) if members.present? |     send_to_members(members, CONVERSATION_REOPENED, conversation.push_event_data) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def conversation_lock_toggle(event) |   def conversation_lock_toggle(event) | ||||||
|     conversation, account, timestamp = extract_conversation_and_account(event) |     conversation, account, timestamp = extract_conversation_and_account(event) | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
|     Pusher.trigger(members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data) if members.present? |     send_to_members(members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def assignee_changed(event) |   def assignee_changed(event) | ||||||
|     conversation, account, timestamp = extract_conversation_and_account(event) |     conversation, account, timestamp = extract_conversation_and_account(event) | ||||||
|     members = conversation.inbox.members.pluck(:pubsub_token) |     members = conversation.inbox.members.pluck(:pubsub_token) | ||||||
|     Pusher.trigger(members, ASSIGNEE_CHANGED, conversation.push_event_data) if members.present? |     send_to_members(members, ASSIGNEE_CHANGED, conversation.push_event_data) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|  |   def send_to_members(members, event_name, data) | ||||||
|  |     return if members.blank? | ||||||
|  | 
 | ||||||
|  |     members.each do |member| | ||||||
|  |       ActionCable.server.broadcast(member, event: event_name, data: data) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   def push(pubsub_token, data) |   def push(pubsub_token, data) | ||||||
|     # Enqueue sidekiq job to push event to corresponding channel |     # Enqueue sidekiq job to push event to corresponding channel | ||||||
|   end |   end | ||||||
| @@ -4,7 +4,7 @@ module Pubsubable | |||||||
|   extend ActiveSupport::Concern |   extend ActiveSupport::Concern | ||||||
|  |  | ||||||
|   included do |   included do | ||||||
|     # Used by the pusher/PubSub Service we use for real time communications |     # Used by the actionCable/PubSub Service we use for real time communications | ||||||
|     has_secure_token :pubsub_token |     has_secure_token :pubsub_token | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ class User < ApplicationRecord | |||||||
|          :validatable, |          :validatable, | ||||||
|          :confirmable |          :confirmable | ||||||
|  |  | ||||||
|   # Used by the pusher/PubSub Service we use for real time communications |   # Used by the actionCable/PubSub Service we use for real time communications | ||||||
|   has_secure_token :pubsub_token |   has_secure_token :pubsub_token | ||||||
|  |  | ||||||
|   validates_uniqueness_of :email, scope: :account_id |   validates_uniqueness_of :email, scope: :account_id | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								config/cable.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								config/cable.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | development: | ||||||
|  |   adapter: async | ||||||
|  |  | ||||||
|  | test: | ||||||
|  |   adapter: test | ||||||
|  |  | ||||||
|  | staging: | ||||||
|  |   adapter: redis | ||||||
|  |   url: <%= ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379') %> | ||||||
|  |  | ||||||
|  | production: | ||||||
|  |   adapter: redis | ||||||
|  |   url: <%= ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379') %> | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| Pusher.app_id = ENV['pusher_app_id'] |  | ||||||
| Pusher.key    = ENV['pusher_key'] |  | ||||||
| Pusher.secret = ENV['pusher_secret'] |  | ||||||
| Pusher.encrypted = true |  | ||||||
| Pusher.logger = Rails.logger |  | ||||||
| Pusher.cluster = ENV['pusher_cluster'] |  | ||||||
| @@ -17,19 +17,11 @@ environment.loaders.append('audio', { | |||||||
|  |  | ||||||
| environment.config.merge({ resolve }); | environment.config.merge({ resolve }); | ||||||
|  |  | ||||||
| const { | const { fb_app_id: fbAppID } = process.env; | ||||||
|   pusher_cluster: cluster, |  | ||||||
|   pusher_key: token, |  | ||||||
|   fb_app_id: fbAppID, |  | ||||||
| } = process.env; |  | ||||||
|  |  | ||||||
| environment.plugins.prepend( | environment.plugins.prepend( | ||||||
|   'DefinePlugin', |   'DefinePlugin', | ||||||
|   new webpack.DefinePlugin({ |   new webpack.DefinePlugin({ | ||||||
|     __PUSHER__: { |  | ||||||
|       token: `"${token}"`, |  | ||||||
|       cluster: `"${cluster}"`, |  | ||||||
|     }, |  | ||||||
|     __FB_ID__: `"${fbAppID}"`, |     __FB_ID__: `"${fbAppID}"`, | ||||||
|   }) |   }) | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -26,17 +26,6 @@ development: | |||||||
|  |  | ||||||
| Following changes has to be in `config/application.yml` | Following changes has to be in `config/application.yml` | ||||||
|  |  | ||||||
| ### Configure Pusher |  | ||||||
|  |  | ||||||
| Chatwoot uses [Pusher](https://pusher.com/) to handle realtime messages. Create a free account on Pusher and fill the following environment values. |  | ||||||
|  |  | ||||||
| ```yml |  | ||||||
| pusher_app_id: '' |  | ||||||
| pusher_key: '' |  | ||||||
| pusher_secret: '' |  | ||||||
| pusher_cluster: '' |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### Configure FB Channel | ### Configure FB Channel | ||||||
|  |  | ||||||
| To use FB Channel, you have to create an Facebook app in developer portal. You can find more details about creating FB channels [here](https://developers.facebook.com/docs/apps/#register) | To use FB Channel, you have to create an Facebook app in developer portal. You can find more details about creating FB channels [here](https://developers.facebook.com/docs/apps/#register) | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@babel/polyfill": "^7.6.0", |     "@babel/polyfill": "^7.6.0", | ||||||
|  |     "@rails/actioncable": "^6.0.0", | ||||||
|     "@rails/webpacker": "^4.0.7", |     "@rails/webpacker": "^4.0.7", | ||||||
|     "axios": "^0.19.0", |     "axios": "^0.19.0", | ||||||
|     "babel-helper-vue-jsx-merge-props": "^2.0.3", |     "babel-helper-vue-jsx-merge-props": "^2.0.3", | ||||||
| @@ -27,7 +28,6 @@ | |||||||
|     "js-cookie": "~2.1.3", |     "js-cookie": "~2.1.3", | ||||||
|     "md5": "~2.2.1", |     "md5": "~2.2.1", | ||||||
|     "moment": "~2.19.3", |     "moment": "~2.19.3", | ||||||
|     "pusher-js": "~4.0.0", |  | ||||||
|     "query-string": "5", |     "query-string": "5", | ||||||
|     "spinkit": "~1.2.5", |     "spinkit": "~1.2.5", | ||||||
|     "tween.js": "~16.6.0", |     "tween.js": "~16.6.0", | ||||||
|   | |||||||
| @@ -1,10 +1,3 @@ | |||||||
| #pusher |  | ||||||
|  |  | ||||||
| pusher_app_id: '' |  | ||||||
| pusher_key: '' |  | ||||||
| pusher_secret: '' |  | ||||||
| pusher_cluster: '' |  | ||||||
|  |  | ||||||
| #fb app | #fb app | ||||||
| fb_verify_token: '' | fb_verify_token: '' | ||||||
| fb_app_secret: '' | fb_app_secret: '' | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								spec/controllers/room_channel_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/controllers/room_channel_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe RoomChannel, type: :channel do | ||||||
|  |   let!(:user) { create(:user) } | ||||||
|  |  | ||||||
|  |   before do | ||||||
|  |     stub_connection | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   it 'subscribes to a stream when pubsub_token is provided' do | ||||||
|  |     subscribe(pubsub_token: user.uid) | ||||||
|  |     expect(subscription).to be_confirmed | ||||||
|  |     expect(subscription).to have_stream_for(user.uid) | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -4,6 +4,8 @@ require File.expand_path('../config/environment', __dir__) | |||||||
| # Prevent database truncation if the environment is production | # Prevent database truncation if the environment is production | ||||||
| abort('The Rails environment is running in production mode!') if Rails.env.production? | abort('The Rails environment is running in production mode!') if Rails.env.production? | ||||||
| require 'rspec/rails' | require 'rspec/rails' | ||||||
|  | require 'action_cable/testing/rspec' | ||||||
|  |  | ||||||
| # Add additional requires below this line. Rails is not loaded until this point! | # Add additional requires below this line. Rails is not loaded until this point! | ||||||
|  |  | ||||||
| # Requires supporting ruby files with custom matchers and macros, etc, in | # Requires supporting ruby files with custom matchers and macros, etc, in | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -966,6 +966,11 @@ | |||||||
|     "@nodelib/fs.scandir" "2.1.3" |     "@nodelib/fs.scandir" "2.1.3" | ||||||
|     fastq "^1.6.0" |     fastq "^1.6.0" | ||||||
|  |  | ||||||
|  | "@rails/actioncable@^6.0.0": | ||||||
|  |   version "6.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.0.tgz#8bff9c902be1531ef7a9e191562e9771efcfdfe1" | ||||||
|  |   integrity sha512-DieouotYHpI6k2EGTCnh1eMvD3W8p3zqjWXEYj4z0khJ+A0qQ5tHxihjTEkio54MVwqHt1DcpUm2woh2n/alCA== | ||||||
|  |  | ||||||
| "@rails/webpacker@^4.0.7": | "@rails/webpacker@^4.0.7": | ||||||
|   version "4.0.7" |   version "4.0.7" | ||||||
|   resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-4.0.7.tgz#268571bf974e78ce57eca9fa478f5bd97fd5182c" |   resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-4.0.7.tgz#268571bf974e78ce57eca9fa478f5bd97fd5182c" | ||||||
| @@ -4431,13 +4436,6 @@ fastq@^1.6.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     reusify "^1.0.0" |     reusify "^1.0.0" | ||||||
|  |  | ||||||
| faye-websocket@0.9.4: |  | ||||||
|   version "0.9.4" |  | ||||||
|   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.9.4.tgz#885934c79effb0409549e0c0a3801ed17a40cdad" |  | ||||||
|   integrity sha1-iFk0x57/sECVSeDAo4Ae0XpAza0= |  | ||||||
|   dependencies: |  | ||||||
|     websocket-driver ">=0.5.1" |  | ||||||
|  |  | ||||||
| faye-websocket@^0.10.0: | faye-websocket@^0.10.0: | ||||||
|   version "0.10.0" |   version "0.10.0" | ||||||
|   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" |   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" | ||||||
| @@ -8730,14 +8728,6 @@ punycode@^2.1.0, punycode@^2.1.1: | |||||||
|   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" |   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" | ||||||
|   integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== |   integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== | ||||||
|  |  | ||||||
| pusher-js@~4.0.0: |  | ||||||
|   version "4.0.0" |  | ||||||
|   resolved "https://registry.yarnpkg.com/pusher-js/-/pusher-js-4.0.0.tgz#3f53f9a8e2cb55b89b7724881615f891f200ab8e" |  | ||||||
|   integrity sha1-P1P5qOLLVbibdySIFhX4kfIAq44= |  | ||||||
|   dependencies: |  | ||||||
|     faye-websocket "0.9.4" |  | ||||||
|     xmlhttprequest "^1.8.0" |  | ||||||
|  |  | ||||||
| q@^1.1.2: | q@^1.1.2: | ||||||
|   version "1.5.1" |   version "1.5.1" | ||||||
|   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" |   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" | ||||||
| @@ -10975,11 +10965,6 @@ xml-name-validator@^3.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" |   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" | ||||||
|   integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== |   integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== | ||||||
|  |  | ||||||
| xmlhttprequest@^1.8.0: |  | ||||||
|   version "1.8.0" |  | ||||||
|   resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" |  | ||||||
|   integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= |  | ||||||
|  |  | ||||||
| xtend@^4.0.0, xtend@~4.0.1: | xtend@^4.0.0, xtend@~4.0.1: | ||||||
|   version "4.0.2" |   version "4.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" |   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Pranav Raj S
					Pranav Raj S