resolve_url_service.rb 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # frozen_string_literal: true
  2. class ResolveURLService < BaseService
  3. include JsonLdHelper
  4. include Authorization
  5. USERNAME_STATUS_RE = %r{/@(?<username>#{Account::USERNAME_RE})/(?<status_id>[0-9]+)\Z}
  6. def call(url, on_behalf_of: nil)
  7. @url = url
  8. @on_behalf_of = on_behalf_of
  9. if local_url?
  10. process_local_url
  11. elsif !fetched_resource.nil?
  12. process_url
  13. else
  14. process_url_from_db
  15. end
  16. end
  17. private
  18. def process_url
  19. if equals_or_includes_any?(type, ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES)
  20. ActivityPub::FetchRemoteActorService.new.call(resource_url, prefetched_body: body)
  21. elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
  22. status = FetchRemoteStatusService.new.call(resource_url, body)
  23. authorize_with @on_behalf_of, status, :show? unless status.nil?
  24. status
  25. end
  26. end
  27. def process_url_from_db
  28. if [500, 502, 503, 504, nil].include?(fetch_resource_service.response_code)
  29. account = Account.find_by(uri: @url)
  30. return account unless account.nil?
  31. end
  32. return unless @on_behalf_of.present? && [401, 403, 404].include?(fetch_resource_service.response_code)
  33. # It may happen that the resource is a private toot, and thus not fetchable,
  34. # but we can return the toot if we already know about it.
  35. scope = Status.where(uri: @url)
  36. # We don't have an index on `url`, so try guessing the `uri` from `url`
  37. parsed_url = Addressable::URI.parse(@url)
  38. parsed_url.path.match(USERNAME_STATUS_RE) do |matched|
  39. parsed_url.path = "/users/#{matched[:username]}/statuses/#{matched[:status_id]}"
  40. scope = scope.or(Status.where(uri: parsed_url.to_s, url: @url))
  41. end
  42. status = scope.first
  43. authorize_with @on_behalf_of, status, :show? unless status.nil?
  44. status
  45. rescue Mastodon::NotPermittedError
  46. nil
  47. end
  48. def fetched_resource
  49. @fetched_resource ||= fetch_resource_service.call(@url)
  50. end
  51. def fetch_resource_service
  52. @_fetch_resource_service ||= FetchResourceService.new
  53. end
  54. def resource_url
  55. fetched_resource.first
  56. end
  57. def body
  58. fetched_resource.second[:prefetched_body]
  59. end
  60. def type
  61. json_data['type']
  62. end
  63. def json_data
  64. @json_data ||= body_to_json(body)
  65. end
  66. def local_url?
  67. TagManager.instance.local_url?(@url)
  68. end
  69. def process_local_url
  70. recognized_params = Rails.application.routes.recognize_path(@url)
  71. case recognized_params[:controller]
  72. when 'statuses'
  73. return unless recognized_params[:action] == 'show'
  74. status = Status.find_by(id: recognized_params[:id])
  75. check_local_status(status)
  76. when 'accounts'
  77. return unless recognized_params[:action] == 'show'
  78. Account.find_local(recognized_params[:username])
  79. when 'home'
  80. return unless recognized_params[:action] == 'index' && recognized_params[:username_with_domain].present?
  81. if recognized_params[:any]&.match?(/\A[0-9]+\Z/)
  82. status = Status.find_by(id: recognized_params[:any])
  83. check_local_status(status)
  84. elsif recognized_params[:any].blank?
  85. username, domain = recognized_params[:username_with_domain].gsub(/\A@/, '').split('@')
  86. return unless username.present? && domain.present?
  87. Account.find_remote(username, domain)
  88. end
  89. end
  90. end
  91. def check_local_status(status)
  92. return if status.nil?
  93. authorize_with @on_behalf_of, status, :show?
  94. status
  95. rescue Mastodon::NotPermittedError
  96. nil
  97. end
  98. end