Files
w3dhub_linux_launcher/lib/worker/w3dhub_api.rb

302 lines
10 KiB
Ruby

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(worker)
@worker = worker
@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
else
result.error = response.read
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)
if hash["error"]
result.error = hash["error"]
return result
end
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)
if hash["error"]
result.error = hash["error"]
return result
end
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
if single_source
@applications = JSON.parse(result.data)["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || []
result
else # We've gotten data back from both backends, merge them.
primary = JSON.parse(primary_result.data)["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || []
alternate = JSON.parse(alternate_result.data)["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || []
primary.each do |app|
application = alternate.find { |a| a.id == app.id }
unless application
alternate << app
# App doesn't exist in alternate list
# comparing channels isn't useful
next
end
app.channels.each do |chan|
channel = application.channels.find { |c| c.id == chan.id }
unless channel
application.channels << chan
# App doesn't have channel in alternate list
# comparing channels isn't useful
next
end
# If channel versions and access levels match then all is well
if chan.version == channel.version &&
chan.user_level == channel.user_level
# All is well!
next
end
# If the access levels don't match then overwrite alternate's channel with primary's channel
if chan.user_level != channel.user_level
# Replace alternate's channel with primary's channel
application.channels[application.channels.index(channel)] = chan
# Replaced, continue.
next
end
# If versions don't match then pick whichever one is higher
if Gem::Version.new(chan.current_version) > Gem::Version.new(channel.current_version)
# Replace alternate's channel with primary's channel
application.channels[application.channels.index(channel)] = chan
else
# Do nothing, alternate backend version is greater.
end
end
end
@applications = alternate # alternate is merged with primary
result.data = { applications: alternate }.to_json
result
end
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 fetch_package(manifest_package, path:, range: 0)
body = URI.encode_www_form(
"data": JSON.dump({
category: manifest_package.category,
subcategory: manifest_package.subcategory,
name: manifest_package.name,
version: manifest_package.version
})
)
download(
"#{W3DHubApi::PRIMARY_W3DHUB_API_ENDPOINT}/apis/launcher/1/get-package",
path: path,
method: :post,
body: body,
headers: headers(form_encoded: true, range: range <= 0 ? nil : range)
)
end
end
end