# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/ class Sessions::Store::Redis SESSIONS_KEY = 'sessions'.freeze MESSAGES_KEY = 'messages'.freeze SPOOL_KEY = 'spool'.freeze NODES_KEY = 'nodes'.freeze def initialize # Only load redis if it is really used. require 'redis' require 'hiredis' @redis = Redis.new(driver: :hiredis) end def create(client_id, data) @redis.set client_session_key(client_id), data @redis.sadd SESSIONS_KEY, client_id end def sessions @redis.smembers SESSIONS_KEY end def session_exists?(client_id) @redis.sismember SESSIONS_KEY, client_id end def destroy(client_id) @redis.srem SESSIONS_KEY, client_id @redis.del client_session_key(client_id) @redis.del client_messages_key(client_id) end def set(client_id, data) @redis.set client_session_key(client_id), data.to_json end def get(client_id) data = nil # if only session is missing, then it's an error behavior session = @redis.get client_session_key(client_id) if !session destroy(client_id) Sessions.log('error', "missing session value for '#{client_id}', removing session.") return end data_json = JSON.parse(session) if data_json data = Sessions.symbolize_keys(data_json) data[:user] = data_json['user'] # for compat. reasons end data end def send_data(client_id, data) @redis.rpush(client_messages_key(client_id), data.to_json).positive? end def queue(client_id) data = [] while (item = @redis.lpop(client_messages_key(client_id))) data.push JSON.parse(item) end data end def cleanup clear_spool clear_sessions clear_messages true end def add_to_spool(data) @redis.rpush SPOOL_KEY, data.to_json end def each_spool() @redis.lrange(SPOOL_KEY, 0, -1).each do |message| yield message, nil end end def remove_from_spool(message, _entry) @redis.lrem SPOOL_KEY, 1, message end def clear_spool @redis.del SPOOL_KEY end def clear_sessions @redis.keys("#{SESSIONS_KEY}/*").each do |key| @redis.del key end @redis.del SESSIONS_KEY end def clear_messages @redis.keys("#{MESSAGES_KEY}/*").each do |key| @redis.del key end end ### Node-specific methods ### def clear_nodes @redis.keys("#{NODES_KEY}/*").each do |key| @redis.del key end @redis.del NODES_KEY end def nodes nodes = [] @redis.smembers(NODES_KEY).each do |node_id| content = @redis.get(node_key(node_id)) if content data = JSON.parse(content) nodes.push data end end nodes end def add_node(node_id, data) @redis.set node_key(node_id), data.to_json @redis.sadd NODES_KEY, node_id end def each_node_session(&block) @redis.smembers(NODES_KEY).each do |node_id| each_session_by_node(node_id, &block) end end def create_node_session(node_id, client_id, data) @redis.set node_client_session_key(node_id, client_id), data.to_json @redis.sadd node_sessions_key(node_id), client_id end def each_session_by_node(node_id) @redis.smembers(node_sessions_key(node_id)).each do |client_id| content = @redis.get(node_client_session_key(node_id, client_id)) if content data = JSON.parse(content) yield data end end end protected def client_session_key(client_id) "#{SESSIONS_KEY}/#{client_id}" end def client_messages_key(client_id) "#{MESSAGES_KEY}/#{client_id}" end def node_key(node_id) "#{NODES_KEY}/#{node_id}" end def node_sessions_key(node_id) "#{node_key(node_id)}/sessions" end def node_client_session_key(node_id, client_id) "#{node_sessions_key(node_id)}/#{client_id}" end end