module W3DHubLauncher class W3DHubApi API_TIMEOUT = 30 # seconds API_CONNECT_TIMEOUT = 10 # seconds PRIMARY_W3DHUB_API_ENDPOINT = "https://secure.w3dhub.com".freeze ALTERNATIVE_W3DHUB_API_ENDPOINT = "https://backend.w3d.cyberarm.dev".freeze attr_reader :applications attr_writer :access_token def initialize @access_token = nil @applications = [] @http_clients = {} end def headers(form_encoded: false, range: nil) array = [ ["user-agent", W3DHubLauncher::USER_AGENT], ["accept", "application/json"], ] array << ["content-type", "application/x-www-form-urlencoded"] if form_encoded array << ["authorization", "Bearer #{@access_token}"] if @access_token array << ["range", "bytes=#{range}-"] if range # pp array array end # return raw response to requester def fetch(url, method: :get, body: nil, headers: headers()) result = CyberarmEngine::Result.new Sync do |task| task.with_timeout(API_TIMEOUT) do Async::HTTP::Internet.send(method, url, headers, body) do |response| if response.success? result.data = response.read else result.error = true end end rescue StandardError => e result.error = e rescue Async::TimeoutError => e result.error = e end result end result end # write response to file, periodically reporting progress to requester def download(url, path:, method: :get, body: nil, headers: headers(), &block) result = CyberarmEngine::Result.new Sync do |task| Async::HTTP::Internet.send(method, url, headers, body) do |response| if response.success? content_length = response.headers["content-length"] || 0 total_downloaded_bytes = 0 range = Integer(headers.find { |key, value| key == "range" }&.last&.split("=")&.last&.split("-")&.first || 0) File.open(path, range.positive? ? "r+b" : "wb") do |file| file.pos = range if range.positive? response.each do |chunk| file.write(chunk) downloaded_bytes = chunk.length total_downloaded_bytes += downloaded_bytes block&.call(downloaded_bytes, total_downloaded_bytes, content_length) end end result.data = true end rescue StandardError => e result.error = e rescue Async::TimeoutError result.error = e end end result end def user_login(username, password) result = CyberarmEngine::Result.new body = URI.encode_www_form("data": JSON.dump({username: username, password: password})) response = fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/launcher/1/user-login", method: :post, body: body, headers: headers(form_encoded: true)) if response.okay? hash = JSON.parse(response.data) user_details_result = fetch_user_details(Integer(hash["userid"])) user_details_hash = user_details_result.okay? ? JSON.parse(user_details_result.data) : {} account = W3DHubLauncher::Worker::Api::Settings::Account.create( user_id: hash["userid"], username: hash["username"], displayname: hash["displayname"], avatar_uri: user_details_hash["avatar-uri"] || "", access_token: hash["accessToken"], refresh_token: hash["refreshToken"], access_token_expiration_time: hash["accessTokenExpiry"] ) result.data = account.to_json end result end def refresh_user_login(refresh_token) result = CyberarmEngine::Result.new body = URI.encode_www_form("data": JSON.dump({refreshToken: refresh_token})) response = fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/launcher/1/user-login", method: :post, body: body, headers: headers(form_encoded: true)) if response.okay? hash = JSON.parse(response.data) user_details_result = fetch_user_details(Integer(hash["userid"])) user_details_hash = user_details_result.okay? ? JSON.parse(user_details_result.data) : {} account = W3DHubLauncher::Worker::Api::Settings::Account.create( user_id: hash["userid"], username: hash["username"], displayname: hash["displayname"], avatar_uri: user_details_hash["avatar-uri"] || "", access_token: hash["accessToken"], refresh_token: hash["refreshToken"], access_token_expiration_time: hash["accessTokenExpiry"] ) result.data = account.to_json end result end def fetch_user_details(user_id) result = CyberarmEngine::Result.new body = URI.encode_www_form("data": JSON.dump({id: user_id})) response = fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/w3dhub/1/get-user-details", method: :post, body: body, headers: headers(form_encoded: true)) result.data = response.data if response.okay? result end def fetch_applications result = CyberarmEngine::Result.new # If we're not signed in, then the primary backend will try to redirect us to the alternate backend. So skip it if we're not signed in. primary_result = @access_token ? fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/launcher/1/get-applications") : CyberarmEngine::Result.new(error: true) alternate_result = fetch("#{ALTERNATIVE_W3DHUB_API_ENDPOINT}/apis/launcher/1/get-applications") single_source = false # We've failed to retrieve data if primary_result.error? && alternate_result.error? return result # We've only gotten data back from the primary backend elsif primary_result.okay? && alternate_result.error? result.data = primary_result.data single_source = true # We've only gotten data back from the alternate backend elsif primary_result.error? && alternate_result.okay? result.data = alternate_result.data single_source = true end unless single_source puts "FIXME: merge applications source" # We've gotten data back from both backends, merge them. # pp [primary_result, alternate_result] # FIXME: Merge primary and alternate results end @applications = JSON.parse(result.data)["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || [] result end def fetch_news(category) fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/w3dhub/1/get-news", method: :post, body: "data={\"category\":\"#{category}\"}", headers: headers(form_encoded: true)) end def fetch_events(category) fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/w3dhub/1/get-server-events", method: :post, body: "data={\"serverPath\":\"#{category}\"}", headers: headers(form_encoded: true)) end def fetch_test_events fetch("#{PRIMARY_W3DHUB_API_ENDPOINT}/apis/w3dhub/1/get-testing-times") end def fetch_manifest() result = CyberarmEngine::Result.new end def fetch_package_details(packages) body = URI.encode_www_form("data": JSON.dump({ packages: packages })) fetch("#{@access_token ? PRIMARY_W3DHUB_API_ENDPOINT : ALTERNATIVE_W3DHUB_API_ENDPOINT}/apis/launcher/1/get-package-details", method: :post, body: body, headers: headers(form_encoded: true)) end def download_package() download() end end end