Various changes - added api system

This commit is contained in:
G. Gibson 2023-02-28 10:07:01 -08:00
parent e2ae775dd1
commit 0f9f967c77
8 changed files with 277 additions and 40 deletions

File diff suppressed because one or more lines are too long

View file

@ -835,18 +835,22 @@ accounting_service = ::GxG::Services::Service.new(:accounting)
# ### Define Public Command Interface:
accounting_service.on(:start, {:description => "Accounting Service Start", :usage => "{ :start => nil }"}) do
::GxG::SERVICES[:accounting].start
::GxG::SERVICES[:accounting].publish_api
end
accounting_service.on(:stop, {:description => "Accounting Service Stop", :usage => "{ :stop => nil }"}) do
::GxG::SERVICES[:accounting].stop
::GxG::SERVICES[:accounting].unpublish_api
end
accounting_service.on(:restart, {:description => "Accounting Service Restart", :usage => "{ :restart => nil }"}) do
::GxG::SERVICES[:accounting].restart
end
accounting_service.on(:pause, {:description => "Accounting Service Pause", :usage => "{ :pause => nil }"}) do
::GxG::SERVICES[:accounting].pause
::GxG::SERVICES[:accounting].unpublish_api
end
accounting_service.on(:resume, {:description => "Accounting Service Resume", :usage => "{ :resume => nil }"}) do
::GxG::SERVICES[:accounting].resume
::GxG::SERVICES[:accounting].publish_api
end
# ### Define Internal Service Control Events:
accounting_service.on(:at_start, {:description => "Accounting Startup", :usage => "{ :at_start => (service-object) }"}) do |service, credential|

View file

@ -389,6 +389,17 @@ module GxG
def fs_root()
::File.expand_path("#{::GxG::SYSTEM.gxg_root()}/#{self.vfs_root()}")
end
# ### API Support
def publish_api()
if ::GxG::Services::service_available?(:www)
::GxG::SERVICES[:www].call_event({:publish_api => {:service => self, :path => "/#{@provides.to_s}"}}, ::GxG::DB[:administrator])
end
end
def unpublish_api()
if ::GxG::Services::service_available?(:www)
::GxG::SERVICES[:www].call_event({:unpublish_api => {:path => "/#{@provides.to_s}"}}, ::GxG::DB[:administrator])
end
end
# ### Service Management
def provides()
@provides

View file

@ -1079,18 +1079,22 @@ exchange_service.require_service(:accounting)
# ### Define Public Command Interface:
exchange_service.on(:start, {:description => "Exchange Service Start", :usage => "{ :start => nil }"}) do
::GxG::SERVICES[:exchange].start
::GxG::SERVICES[:exchange].publish_api
end
exchange_service.on(:stop, {:description => "Exchange Service Stop", :usage => "{ :stop => nil }"}) do
::GxG::SERVICES[:exchange].stop
::GxG::SERVICES[:exchange].unpublish_api
end
exchange_service.on(:restart, {:description => "Exchange Service Restart", :usage => "{ :restart => nil }"}) do
::GxG::SERVICES[:exchange].restart
end
exchange_service.on(:pause, {:description => "Exchange Service Pause", :usage => "{ :pause => nil }"}) do
::GxG::SERVICES[:exchange].pause
::GxG::SERVICES[:exchange].unpublish_api
end
exchange_service.on(:resume, {:description => "Exchange Service Resume", :usage => "{ :resume => nil }"}) do
::GxG::SERVICES[:exchange].resume
::GxG::SERVICES[:exchange].publish_api
end
# ### Define Internal Service Control Events:
exchange_service.on(:at_start, {:description => "Exchange Startup", :usage => "{ :at_start => (service-object) }"}) do |service|

View file

@ -289,6 +289,18 @@ www_service.on(:at_stop, {:description => "WWW Stop", :usage => "{ :at_stop => (
#
{:result => true}
end
www_service.on(:publish_api, {:description => "Publish Service API", :usage => "{ :publish_api => {:service => (service-object), :path => '/path'} }", :public => false}) do |data, credential|
#
::GxGwww::publish_api(data[:service], data[:path])
#
{:result => true}
end
www_service.on(:unpublish_api, {:description => "Unpublish Service API", :usage => "{ :unpublish_api => {:path => '/path'} }", :public => false}) do |data, credential|
#
::GxGwww::unpublish_api(data[:path])
#
{:result => true}
end
# ### Service Installation
unless ::GxG::Services::service_available?(:www)
# Set Configuration Defaults

View file

@ -16,7 +16,194 @@ module GxGwww
end
the_value
end
# ### API toolbox
API = {}
API_SESSIONS = {}
API_THREAD_SAFETY = ::Mutex.new
def self.publish_api(the_service=nil, the_path=nil)
if the_service && the_path
the_controller = ::GxGwww::APIController.new(the_service, the_path)
if the_controller
::GxGwww::API_THREAD_SAFETY.synchronize {
::GxGwww::API[(the_path.to_sym)] = the_controller
}
true
else
false
end
else
false
end
end
#
def self.unpublish_api(the_path="/unknown")
if the_path
::GxGwww::API_THREAD_SAFETY.synchronize {
::GxGwww::API.delete(the_path.to_sym)
}
end
true
end
#
def self.api_login(session_id=nil, credential=:"00000000-0000-4000-0000-000000000000")
if session_id
::GxGwww::API_THREAD_SAFETY.synchronize {
::GxGwww::API_SESSIONS[(session_id.to_sym)] = credential
}
true
else
false
end
end
#
def self.api_logout(session_id=nil)
if session_id
::GxGwww::API_THREAD_SAFETY.synchronize {
::GxGwww::API_SESSIONS.delete(session_id.to_sym)
}
true
else
false
end
end
#
def self.api_credential(session_id=nil)
result = :"00000000-0000-4000-0000-000000000000"
if session_id
::GxGwww::API_THREAD_SAFETY.synchronize {
result = (::GxGwww::API_SESSIONS[(session_id.to_sym)] || :"00000000-0000-4000-0000-000000000000")
}
end
result
end
#
class APIController
def initialize(the_service=nil, the_path="/unknown")
@service = the_service
@path = the_path
end
def service
@service
end
def path
@path
end
#
def process(the_method=nil, the_request=nil, the_session=nil)
credential = ::GxGwww::api_credential(the_session['session_id'])
# Pre-processing
response = nil
operation = nil
begin
if the_method
case the_method
when :get
if the_request["details"].to_s == "eyJpbnRyb2R1Y3Rpb24iOnRydWV9"
response = [200, {"content-type" => "application/text"}, ({:csrf => the_session["csrf"]}.gxg_export.to_json.encode64)]
else
operation = (::JSON::parse(URI.unescape(URI.unescape(the_request.params["details"].to_s.decode64.decrypt(the_session["csrf"]))), {:symbolize_names => true}))
unless operation.is_a?(::Hash)
response = [500, {"content-type" => "application/text"}, "Malformed query."]
end
end
when :put
if the_request.body.respond_to?(:rewind) && the_request.body.respond_to?(:read)
the_request.body.rewind
operation = (::JSON::parse(URI.unescape(URI.unescape(the_request.body.read().to_s.decode64.decrypt(the_session["csrf"]))), {:symbolize_names => true}))
unless operation.is_a?(::Hash)
response = [500, {"content-type" => "application/text"}, "Malformed payload."]
end
else
log_warn("Abnormal PUT Body #{the_request.body.inspect}")
response = [500, {"content-type" => "application/text"}, "Malformed payload."]
end
else
response = [500, {"content-type" => "application/text"}, "Provide a get or put method."]
end
else
response = [500, {"content-type" => "application/text"}, "No http method provided."]
end
rescue Exception => the_error
response = [500, {"content-type" => "application/text"}, the_error.to_s]
end
# Processing
unless response
response = [403, {"content-type" => "application/text"}, "You don't have permissions to do that."]
# Operation formatting
if operation.is_a?(::Hash)
operation = ::Hash::gxg_import(operation)
operation.search do |the_value, the_selector, the_container|
if the_value.is_a?(::Hash)
if the_value[:formats].is_a?(::Hash) && the_value[:records].is_a?(::Array)
imported_list = ::GxG::Database::Database::detached_package_import(the_value)
if imported_list.size > 1
the_container[(the_selector)] = imported_list
else
the_container[(the_selector)] = imported_list[0]
end
end
end
end
end
#
if operation
case operation.keys[0].to_sym
when :upgrade_credential
new_credential = ::GxG::DB[:authority].user_credential(operation[:upgrade_credential][:username].to_s, operation[:upgrade_credential][:password].to_s)
if new_credential
response = [200, {"content-type" => "application/text"}, {:result => ::GxGwww::api_login(the_session['session_id'], new_credential), :error => nil}.gxg_export.to_json.encrypt(the_session["csrf"]).encode64]
else
response = [403, {"content-type" => "application/text"}, {:result => nil, :error => "Bad username or password."}.gxg_export.to_json.encrypt(the_session["csrf"]).encode64]
end
when :downgrade_credential
response = [200, {"content-type" => "application/text"}, {:result => ::GxGwww::api_logout(the_session['session_id']), :error => nil}.gxg_export.to_json.encrypt(the_session["csrf"]).encode64]
when :interface
response = [200, {"content-type" => "application/text"}, @service.interface(credential).gxg_export.to_json.encrypt(the_session["csrf"]).encode64]
else
begin
call_response = @service.call_event(operation, , credential)
if call_response.is_a?(::Hash)
call_response.search do |the_value, the_selector, the_container|
if the_value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::DetachedHash)
the_container[(the_selector)] = the_value.export_package()
end
end
end
response = [200, {"content-type" => "application/text"}, call_response.gxg_export.to_json.encrypt(the_session["csrf"]).encode64]
rescue Exception => the_error
response = [500, {"content-type" => "application/text"}, the_error.to_s]
end
end
# the_manifest = GxG::SERVICES[:www][:manifests][(the_session['session_id'])]
end
end
#
response
# API ????
# @service.call_event(operation_envelope=nil, credential)
# GxG::SERVICES[:www][:manifests][(the_session['session_id'])]
# unless the_manifest
# the_manifest = ::GxGwww::Manifest.new({:credential => :'00000000-0000-4000-0000-000000000000'})
# # manifest format: {:credential => :'00000000-0000-4000-0000-000000000000', :displays => {}, :timers => {}, :connectors => {}, :status => :none}
# GxG::SERVICES[:www][:manifests][(the_session['session_id'])] = the_manifest
# end
# operations = []
# case the_method
# when :get
# if the_request.params["details"].to_s.size > 0
# operations = (::JSON::parse(URI.unescape(URI.unescape(the_request.params["details"].to_s.decode64)), {:symbolize_names => true}))
# end
# when :put
# if the_request.body.respond_to?(:rewind) && the_request.body.respond_to?(:read)
# the_request.body.rewind
# operations = (::JSON::parse(URI.unescape(URI.unescape(the_request.body.read())), {:symbolize_names => true}))
# else
# log_warn("Abnormal PUT Body #{the_request.body.inspect}")
# end
# end
end
end
# ### Application Support
module Applications
MENU = []
def self.refresh_application_menu()
@ -956,30 +1143,47 @@ module GxGwww
# puts "Got Request Path: #{request_path}"
# ????
case the_method
when :get
if ["", "/", "/index"].include?(request_path) || ::GxG::SERVICES[:core][:resources].exist?("/Public/www/content/pages#{request_path}")
# Send bootstrap html page and wait for xhr requests
# content = GxGwww::CACHE.fetch("/page.html",the_session[:credential])
response[0] = 200
# response[1]["content-type"] = content[:content_type]
response[1]["content-type"] = "text/html"
# response[2] = content[:data]
response[2] = self.default_page_content()
else
# Asset :get request
if ::GxG::SERVICES[:core][:resources].exist?(("/Public/www" << request_path))
content = GxGwww::CACHE.fetch(("/Public/www" << request_path), (the_session[:credential] || :"00000000-0000-4000-0000-000000000000"), true)
if content
response[0] = 200
response[1]["content-type"] = content[:content_type]
response[1]["X-Content-Type-Options"] = "nosniff"
response[2] = content[:data]
when :get, :put
if request_path.to_s.downcase[(0..3)].include?("/api")
# Filter for API controller match
the_controller = nil
::GxGwww::API_THREAD_SAFETY.synchronize {
::GxGwww::API.each_pair do |the_path, the_handler|
if request_path.to_s[(4..-1)] == the_path.to_s
the_controller = the_handler
break
end
end
}
if the_controller
response = the_controller.process(the_method, the_request, the_session)
else
log_warn "Failed to fetch: #{request_path}"
response = [404, {"content-type" => "application/text"}, "Endpoint Not Found"]
end
else
if ["", "/", "/index"].include?(request_path) || ::GxG::SERVICES[:core][:resources].exist?("/Public/www/content/pages#{request_path}")
# Send bootstrap html page and wait for xhr requests
# content = GxGwww::CACHE.fetch("/page.html",the_session[:credential])
response[0] = 200
# response[1]["content-type"] = content[:content_type]
response[1]["content-type"] = "text/html"
# response[2] = content[:data]
response[2] = self.default_page_content()
else
# Asset :get request
if ::GxG::SERVICES[:core][:resources].exist?(("/Public/www" << request_path))
content = GxGwww::CACHE.fetch(("/Public/www" << request_path), (the_session[:credential] || :"00000000-0000-4000-0000-000000000000"), true)
if content
response[0] = 200
response[1]["content-type"] = content[:content_type]
response[1]["X-Content-Type-Options"] = "nosniff"
response[2] = content[:data]
else
log_warn "Failed to fetch: #{request_path}"
end
end
end
end
end
when :put
when :post
# if the_request.path.include?("/display/")
# if the_request.params["operation"] == "close"

View file

@ -326,7 +326,8 @@ module GxGwww
:".tr"=>{:display=>"table-row", :margin=>0, :padding=>0},
:".td"=>{:display=>"table-cell", :margin=>0, :padding=>0},
:".default-background-color" => {:"background-color" => "#f2f2f2"},
:".default-highlite-color" => {:"background-color" => "#87acd5"}
:".default-highlite-color" => {:"background-color" => "#87acd5"},
:"menu-item-disabled" => {:color => "#655e7a"}
}
#
if GxG::VFS.exist?("/Public/www/content/themes/setup")
@ -389,7 +390,8 @@ module GxGwww
:".tr"=>{:display=>"table-row", :margin=>0, :padding=>0},
:".td"=>{:display=>"table-cell", :margin=>0, :padding=>0},
:".default-background-color" => {:"background-color" => "#f2f2f2"},
:".default-highlite-color" => {:"background-color" => "#87acd5"}
:".default-highlite-color" => {:"background-color" => "#87acd5"},
:"menu-item-disabled" => {:color => "#655e7a"}
}
#
if GxG::VFS.exist?("/Public/www/content/themes/default")

Binary file not shown.