heimdall stuff

This commit is contained in:
Yessiest 2024-06-19 16:40:29 +04:00
parent 2dddb0dc89
commit ff8e103274
3 changed files with 192 additions and 27 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.env.rb
/hyde/

121
proto.rb
View File

@ -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

View File

@ -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