require_relative "proto" require_relative "hyde/hyde" require 'webrick/websocket' require "json" Users = Heimdall::UserCache.new Rooms = Heimdall::RoomCache.new SocketsMap = {} NotifyList = {} def _require_keys(dict,key_dict) raise KeyError, "not a dict" unless dict.kind_of? Hash key_dict.each_pair { |k,v| unless (dict.has_key? k.to_s) and (dict[k.to_s].kind_of? v) then raise KeyError, "key #{k} of type #{v} required" end } end def _send_json(res,data,code: 200) res.body = JSON::fast_generate(data) res['Content-Type'] = "application/json" res.status = code end def _throw_error(res,error) _send_json(res,{ error: "#{error}" },code: 400) end def _parse_json(body,key_dict) data = JSON::Parser.new(body).parse _require_keys(data,key_dict) return data end server = Hyde::Server.new Port: 8000 do path "user" do path "find" do index ["by-name"] get "by-name" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { username: String }) _send_json(res, { "results": Users.search_by_name(req.query['username']).map { |x| x[1].to_card } }) rescue KeyError => keyerror _throw_error(res,keyerror) end end get "by-protoid" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { protocol_id: String }) _send_json(res, { "results": Users.search_by_protoid(req.query['protocol_id']).map { |x| x[1].to_card; x } }) rescue KeyError => keyerror _throw_error(res,keyerror) end end end post "sync" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { data: Array }) Users.sync(data["data"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "bulk-delete" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { users: Array }) Users.bulk_delete(data["users"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end get "exists" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { protocol_id: String }) _send_json(res,{ exists: (Users.get(req.query["protocol_id"]) != nil) }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue Heimdall::ProtocolError => protoerr _send_json(res, { exists: false }) end end post "new" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body,{ username: String, protocol_id: String }) new_user = Heimdall::User.new(data) Users.add(new_user) _send_json(res,{ status: true }) NotifyList[new_user.UID] = [] rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) rescue KeyError => keyerror _throw_error(res,keyerror) rescue ProtocolError => protoerr _throw_error(res,protoerr) end end post "modify" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { data: Hash, protocol_id: String }) user = Users.get(data["protocol_id"]) user.modify(data["data"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue ProtocolError => protoerr _throw_error(res,protoerr) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "send" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { content: String, from: String, to: String }) new_message = Heimdall::Message.new(data) user = Users.get(new_message.to) if NotifyList[user.UID].length != 0 then NotifyList[user.UID].each { |sockid| sock = SocketsMap[sockid] msg = new_message.to_struct msg["user"] = Users.get(msg["from"]).to_card sock.puts(JSON::fast_generate(msg)) } user.channel.send_silent(new_message) else user.channel.send(new_message) end _send_json(res,{ status: true }) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) rescue KeyError => keyerror _throw_error(res,keyerror) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) end end get "get" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query,{ n: Integer, protocol_id: String }) number = req.query[:n] id = req.query["protocol_id"] user = Users.get(id) messages = user.channel.get(number) _send_json(res, { messages: messages.map { |x| x = x.to_struct x["user"] = Users.get(x["from"]).to_card x } }) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) end end get "read" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query,{ protocol_id: String }) id = req.query["protocol_id"] user = Users.get(id) messages = user.channel.read _send_json(res, { messages: messages.map { |x| x = x.to_struct x["user"] = Users.get(x["from"]).to_card x } }) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) end end post "listen" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body, { websocket: String, protocol_id: String }) uid = Users.get(data["protocol_id"]).UID raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] NotifyList[uid].append data["websocket"] _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "unlisten" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body, { websocket: String, protocol_id: String }) uid = Users.get(data["protocol_id"]).UID raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] NotfiyList[uid].delete data["websocket"] _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "delete" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body,{ protocol_id: String }) id = data["protocol_id"] _send_json(res, { status: true }) user = Users.get(id) NotifyList.delete(user.UID) Users.delete(id) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end end path "room" do path "find" do index ["by-name"] get "by-name" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { username: String }) _send_json(res, { "results": Rooms.search_by_name(req.query['username']).map { |x| x[1].to_card } }) rescue KeyError => keyerror _throw_error(res,keyerror) end end get "by-protoid" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { protocol_id: String }) _send_json(res, { "results": Rooms.search_by_protoid(req.query['protocol_id']).map { |x| x[1].to_card; x } }) rescue KeyError => keyerror _throw_error(res,keyerror) end end end post "new" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { name: String, protocol_id: String }) new_room = Heimdall::Room.new(data) Rooms.add(new_room) _send_json(res, { status: true }) NotifyList[new_room.UID] = [] rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "send" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { content: String, from: String, to: String }) new_message = Heimdall::Message.new(data) room = Rooms.get(new_message.to) if NotifyList[room.UID].length != 0 then NotifyList[room.UID].each { |sockid| sock = SocketsMap[sockid] msg = new_message.to_struct msg["user"] = Users.get(msg["from"]).to_card msg["room"] = room.to_card sock.puts(JSON::fast_generate(msg)) } room.channel.send_silent(new_message) else room.channel.send(new_message) end _send_json(res,{ status: true }) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) rescue KeyError => keyerror _throw_error(res,keyerror) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) end end get "get" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query,{ n: Integer, protocol_id: String }) number = req.query[:n] id = req.query["protocol_id"] room = Rooms.get(id) messages = room.channel.get(number) _send_json(res, { messages: messages.map { |x| x = x.to_struct x["user"] = Users.get(x["from"]).to_card x["room"] = room.to_card x } }) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) end end post "listen" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body, { websocket: String, protocol_id: String }) uid = Rooms.get(data["protocol_id"]).UID raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] NotifyList[uid].append data["websocket"] _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "unlisten" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body, { websocket: String, protocol_id: String }) uid = Rooms.get(data["protocol_id"]).UID raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] NotfiyList[uid].delete data["websocket"] _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "modify" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { data: Hash, protocol_id: String }) room = Rooms.get(data["protocol_id"]) room.modify(data["data"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue ProtocolError => protoerr _throw_error(res,protoerr) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "delete" do |ctx| req,res = ctx.request, ctx.response begin data = _parse_json(req.body,{ protocol_id: String }) id = data["protocol_id"] _send_json(res, { status: true }) room = Rooms.get(id) NotifyList.delete(room.UID) Rooms.delete(id) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "sync" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { data: Array }) Rooms.sync(data["data"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end post "bulk-delete" do |ctx| req,res = ctx.request,ctx.response begin data = _parse_json(req.body, { users: Array }) Rooms.bulk_delete(data["users"]) _send_json(res, { status: true }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue JSON::ParserError => jsonerror _throw_error(res,jsonerror) end end get "exists" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { protocol_id: String }) _send_json(res,{ exists: (Rooms.get(req.query["protocol_id"]) != nil) }) rescue KeyError => keyerror _throw_error(res,keyerror) rescue Heimdall::ProtocolError => protoerr _send_json(res, { exists: false }) end end end get "version" do |ctx| ctx.response.body = "{\"version\":\"#{Heimdall::VERSION}\"}" ctx.response['Content-Type'] = "application/json" end end at_exit do server.shutdown end class WebsocketUID < Heimdall::UID def initialize @UID_prefix = "websocket" super end end class EventServlet < WEBrick::Websocket::Servlet def socket_open(sock) @UID = WebsocketUID.new @connected_listeners = [] SocketsMap[@UID.UID] = sock sock.puts(JSON::fast_generate({ websocket: @UID.UID.to_s })) end def socket_close(sock) SocketsMap.delete @UID.UID NotifyList.each do |k,v| if v.include? @UID.UID v.delete @UID.UID end end end def socket_text(sock,text) # do nothing end end Thread.new do websocket_server = WEBrick::Websocket::HTTPServer.new Port:8001 websocket_server.mount "/", EventServlet websocket_server.start at_exit do websocket_server.shutdown end end server.start