77 lines
2.4 KiB
Ruby
77 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class FetchInstanceInfoWorker
|
|
include Sidekiq::Worker
|
|
include JsonLdHelper
|
|
include Redisable
|
|
include Lockable
|
|
|
|
class Error < StandardError; end
|
|
class GoneError < Error; end
|
|
class RequestError < Error; end
|
|
|
|
SUPPORTED_NOTEINFO_RELS = ['http://nodeinfo.diaspora.software/ns/schema/2.0', 'http://nodeinfo.diaspora.software/ns/schema/2.1'].freeze
|
|
|
|
def perform(domain)
|
|
@instance = Instance.find_by(domain: domain)
|
|
return if !@instance || @instance.unavailable_domain.present?
|
|
|
|
with_redis_lock("instance_info:#{domain}") do
|
|
link = nodeinfo_link
|
|
return if link.nil?
|
|
|
|
update_info!(link)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def nodeinfo_link
|
|
nodeinfo = fetch_json("https://#{@instance.domain}/.well-known/nodeinfo")
|
|
return nil if nodeinfo.nil? || !nodeinfo.key?('links')
|
|
|
|
nodeinfo_links = nodeinfo['links']
|
|
return nil if !nodeinfo_links.is_a?(Array) || nodeinfo_links.blank?
|
|
|
|
nodeinfo_link = nodeinfo_links.find { |item| item.key?('rel') && item.key?('href') && SUPPORTED_NOTEINFO_RELS.include?(item['rel']) }
|
|
return nil if nodeinfo_link.nil? || nodeinfo_link['href'].nil? || !nodeinfo_link['href'].start_with?('http')
|
|
|
|
nodeinfo_link['href']
|
|
end
|
|
|
|
def update_info!(url)
|
|
content = fetch_json(url)
|
|
return nil if content.nil? || !content.key?('software') || !content['software'].key?('name')
|
|
|
|
software = content['software']['name']
|
|
version = content['software'].key?('version') ? content['software']['version'] : ''
|
|
|
|
exists = @instance.instance_info
|
|
if exists.nil?
|
|
InstanceInfo.create!(domain: @instance.domain, software: software, version: version, data: content)
|
|
else
|
|
exists.software = software
|
|
exists.version = version
|
|
exists.data = content
|
|
exists.save!
|
|
end
|
|
end
|
|
|
|
def fetch_json(url)
|
|
build_request(url).perform do |response|
|
|
if [200, 203].include?(response.code)
|
|
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
|
|
|
|
body_to_json(response.body_with_limit)
|
|
elsif response.code == 410
|
|
raise FetchInstanceInfoWorker::GoneError, "#{domain} is gone from the server"
|
|
else
|
|
raise FetchInstanceInfoWorker::RequestError, "Request for #{domain} returned HTTP #{response.code}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def build_request(url)
|
|
Request.new(:get, url).add_headers('Accept' => 'application/jrd+json, application/json')
|
|
end
|
|
end
|