nas/app/workers/fetch_instance_info_worker.rb
2023-08-05 09:10:55 +09:00

75 lines
2.3 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
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') && item['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.0' }
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