heimdall stuff
This commit is contained in:
parent
2dddb0dc89
commit
ff8e103274
|
@ -0,0 +1,2 @@
|
||||||
|
/.env.rb
|
||||||
|
/hyde/
|
121
proto.rb
121
proto.rb
|
@ -1,6 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'weakref'
|
||||||
|
|
||||||
module Heimdall
|
module Heimdall
|
||||||
|
# Protocol error class
|
||||||
|
class ProtoError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
# Container for any uniquely identifiable object
|
# Container for any uniquely identifiable object
|
||||||
# @abstract
|
# @abstract
|
||||||
class UUIDObject
|
class UUIDObject
|
||||||
|
@ -18,43 +24,92 @@ module Heimdall
|
||||||
|
|
||||||
# Create a new UUIDObject with unique UUID
|
# Create a new UUIDObject with unique UUID
|
||||||
# @return [self]
|
# @return [self]
|
||||||
def new
|
def new(*args, **params)
|
||||||
object = super
|
object = super(*args, **params)
|
||||||
@uuids[object.uuid] = object
|
@uuids[object.uuid] = object
|
||||||
@last = object.uuid
|
@last = object.uuid
|
||||||
@foreign_ids[object.foreign_id] = object if object.foreign_id
|
add_foreign(object.foreign_ids, object)
|
||||||
object
|
object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Delete an object found by UUID
|
||||||
|
# @param uuid [Integer]
|
||||||
|
# @return [void]
|
||||||
|
def delete(uuid)
|
||||||
|
obj = @uuids[uuid]
|
||||||
|
return false unless obj
|
||||||
|
|
||||||
|
@uuids.delete(obj.uuid)
|
||||||
|
obj.foreign_ids.each { |x| @foreign_ids.delete(x) }
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete an object found by foreign id
|
||||||
|
# @param foreign_id [String]
|
||||||
|
# @return [void]
|
||||||
|
def delete_foreign(foreign_id)
|
||||||
|
obj = @foreign_ids[foreign_id]
|
||||||
|
return false unless obj
|
||||||
|
|
||||||
|
@uuids.delete(obj.uuid)
|
||||||
|
obj.foreign_ids.each { |x| @foreign_ids.delete(x) }
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
# Get object by UUID
|
# Get object by UUID
|
||||||
# @param uuid
|
# @param uuid [Integer]
|
||||||
# @return [self, nil]
|
# @return [self, nil]
|
||||||
def get(uuid)
|
def get(uuid)
|
||||||
@uuids[uuid] if @uuids[uuid].is_a? self
|
@uuids[uuid] if @uuids[uuid].is_a? self
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get object by foreign id
|
# Get object by foreign id
|
||||||
# @param foreign_id
|
# @param foreign_id [String]
|
||||||
# @return [self, nil]
|
# @return [self, nil]
|
||||||
def get_foreign(foreign_id)
|
def get_foreign(foreign_id)
|
||||||
@foreign_ids[foreign_id] if @foreign_ids[foreign_id].is_a? self
|
@foreign_ids[foreign_id] if @foreign_ids[foreign_id].is_a? self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add foreign ids to the list of known foreign ids
|
||||||
|
# @param foreign [Array<String>]
|
||||||
|
# @return [void]
|
||||||
|
def add_foreign(foreign, obj)
|
||||||
|
foreign.each do |f_id|
|
||||||
|
@foreign_ids[check_foreign(f_id)] = obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check a foreign id for valid syntax
|
||||||
|
# @param foreign [String]
|
||||||
|
# @return [String]
|
||||||
|
def check_foreign(foreign)
|
||||||
|
if foreign_ids.include?(foreign)
|
||||||
|
raise ProtoError, 'foreign id already exists'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless foreign.match?(/^([\w_]+):.*$/)
|
||||||
|
raise ProtoError, 'invalid foreign id syntax'
|
||||||
|
end
|
||||||
|
|
||||||
|
foreign
|
||||||
|
end
|
||||||
|
|
||||||
attr_accessor :uuids, :foreign_ids, :last
|
attr_accessor :uuids, :foreign_ids, :last
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@uuid = __gen_uuid
|
@uuid = __gen_uuid
|
||||||
@foreign_ids = []
|
@foreign_ids = []
|
||||||
|
@foreign_ids.append(@id) if @id
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :uuid, :foreign_id
|
attr_reader :uuid, :foreign_ids
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def __gen_uuid
|
def __gen_uuid
|
||||||
newuuid = (Time.now.to_f * 1000).to_i * 10000
|
newuuid = (Time.now.to_f * 1000).to_i * 10000
|
||||||
if (self.class.last / 1000) == (newuuid / 1000)
|
if self.class.last && (self.class.last / 1000) == (newuuid / 1000)
|
||||||
newuuid += (self.class.last % 1000) + 1
|
newuuid += (self.class.last % 1000) + 1
|
||||||
end
|
end
|
||||||
newuuid
|
newuuid
|
||||||
|
@ -103,13 +158,14 @@ module Heimdall
|
||||||
# Message struct
|
# Message struct
|
||||||
class Message < UUIDObject
|
class Message < UUIDObject
|
||||||
def initialize(datahash, **params)
|
def initialize(datahash, **params)
|
||||||
super(**params)
|
@id = datahash["id"]
|
||||||
|
|
||||||
@from = UUIDObject.get(datahash["from"])
|
@from = UUIDObject.get(datahash["from"])
|
||||||
@to = UUIDObject.get(datahash["to"])
|
@to = UUIDObject.get(datahash["to"])
|
||||||
@content = datahash["content"]
|
@content = datahash["content"]
|
||||||
# @reply_to = datahash["reply_to"] # TODO: make this make sense
|
# @reply_to = datahash["reply_to"] # TODO: make this make sense
|
||||||
@attachments = datahash["attachments"]
|
@attachments = datahash["attachments"]
|
||||||
|
|
||||||
|
super(**params)
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :from, :to, :content, :reply_to, :attachments
|
attr_reader :from, :to, :content, :reply_to, :attachments
|
||||||
|
@ -117,8 +173,51 @@ module Heimdall
|
||||||
|
|
||||||
# User struct
|
# User struct
|
||||||
class User < UUIDObject
|
class User < UUIDObject
|
||||||
def initialize(datahash, **params)
|
DEFAULT_AVATAR = ""
|
||||||
super(**params)
|
|
||||||
|
|
||||||
|
def initialize(datahash, **params)
|
||||||
@id = datahash["id"]
|
@id = datahash["id"]
|
||||||
|
@username = datahash["username"]
|
||||||
|
@nickname = datahash["nickname"]
|
||||||
|
@avatar = datahash["avatar"] || self.class::DEFAULT_AVATAR
|
||||||
|
|
||||||
|
super(**params)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert user data to a JSON struct
|
||||||
|
# @return [String] JSON struct
|
||||||
|
def to_struct
|
||||||
|
JSON.dump({
|
||||||
|
"id" => @id,
|
||||||
|
"username" => @username,
|
||||||
|
"nickname" => @nickname,
|
||||||
|
"avatar" => @avatar
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Channel struct
|
||||||
|
class Channel < UUIDObject
|
||||||
|
DEFAULT_AVATAR = ""
|
||||||
|
|
||||||
|
def initialize(datahash, **params)
|
||||||
|
@id = datahash["id"]
|
||||||
|
@name = datahash["name"] or @id
|
||||||
|
@avatar = datahash["avatar"] || self.class::DEFAULT_AVATAR
|
||||||
|
|
||||||
|
super(**params)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert channel data to a JSON struct
|
||||||
|
# @return [String] JSON struct
|
||||||
|
def to_struct
|
||||||
|
JSON.dump({
|
||||||
|
"id" => @id,
|
||||||
|
"name" => @name,
|
||||||
|
"avatar" => @avatar
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
VERSION = "1.0"
|
||||||
end
|
end
|
||||||
|
|
96
server.ru
96
server.ru
|
@ -8,6 +8,8 @@ require 'json'
|
||||||
# Primary server class
|
# Primary server class
|
||||||
class HeimdallServer < Landline::App
|
class HeimdallServer < Landline::App
|
||||||
before do
|
before do
|
||||||
|
header "content-type", "application/json"
|
||||||
|
|
||||||
# Match data type against a list of datatypes
|
# Match data type against a list of datatypes
|
||||||
# @param obj [Object]
|
# @param obj [Object]
|
||||||
# @param type [Array, Class]
|
# @param type [Array, Class]
|
||||||
|
@ -23,41 +25,103 @@ class HeimdallServer < Landline::App
|
||||||
# @param args [Hash] hash of key - type pairs to check JSON data against
|
# @param args [Hash] hash of key - type pairs to check JSON data against
|
||||||
def validate_json(**args)
|
def validate_json(**args)
|
||||||
die(400, backtrace: ['JSON body expected']) unless json?
|
die(400, backtrace: ['JSON body expected']) unless json?
|
||||||
data = begin
|
@data = begin
|
||||||
JSON.parse(request.body)
|
JSON.parse(request.body)
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
die(400, backtrace: ['JSON body is invalid'])
|
die(400, backtrace: ['JSON body is invalid'])
|
||||||
end
|
end
|
||||||
args.each do |k, v|
|
args.each do |k, v|
|
||||||
unless data.include?(k) and match_type(data[k], v)
|
unless match_type(@data[k], v)
|
||||||
die(400, backtrace: ["Key #{k} is missing"])
|
die(400, backtrace: ["Key #{k} of type #{v} is missing"])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
filter do
|
filter do
|
||||||
request.cookies["token"] == BOT_TOKEN
|
defined? ::BOT_TOKEN ? request.cookies["token"] == ::BOT_TOKEN : true
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/version" do
|
||||||
|
JSON.dump({
|
||||||
|
"version" => Heimdall::VERSION
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline do |request, &output|
|
||||||
|
output.call(request)
|
||||||
|
rescue Heimdall::ProtoError => e
|
||||||
|
throw :finish, [400,
|
||||||
|
{ "content-type": "application/json" },
|
||||||
|
JSON.dump({
|
||||||
|
"error" => e.message,
|
||||||
|
"code" => 400
|
||||||
|
})]
|
||||||
|
end
|
||||||
|
|
||||||
|
read_delete = proc do |cls|
|
||||||
|
get "/foreign/*" do |id|
|
||||||
|
user = cls.get_foreign(id)
|
||||||
|
user&.to_struct or die(400, backtrace: ["#{cls} not found"])
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/uuid/*" do |id|
|
||||||
|
user = cls.get(id.to_i)
|
||||||
|
user&.to_struct or die(400, backtrace: ["#{cls} not found"])
|
||||||
|
end
|
||||||
|
|
||||||
|
delete "/foreign/*" do |id|
|
||||||
|
if cls.delete_foreign(id)
|
||||||
|
JSON.dump({ "code" => 200 })
|
||||||
|
else
|
||||||
|
die(400, backtrace: ["#{cls} not found"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
delete "/uuid/*" do |id|
|
||||||
|
if cls.delete(id.to_i)
|
||||||
|
JSON.dump({ "code" => 200 })
|
||||||
|
else
|
||||||
|
die(400, backtrace: ["#{cls} not found"])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
path "/user" do
|
path "/user" do
|
||||||
post "/register" do
|
post "/register" do
|
||||||
validate_json("id" => Integer,
|
validate_json("id" => String,
|
||||||
"username" => String,
|
"username" => String,
|
||||||
"nickname" => [String, NilClass])
|
"nickname" => [String, NilClass],
|
||||||
|
"avatar" => [String, NilClass])
|
||||||
|
new_user = Heimdall::User.new(@data)
|
||||||
|
JSON.dump({ "uuid": new_user.uuid })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
instance_exec(Heimdall::User, &read_delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
handle do |status, backtrace: nil|
|
path "/channel" do
|
||||||
page = JSON.dump({
|
post "/register" do
|
||||||
"error" => backtrace.join("\n"),
|
validate_json("id" => String,
|
||||||
"code" => status
|
"name" => String,
|
||||||
})
|
"avatar" => [String, NilClass])
|
||||||
[{
|
new_channel = Heimdall::Channel.new(@data)
|
||||||
"content-length": page.bytesize,
|
JSON.dump({ "uuid": new_channel.uuid })
|
||||||
"content-type": "application/json"
|
end
|
||||||
}, page]
|
|
||||||
|
instance_exec(Heimdall::Channel, &read_delete)
|
||||||
|
end
|
||||||
|
|
||||||
|
handle do |status, backtrace: nil, **_|
|
||||||
|
backtrace ||= [Landline::Util::HTTP_STATUS[status]]
|
||||||
|
[status,
|
||||||
|
{
|
||||||
|
"content-type": "application/json"
|
||||||
|
},
|
||||||
|
JSON.dump({
|
||||||
|
"error" => (backtrace || []).join("\n"),
|
||||||
|
"code" => status
|
||||||
|
})]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue