From 5f74e5043391caefa1a75a05b844d12db9549886 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 9 Dec 2015 14:09:37 +0100 Subject: [PATCH] Rewrite of web socket event backend. --- .../app/controllers/maintenance.coffee | 8 +- .../app/controllers/widget/maintenance.coffee | 8 +- .../widget/online_notification.coffee | 2 +- .../widget/session_taken_over.coffee | 10 +- .../app/lib/app_post/websocket.coffee | 53 +++---- app/controllers/long_polling_controller.rb | 99 ++++--------- lib/sessions.rb | 18 ++- lib/sessions/backend/collections/base.rb | 2 +- lib/sessions/backend/ticket_overview_index.rb | 2 +- lib/sessions/backend/ticket_overview_list.rb | 4 +- lib/sessions/event.rb | 20 +-- lib/sessions/event/base.rb | 44 ++++++ lib/sessions/event/broadcast.rb | 45 ++++++ lib/sessions/event/chat_agent_state.rb | 6 +- lib/sessions/event/chat_base.rb | 29 ++-- lib/sessions/event/chat_session_close.rb | 2 + lib/sessions/event/chat_session_init.rb | 4 +- .../event/chat_session_leave_temporary.rb | 1 + lib/sessions/event/chat_session_message.rb | 4 +- lib/sessions/event/chat_session_start.rb | 2 + lib/sessions/event/chat_session_typing.rb | 2 + lib/sessions/event/chat_status_agent.rb | 1 + lib/sessions/event/chat_status_customer.rb | 6 +- lib/sessions/event/login.rb | 32 +++++ lib/sessions/event/ping.rb | 9 ++ lib/sessions/event/spool.rb | 41 ++++++ script/websocket-server.rb | 134 ++---------------- 27 files changed, 309 insertions(+), 279 deletions(-) create mode 100644 lib/sessions/event/base.rb create mode 100644 lib/sessions/event/broadcast.rb create mode 100644 lib/sessions/event/login.rb create mode 100644 lib/sessions/event/ping.rb create mode 100644 lib/sessions/event/spool.rb diff --git a/app/assets/javascripts/app/controllers/maintenance.coffee b/app/assets/javascripts/app/controllers/maintenance.coffee index 24621fff5..1f5046860 100644 --- a/app/assets/javascripts/app/controllers/maintenance.coffee +++ b/app/assets/javascripts/app/controllers/maintenance.coffee @@ -20,10 +20,10 @@ class Index extends App.ControllerContent params = @formParam(e.target) App.Event.trigger( 'ws:send' - action: 'broadcast' - event: 'session:maintenance' - spool: false - data: params + event: 'broadcast' + data: + event: 'session:maintenance' + data: params ) @notify type: 'success' diff --git a/app/assets/javascripts/app/controllers/widget/maintenance.coffee b/app/assets/javascripts/app/controllers/widget/maintenance.coffee index 87a4396f5..5c0d74f9b 100644 --- a/app/assets/javascripts/app/controllers/widget/maintenance.coffee +++ b/app/assets/javascripts/app/controllers/widget/maintenance.coffee @@ -7,7 +7,7 @@ class Widget extends App.Controller 'session:maintenance' (data) => console.log('session:maintenance', data) - @showMessage( data ) + @showMessage(data) 'maintenance' ) @@ -19,8 +19,8 @@ class Widget extends App.Controller button = 'Close' # convert to html and linkify - message.message = App.Utils.textCleanup( message.message ) - message.message = App.Utils.text2html( message.message ) + message.message = App.Utils.textCleanup(message.message) + message.message = App.Utils.text2html(message.message) new App.SessionMessage( head: message.head @@ -32,4 +32,4 @@ class Widget extends App.Controller forceReload: message.reload ) -App.Config.set( 'maintenance', Widget, 'Widgets' ) \ No newline at end of file +App.Config.set('maintenance', Widget, 'Widgets') \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.coffee index 872a55644..e46a6b6ac 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.coffee @@ -142,7 +142,7 @@ class App.OnlineNotificationWidget extends App.Controller @alreadyShown[item.id] = true if @fetchedData word = "#{item.type}d" - title = "#{item.created_by.displayName()} #{App.i18n.translateInline(word)} #{App.i18n.translateInline(item.object_name)} #{item.title}" + title = "#{item.created_by.displayName()} #{App.i18n.translateInline(word)} #{App.i18n.translateInline(item.object_name)} \"#{item.title}\"" @notifyDesktop( url: item.link title: title diff --git a/app/assets/javascripts/app/controllers/widget/session_taken_over.coffee b/app/assets/javascripts/app/controllers/widget/session_taken_over.coffee index 3f02396c1..6ea0c611b 100644 --- a/app/assets/javascripts/app/controllers/widget/session_taken_over.coffee +++ b/app/assets/javascripts/app/controllers/widget/session_taken_over.coffee @@ -1,5 +1,4 @@ class Widget extends App.Controller - constructor: -> super @bind() @@ -14,13 +13,14 @@ class Widget extends App.Controller # broadcast to other browser instance App.WebSocket.send( - action: 'broadcast' - event: 'session:takeover' + event: 'broadcast' spool: true recipient: user_id: [ App.Session.get( 'id' ) ] data: - taskbar_id: App.TaskManager.TaskbarId() + event: 'session:takeover' + data: + taskbar_id: App.TaskManager.TaskbarId() ) 'maintenance' ) @@ -51,4 +51,4 @@ class Widget extends App.Controller 'maintenance' ) -App.Config.set( 'session_taken_over', Widget, 'Widgets' ) \ No newline at end of file +App.Config.set('session_taken_over', Widget, 'Widgets') diff --git a/app/assets/javascripts/app/lib/app_post/websocket.coffee b/app/assets/javascripts/app/lib/app_post/websocket.coffee index ec25346cb..0fa0f6cdd 100644 --- a/app/assets/javascripts/app/lib/app_post/websocket.coffee +++ b/app/assets/javascripts/app/lib/app_post/websocket.coffee @@ -68,7 +68,7 @@ class _webSocketSingleton extends App.Controller # get spool messages after successful ws login App.Event.bind( - 'ws:login', (data) => + 'ws:login', => @spool() 'ws' ) @@ -106,7 +106,7 @@ class _webSocketSingleton extends App.Controller if @ws.readyState isnt 1 @queue.push data else - string = JSON.stringify( data ) + string = JSON.stringify(data) @ws.send(string) auth: (data) => @@ -114,7 +114,7 @@ class _webSocketSingleton extends App.Controller # logon websocket data = - action: 'login' + event: 'login' session_id: App.Config.get('session_id') fingerprint: App.Browser.fingerprint() @send(data) @@ -125,7 +125,7 @@ class _webSocketSingleton extends App.Controller # build data to send to server data = - action: 'spool' + event: 'spool' if @lastSpoolMessage data['timestamp'] = @lastSpoolMessage @@ -137,10 +137,7 @@ class _webSocketSingleton extends App.Controller App.Delay.set reset, 60000, 'reset-spool-sent-finished-if-not-returned', 'ws' # ask for spool messages - App.Event.trigger( - 'ws:send' - data - ) + @send(data) close: ( params = {} ) => if params['force'] @@ -154,7 +151,7 @@ class _webSocketSingleton extends App.Controller return if @backend is 'ajax' @log 'debug', 'send websocket ping' - @send( { action: 'ping' } ) + @send(event: 'ping') # check if ping is back within 2 min App.Delay.clear 'websocket-ping-check', 'ws' @@ -166,10 +163,10 @@ class _webSocketSingleton extends App.Controller pong: -> return if @backend is 'ajax' - @log 'debug', 'received websocket ping' + @log 'debug', 'received websocket pong' # test again after 1 min - App.Delay.set @ping, 60000, 'websocket-pong', 'ws' + App.Delay.set(@ping, 60000, 'websocket-pong', 'ws') connect: => @@ -216,10 +213,10 @@ class _webSocketSingleton extends App.Controller @queue = [] # send ping to check connection - App.Delay.set @ping, 60000, 'websocket-send-ping-to-heck-connection', 'ws' + App.Delay.set(@ping, 60000, 'websocket-send-ping-to-heck-connection', 'ws') @ws.onmessage = (e) => - pipe = JSON.parse( e.data ) + pipe = JSON.parse(e.data) @log 'debug', 'ws:onmessage', pipe @_receiveMessage(pipe) @@ -271,28 +268,20 @@ class _webSocketSingleton extends App.Controller # go through all blocks for item in data + @log 'debug', 'onmessage', item # set timestamp to get spool messages later if item['spool'] @lastSpoolMessage = Math.round( +new Date()/1000 ) # reset reconnect loop - if item['action'] is 'pong' + if item['event'] is 'pong' @pong() - # fill collection - if item['collection'] - @log 'debug', 'onmessage collection:' + item['collection'] - # fire event if item['event'] - if typeof item['event'] is 'object' - for event in item['event'] - @log 'debug', 'onmessage event:' + event - App.Event.trigger( event, item['data'] ) - else - @log 'debug', 'onmessage event:' + item['event'] - App.Event.trigger( item['event'], item['data'] ) + @log 'debug', "onmessage event: #{item['event']}" + App.Event.trigger(item['event'], item['data']) _ajaxInit: (data = {}) => @@ -304,7 +293,7 @@ class _webSocketSingleton extends App.Controller id: 'ws-login' type: 'POST' url: @Config.get('api_path') + '/message_send' - data: JSON.stringify({ data: { action: 'login' } }) + data: JSON.stringify(data: { event: 'login' }) processData: false queue: false success: (data) => @@ -319,8 +308,8 @@ class _webSocketSingleton extends App.Controller # try reconnect on error after x sec. reconnect = => - @_ajaxInit( force: true ) - App.Delay.set reconnect, 10000, '_ajaxInit-reconnect-on-error', 'ws' + @_ajaxInit(force: true) + App.Delay.set(reconnect, 10000, '_ajaxInit-reconnect-on-error', 'ws') ) _ajaxSend: (data) => @@ -338,7 +327,7 @@ class _webSocketSingleton extends App.Controller App.Ajax.request( type: 'POST' url: @Config.get('api_path') + '/message_send' - data: JSON.stringify({ client_id: @client_id, data: data }) + data: JSON.stringify(client_id: @client_id, data: data) processData: false queue: true success: (data) => @@ -358,19 +347,19 @@ class _webSocketSingleton extends App.Controller id: 'message_receive', type: 'POST' url: @Config.get('api_path') + '/message_receive' - data: JSON.stringify({ client_id: @client_id }) + data: JSON.stringify(client_id: @client_id) processData: false success: (data) => @log 'debug', 'ajax:onmessage', data @_receiveMessage(data) if data && data.error @client_id = undefined - @_ajaxInit( force: true ) + @_ajaxInit(force: true) @_ajaxReceiveWorking = false @_ajaxReceive() error: (data) => @client_id = undefined - @_ajaxInit( force: true ) + @_ajaxInit(force: true) @_ajaxReceiveWorking = false ) diff --git a/app/controllers/long_polling_controller.rb b/app/controllers/long_polling_controller.rb index d3a8ff7b1..492d8af48 100644 --- a/app/controllers/long_polling_controller.rb +++ b/app/controllers/long_polling_controller.rb @@ -17,77 +17,31 @@ class LongPollingController < ApplicationController if !params['data'] params['data'] = {} end + session_data = {} + if current_user.id + session_data = { 'id' => current_user.id } + end # spool messages for new connects if params['data']['spool'] - msg = JSON.generate( params['data'] ) - Sessions.spool_create(msg) + Sessions.spool_create(params['data']) end - - # get spool messages and send them to new client connection - if params['data']['action'] == 'spool' - - # error handling - if params['data']['timestamp'] - log "request spool data > '#{Time.zone.at( params['data']['timestamp'] )}'", client_id - else - log 'request spool init data', client_id + if params['data']['event'] == 'login' + Sessions.create(client_id, session_data, { type: 'ajax' }) + elsif params['data']['event'] + message = Sessions::Event.run( + event: params['data']['event'], + payload: params['data'], + session: session_data, + client_id: client_id, + clients: {}, + options: {}, + ) + if message + Sessions.send(client_id, message) end - - if current_user - spool = Sessions.spool_list( params['data']['timestamp'], current_user.id ) - spool.each { |item| - if item[:type] == 'direct' - log "send spool to (user_id=#{current_user.id})", client_id - Sessions.send( client_id, item[:message] ) - else - log 'send spool', client_id - Sessions.send( client_id, item[:message] ) - end - } - end - - # send spool:sent event to client - sleep 0.2 - log 'send spool:sent event', client_id - Sessions.send( client_id, { event: 'spool:sent', data: { timestamp: Time.zone.now.utc.to_i } } ) - end - - # receive message - if params['data']['action'] == 'login' - user_id = session[:user_id] - user = {} - if user_id - user = User.find( user_id ).attributes - end - log "send auth login (user_id #{user_id})", client_id - Sessions.create( client_id, user, { type: 'ajax' } ) - - # broadcast - elsif params['data']['action'] == 'broadcast' - - # list all current clients - client_list = Sessions.list - client_list.each {|local_client_id, local_client| - if local_client_id != client_id - - # broadcast to recipient list - if params['data']['recipient'] && params['data']['recipient']['user_id'] - params['data']['recipient']['user_id'].each { |loop_user_id| - if local_client[:user]['id'].to_s == loop_user_id.to_s - log "send broadcast from (#{client_id}) to (user_id #{loop_user_id})", local_client_id - Sessions.send( local_client_id, params['data'] ) - end - } - # broadcast every client - else - log "send broadcast from (#{client_id})", local_client_id - Sessions.send( local_client_id, params['data'] ) - end - else - log 'do not send broadcast to it self', client_id - end - } + else + log "unknown message '#{params['data'].inspect}'", client_id end if new_connection @@ -116,13 +70,16 @@ class LongPollingController < ApplicationController sleep 0.25 } #sleep 1 - Sessions.touch( client_id ) + Sessions.touch(client_id) # set max loop time to 24 sec. because of 30 sec. timeout of mod_proxy - count = 12 + count = 3 + if Rails.env.production? + count = 12 + end loop do count = count - 1 - queue = Sessions.queue( client_id ) + queue = Sessions.queue(client_id) if queue && queue[0] logger.debug "send #{queue.inspect} to #{client_id}" render json: queue @@ -133,7 +90,7 @@ class LongPollingController < ApplicationController } #sleep 2 if count == 0 - render json: { action: 'pong' } + render json: { event: 'pong' } return end end @@ -154,7 +111,7 @@ class LongPollingController < ApplicationController def client_id_verify return if !params[:client_id] sessions = Sessions.sessions - return if !sessions.include?( params[:client_id].to_s ) + return if !sessions.include?(params[:client_id].to_s) params[:client_id].to_s end diff --git a/lib/sessions.rb b/lib/sessions.rb index e2dca2df1..79cefbab0 100644 --- a/lib/sessions.rb +++ b/lib/sessions.rb @@ -427,10 +427,11 @@ returns FileUtils.rm_rf path end - def self.spool_create(msg) + def self.spool_create(data) + msg = JSON.generate(data) path = "#{@path}/spool/" FileUtils.mkpath path - file_path = path + "/#{Time.now.utc.to_f}-#{rand(99_999)}" + file_path = "#{path}/#{Time.now.utc.to_f}-#{rand(99_999)}" File.open( file_path, 'wb' ) { |file| data = { msg: msg, @@ -483,18 +484,27 @@ returns next if current_user_id != user_id + message = message_parsed + if message_parsed['event'] == 'broadcast' + message = message_parsed['data'] + end + item = { type: 'direct', - message: message_parsed, + message: message, } data.push item } # spool to every client else + message = message_parsed + if message_parsed['event'] == 'broadcast' + message = message_parsed['data'] + end item = { type: 'broadcast', - message: message_parsed, + message: message, } data.push item end diff --git a/lib/sessions/backend/collections/base.rb b/lib/sessions/backend/collections/base.rb index 41dd5d2dc..f4f3b59f2 100644 --- a/lib/sessions/backend/collections/base.rb +++ b/lib/sessions/backend/collections/base.rb @@ -80,7 +80,7 @@ class Sessions::Backend::Collections::Base @client.log "push assets for push_collection #{items.first.class} for user #{@user.id}" @client.send( data: assets, - event: [ 'loadAssets' ], + event: 'loadAssets', ) @client.log "push push_collection #{items.first.class} for user #{@user.id}" diff --git a/lib/sessions/backend/ticket_overview_index.rb b/lib/sessions/backend/ticket_overview_index.rb index 5055a38ec..d3da40ab5 100644 --- a/lib/sessions/backend/ticket_overview_index.rb +++ b/lib/sessions/backend/ticket_overview_index.rb @@ -58,7 +58,7 @@ class Sessions::Backend::TicketOverviewIndex @client.log "push overview_index for user #{@user.id}" @client.send( - event: ['ticket_overview_index'], + event: 'ticket_overview_index', data: data, ) end diff --git a/lib/sessions/backend/ticket_overview_list.rb b/lib/sessions/backend/ticket_overview_list.rb index bc9fb7f52..04ee027db 100644 --- a/lib/sessions/backend/ticket_overview_list.rb +++ b/lib/sessions/backend/ticket_overview_list.rb @@ -107,7 +107,7 @@ class Sessions::Backend::TicketOverviewList # send update to browser @client.send( data: assets, - event: [ 'loadAssets' ] + event: 'loadAssets' ) @client.send( data: { @@ -120,7 +120,7 @@ class Sessions::Backend::TicketOverviewList owner_id: [], }, }, - event: [ 'ticket_overview_rebuild' ], + event: 'ticket_overview_rebuild', ) end } diff --git a/lib/sessions/event.rb b/lib/sessions/event.rb index 8f72fa3e7..ed7ecb62e 100644 --- a/lib/sessions/event.rb +++ b/lib/sessions/event.rb @@ -1,28 +1,18 @@ class Sessions::Event include ApplicationLib - def self.run(event, data, session, client_id) - adapter = "Sessions::Event::#{event.to_classname}" + def self.run(params) + adapter = "Sessions::Event::#{params[:event].to_classname}" begin backend = load_adapter(adapter) rescue => e - return { error: "No such event #{event}" } + return { error: "No such event #{params[:event]}" } end - ActiveRecord::Base.establish_connection - instance = backend.new(data, session, client_id) - pre = instance.pre - if pre - ActiveRecord::Base.remove_connection - return pre - end + instance = backend.new(params) result = instance.run - post = instance.post - if post - ActiveRecord::Base.remove_connection - return post - end + instance.destroy result end diff --git a/lib/sessions/event/base.rb b/lib/sessions/event/base.rb new file mode 100644 index 000000000..e36273cd1 --- /dev/null +++ b/lib/sessions/event/base.rb @@ -0,0 +1,44 @@ +class Sessions::Event::Base + + def initialize(params) + params.each { |key, value| + instance_variable_set "@#{key}", value + } + + @is_web_socket = false + return if !@clients[@client_id] + @is_web_socket = true + end + + def websocket_send(recipient_client_id, data) + if data.class != Array + msg = "[#{data.to_json}]" + else + msg = data.to_json + end + if @clients[recipient_client_id] + log 'debug', "ws send #{msg}", recipient_client_id + @clients[recipient_client_id][:websocket].send(msg) + else + log 'debug', "fs send #{msg}", recipient_client_id + Sessions.send(recipient_client_id, data) + end + end + + def log(level, data, client_id = nil) + if !@options[:v] + return if level == 'debug' + end + if !client_id + client_id = @client_id + end + # rubocop:disable Rails/Output + puts "#{Time.now.utc.iso8601}:client(#{client_id}) #{data}" + #puts "#{Time.now.utc.iso8601}:#{ level }:client(#{ client_id }) #{ data }" + # rubocop:enable Rails/Output + end + + def destroy + end + +end diff --git a/lib/sessions/event/broadcast.rb b/lib/sessions/event/broadcast.rb new file mode 100644 index 000000000..3fec53074 --- /dev/null +++ b/lib/sessions/event/broadcast.rb @@ -0,0 +1,45 @@ +class Sessions::Event::Broadcast < Sessions::Event::Base + + def run + + # list all current clients + client_list = Sessions.list + client_list.each {|local_client_id, local_client| + if local_client_id == @client_id + log 'notice', 'do not send broadcast to it self' + next + end + + # broadcast to recipient list + if @payload['recipient'] + if @payload['recipient'].class != Hash + log 'error', "recipient attribute isn't a hash '#{@payload['recipient'].inspect}'" + else + if !@payload['recipient'].key?('user_id') + log 'error', "need recipient.user_id attribute '#{@payload['recipient'].inspect}'" + else + if @payload['recipient']['user_id'].class != Array + log 'error', "recipient.user_id attribute isn't an array '#{@payload['recipient']['user_id'].inspect}'" + else + @payload['recipient']['user_id'].each { |user_id| + + next if local_client[:user]['id'].to_i != user_id.to_i + + log 'notice', "send broadcast from (#{@client_id}) to (user_id=#{user_id})", local_client_id + websocket_send(local_client_id, @payload['data']) + } + end + end + end + + # broadcast every client + else + log 'notice', "send broadcast from (#{@client_id})", local_client_id + websocket_send(local_client_id, @payload['data']) + end + } + + false + end + +end diff --git a/lib/sessions/event/chat_agent_state.rb b/lib/sessions/event/chat_agent_state.rb index f89a517bc..56ff7ee55 100644 --- a/lib/sessions/event/chat_agent_state.rb +++ b/lib/sessions/event/chat_agent_state.rb @@ -1,11 +1,12 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase def run + return super if super # check if user has permissions return if !agent_permission_check - Chat::Agent.state(@session['id'], @data['data']['active']) + Chat::Agent.state(@session['id'], @payload['data']['active']) # broadcast new state to agents broadcast_agent_state_update(@session['id']) @@ -14,8 +15,9 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase event: 'chat_agent_state', data: { state: 'ok', - active: @data['data']['active'], + active: @payload['data']['active'], }, } end + end diff --git a/lib/sessions/event/chat_base.rb b/lib/sessions/event/chat_base.rb index 92a5ef153..fe36b7418 100644 --- a/lib/sessions/event/chat_base.rb +++ b/lib/sessions/event/chat_base.rb @@ -1,12 +1,17 @@ -class Sessions::Event::ChatBase +class Sessions::Event::ChatBase < Sessions::Event::Base - def initialize(data, session, client_id) - @data = data - @session = session - @client_id = client_id + def initialize(params) + super(params) + return if !@is_web_socket + ActiveRecord::Base.establish_connection end - def pre + def destroy + return if !@is_web_socket + ActiveRecord::Base.remove_connection + end + + def run # check if feature is enabled return if Setting.get('chat') @@ -18,10 +23,6 @@ class Sessions::Event::ChatBase } end - def post - false - end - def broadcast_agent_state_update(ignore_user_id = nil) # send broadcast to agents @@ -99,11 +100,11 @@ class Sessions::Event::ChatBase end def current_chat_session - Chat::Session.find_by(session_id: @data['data']['session_id']) + Chat::Session.find_by(session_id: @payload['data']['session_id']) end def check_chat_session_exists - if !@data['data'] || !@data['data']['session_id'] + if !@payload['data'] || !@payload['data']['session_id'] error = { event: 'chat_error', data: { @@ -117,7 +118,7 @@ class Sessions::Event::ChatBase error = { event: 'chat_error', data: { - state: "No such session id #{@data['data']['session_id']}", + state: "No such session id #{@payload['data']['session_id']}", }, } Sessions.send(@client_id, error) @@ -125,7 +126,7 @@ class Sessions::Event::ChatBase end def current_chat - Chat.find_by(id: @data['data']['chat_id']) + Chat.find_by(id: @payload['data']['chat_id']) end def check_chat_exists diff --git a/lib/sessions/event/chat_session_close.rb b/lib/sessions/event/chat_session_close.rb index a2b7fae01..4170f82a5 100644 --- a/lib/sessions/event/chat_session_close.rb +++ b/lib/sessions/event/chat_session_close.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase def run + return super if super return if !check_chat_session_exists @@ -57,4 +58,5 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase }, } end + end diff --git a/lib/sessions/event/chat_session_init.rb b/lib/sessions/event/chat_session_init.rb index 4edac5151..c9ce5826d 100644 --- a/lib/sessions/event/chat_session_init.rb +++ b/lib/sessions/event/chat_session_init.rb @@ -1,11 +1,12 @@ class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase def run + return super if super return if !check_chat_exists # create chat session chat_session = Chat::Session.create( - chat_id: @data['data']['chat_id'], + chat_id: @payload['data']['chat_id'], name: '', state: 'waiting', preferences: { @@ -26,4 +27,5 @@ class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase }, } end + end diff --git a/lib/sessions/event/chat_session_leave_temporary.rb b/lib/sessions/event/chat_session_leave_temporary.rb index 42d589993..dbc7d614d 100644 --- a/lib/sessions/event/chat_session_leave_temporary.rb +++ b/lib/sessions/event/chat_session_leave_temporary.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatSessionLeaveTemporary < Sessions::Event::ChatBase def run + return super if super return if !check_chat_session_exists chat_session = current_chat_session diff --git a/lib/sessions/event/chat_session_message.rb b/lib/sessions/event/chat_session_message.rb index 46e317bbf..eb446992d 100644 --- a/lib/sessions/event/chat_session_message.rb +++ b/lib/sessions/event/chat_session_message.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase def run + return super if super return if !check_chat_session_exists chat_session = current_chat_session @@ -10,7 +11,7 @@ class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase end chat_message = Chat::Message.create( chat_session_id: chat_session.id, - content: @data['data']['content'], + content: @payload['data']['content'], created_by_id: user_id, ) message = { @@ -35,4 +36,5 @@ class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase } end + end diff --git a/lib/sessions/event/chat_session_start.rb b/lib/sessions/event/chat_session_start.rb index ab95d6b93..518b4fc41 100644 --- a/lib/sessions/event/chat_session_start.rb +++ b/lib/sessions/event/chat_session_start.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase def run + return super if super agent_permission_check # find first in waiting list @@ -48,4 +49,5 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase nil end + end diff --git a/lib/sessions/event/chat_session_typing.rb b/lib/sessions/event/chat_session_typing.rb index 9ab4b109e..e87463748 100644 --- a/lib/sessions/event/chat_session_typing.rb +++ b/lib/sessions/event/chat_session_typing.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatSessionTyping < Sessions::Event::ChatBase def run + return super if super return if !check_chat_session_exists chat_session = current_chat_session @@ -28,4 +29,5 @@ class Sessions::Event::ChatSessionTyping < Sessions::Event::ChatBase }, } end + end diff --git a/lib/sessions/event/chat_status_agent.rb b/lib/sessions/event/chat_status_agent.rb index 9f0801d8d..220f25723 100644 --- a/lib/sessions/event/chat_status_agent.rb +++ b/lib/sessions/event/chat_status_agent.rb @@ -1,6 +1,7 @@ class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase def run + return super if super # check if user has permissions return if !agent_permission_check diff --git a/lib/sessions/event/chat_status_customer.rb b/lib/sessions/event/chat_status_customer.rb index 7dbeeeec6..5c0a68633 100644 --- a/lib/sessions/event/chat_status_customer.rb +++ b/lib/sessions/event/chat_status_customer.rb @@ -1,12 +1,13 @@ class Sessions::Event::ChatStatusCustomer < Sessions::Event::ChatBase def run + return super if super return if !check_chat_exists # check if it's a chat sessin reconnect session_id = nil - if @data['data']['session_id'] - session_id = @data['data']['session_id'] + if @payload['data']['session_id'] + session_id = @payload['data']['session_id'] # update recipients of existing sessions chat_session = Chat::Session.find_by(session_id: session_id) @@ -17,4 +18,5 @@ class Sessions::Event::ChatStatusCustomer < Sessions::Event::ChatBase data: current_chat.customer_state(session_id), } end + end diff --git a/lib/sessions/event/login.rb b/lib/sessions/event/login.rb new file mode 100644 index 000000000..e9b6b347b --- /dev/null +++ b/lib/sessions/event/login.rb @@ -0,0 +1,32 @@ +class Sessions::Event::Login < Sessions::Event::Base + + def run + + # get user_id + if @payload && @payload['session_id'] + if @is_web_socket + ActiveRecord::Base.establish_connection + end + session = ActiveRecord::SessionStore::Session.find_by(session_id: @payload['session_id']) + if @is_web_socket + ActiveRecord::Base.remove_connection + end + end + + if session && session.data && session.data['user_id'] + new_session_data = { 'id' => session.data['user_id'] } + else + new_session_data = {} + end + + if @clients[@client_id] + @clients[@client_id][:session] = new_session_data + Sessions.create(@client_id, new_session_data, { type: 'websocket' }) + else + Sessions.create(@client_id, new_session_data, { type: 'ajax' }) + end + + false + end + +end diff --git a/lib/sessions/event/ping.rb b/lib/sessions/event/ping.rb new file mode 100644 index 000000000..e4de5cc2b --- /dev/null +++ b/lib/sessions/event/ping.rb @@ -0,0 +1,9 @@ +class Sessions::Event::Ping < Sessions::Event::Base + + def run + { + event: 'pong', + } + end + +end diff --git a/lib/sessions/event/spool.rb b/lib/sessions/event/spool.rb new file mode 100644 index 000000000..702044b4f --- /dev/null +++ b/lib/sessions/event/spool.rb @@ -0,0 +1,41 @@ +class Sessions::Event::Spool < Sessions::Event::Base + + # get spool messages and send them to new client connection + def run + + # error handling + if @payload['timestamp'] + log 'notice', "request spool data > '#{Time.at(@payload['timestamp']).utc.iso8601}'" + else + log 'notice', 'request spool with init data' + end + + if !@session || !@session['id'] + log 'error', "can't send spool, session not authenticated" + return + end + + spool = Sessions.spool_list(@payload['timestamp'], @session['id']) + spool.each { |item| + + # create new msg to push to client + if item[:type] == 'direct' + log 'notice', "send spool to (user_id=#{@session['id']})" + websocket_send(@client_id, item[:message]) + else + log 'notice', 'send spool' + websocket_send(@client_id, item[:message]) + end + } + + # send spool:sent event to client + log 'notice', 'send spool:sent event' + { + event: 'spool:sent', + data: { + timestamp: Time.now.utc.to_i, + }, + } + end + +end diff --git a/script/websocket-server.rb b/script/websocket-server.rb index b31818c48..2ea375723 100755 --- a/script/websocket-server.rb +++ b/script/websocket-server.rb @@ -122,7 +122,7 @@ EventMachine.run { @clients.delete client_id end - Sessions.destory( client_id ) + Sessions.destory(client_id) } # manage messages @@ -145,128 +145,24 @@ EventMachine.run { # spool messages for new connects if data['spool'] - Sessions.spool_create(msg) + Sessions.spool_create(data) end - # get spool messages and send them to new client connection - if data['action'] == 'spool' - - # error handling - if data['timestamp'] - log 'notice', "request spool data > '#{Time.at(data['timestamp']).utc.iso8601}'", client_id - else - log 'notice', 'request spool with init data', client_id - end - - if @clients[client_id] && @clients[client_id][:session] && @clients[client_id][:session]['id'] - spool = Sessions.spool_list( data['timestamp'], @clients[client_id][:session]['id'] ) - spool.each { |item| - - # create new msg to push to client - if item[:type] == 'direct' - log 'notice', "send spool to (user_id=#{@clients[client_id][:session]['id']})", client_id - websocket_send(client_id, item[:message]) - else - log 'notice', 'send spool', client_id - websocket_send(client_id, item[:message]) - end - } - else - log 'error', "can't send spool, session not authenticated", client_id - end - - # send spool:sent event to client - log 'notice', 'send spool:sent event', client_id - message = { - event: 'spool:sent', - data: { - timestamp: Time.now.utc.to_i, - }, - } - websocket_send(client_id, message) - end - - # get session - if data['action'] == 'login' - - # get user_id - if data && data['session_id'] - ActiveRecord::Base.establish_connection - session = ActiveRecord::SessionStore::Session.find_by( session_id: data['session_id'] ) - ActiveRecord::Base.remove_connection - end - - if session && session.data && session.data['user_id'] - new_session_data = { 'id' => session.data['user_id'] } - else - new_session_data = {} - end - - @clients[client_id][:session] = new_session_data - - Sessions.create( client_id, new_session_data, { type: 'websocket' } ) - - # remember ping, send pong back - elsif data['action'] == 'ping' - message = { - action: 'pong', - } - websocket_send(client_id, message) - - # broadcast - elsif data['action'] == 'broadcast' - - # list all current clients - client_list = Sessions.list - client_list.each {|local_client_id, local_client| - if local_client_id != client_id - - # broadcast to recipient list - if data['recipient'] - if data['recipient'].class != Hash - log 'error', "recipient attribute isn't a hash '#{data['recipient'].inspect}'" - else - if !data['recipient'].key?('user_id') - log 'error', "need recipient.user_id attribute '#{data['recipient'].inspect}'" - else - if data['recipient']['user_id'].class != Array - log 'error', "recipient.user_id attribute isn't an array '#{data['recipient']['user_id'].inspect}'" - else - data['recipient']['user_id'].each { |user_id| - - next if local_client[:user]['id'].to_i != user_id.to_i - - log 'notice', "send broadcast from (#{client_id}) to (user_id=#{user_id})", local_client_id - if local_client[:meta][:type] == 'websocket' && @clients[ local_client_id ] - websocket_send(local_client_id, data) - else - Sessions.send(local_client_id, data) - end - } - end - end - end - - # broadcast every client - else - log 'notice', "send broadcast from (#{client_id})", local_client_id - if local_client[:meta][:type] == 'websocket' && @clients[ local_client_id ] - websocket_send(local_client_id, data) - else - Sessions.send(local_client_id, data) - end - end - else - log 'notice', 'do not send broadcast to it self', client_id - end - } - - elsif data['event'] - log 'notice', "execute event '#{data['event']}'", client_id - message = Sessions::Event.run(data['event'], data, @clients[client_id][:session], client_id) + if data['event'] + log 'debug', "execute event '#{data['event']}'", client_id + message = Sessions::Event.run( + event: data['event'], + payload: data, + session: @clients[client_id][:session], + client_id: client_id, + clients: @clients, + options: @options, + ) if message websocket_send(client_id, message) end + else + log 'error', "unknown message '#{data.inspect}'", client_id end } end @@ -375,7 +271,7 @@ EventMachine.run { } end - def log( level, data, client_id = '-' ) + def log(level, data, client_id = '-') if !@options[:v] return if level == 'debug' end