Add references activitypub support
This commit is contained in:
parent
4c65f2721c
commit
cadfbbab96
8 changed files with 115 additions and 5 deletions
87
app/controllers/activitypub/references_controller.rb
Normal file
87
app/controllers/activitypub/references_controller.rb
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ActivityPub::ReferencesController < ActivityPub::BaseController
|
||||||
|
include SignatureVerification
|
||||||
|
include Authorization
|
||||||
|
include AccountOwnedConcern
|
||||||
|
|
||||||
|
REFERENCES_LIMIT = 5
|
||||||
|
|
||||||
|
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||||
|
before_action :set_status
|
||||||
|
|
||||||
|
def index
|
||||||
|
expires_in 0, public: public_fetch_mode?
|
||||||
|
render json: references_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pundit_user
|
||||||
|
signed_request_account
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_status
|
||||||
|
@status = @account.statuses.find(params[:status_id])
|
||||||
|
authorize @status, :show?
|
||||||
|
rescue Mastodon::NotPermittedError
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_statuses
|
||||||
|
cached_references
|
||||||
|
end
|
||||||
|
|
||||||
|
def cached_references
|
||||||
|
cache_collection(Status.where(id: results).reorder(:id), Status)
|
||||||
|
end
|
||||||
|
|
||||||
|
def results
|
||||||
|
@results ||= begin
|
||||||
|
references = @status.reference_objects.order(target_status_id: :asc)
|
||||||
|
references = references.where('target_status_id > ?', page_params[:min_id]) if page_params[:min_id].present?
|
||||||
|
references = references.limit(limit_param(REFERENCES_LIMIT))
|
||||||
|
references.pluck(:target_status_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_min_id
|
||||||
|
results.last
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
results.size == limit_param(REFERENCES_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def references_collection_presenter
|
||||||
|
page = ActivityPub::CollectionPresenter.new(
|
||||||
|
id: ActivityPub::TagManager.instance.references_uri_for(@status, page_params),
|
||||||
|
type: :unordered,
|
||||||
|
part_of: ActivityPub::TagManager.instance.references_uri_for(@status),
|
||||||
|
items: load_statuses.map(&:uri),
|
||||||
|
next: next_page
|
||||||
|
)
|
||||||
|
|
||||||
|
return page if page_requested?
|
||||||
|
|
||||||
|
ActivityPub::CollectionPresenter.new(
|
||||||
|
type: :unordered,
|
||||||
|
id: ActivityPub::TagManager.instance.references_uri_for(@status),
|
||||||
|
first: page
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def page_requested?
|
||||||
|
truthy_param?(:page)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_page
|
||||||
|
return unless records_continue?
|
||||||
|
|
||||||
|
ActivityPub::TagManager.instance.references_uri_for(@status, page_params.merge(min_id: pagination_min_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def page_params
|
||||||
|
params_slice(:min_id, :limit).merge(page: true)
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,6 +25,7 @@ module ContextHelper
|
||||||
searchable_by: { 'fedibird' => 'http://fedibird.com/ns#', 'searchableBy' => { '@id' => 'fedibird:searchableBy', '@type' => '@id' } },
|
searchable_by: { 'fedibird' => 'http://fedibird.com/ns#', 'searchableBy' => { '@id' => 'fedibird:searchableBy', '@type' => '@id' } },
|
||||||
subscribable_by: { 'kmyblue' => 'http://kmy.blue/ns#', 'subscribableBy' => { '@id' => 'kmyblue:subscribableBy', '@type' => '@id' } },
|
subscribable_by: { 'kmyblue' => 'http://kmy.blue/ns#', 'subscribableBy' => { '@id' => 'kmyblue:subscribableBy', '@type' => '@id' } },
|
||||||
other_setting: { 'fedibird' => 'http://fedibird.com/ns#', 'otherSetting' => 'fedibird:otherSetting' },
|
other_setting: { 'fedibird' => 'http://fedibird.com/ns#', 'otherSetting' => 'fedibird:otherSetting' },
|
||||||
|
references: { 'fedibird' => 'http://fedibird.com/ns#', 'references' => { '@id' => 'fedibird:references', '@type' => '@id' } },
|
||||||
olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' },
|
olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' },
|
||||||
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
|
@ -473,7 +473,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_references!
|
def process_references!
|
||||||
references = @json['references'].nil? ? [] : ActivityPub::FetchReferencesService(@json['references'])
|
references = @json['references'].nil? ? [] : ActivityPub::FetchReferencesService.new.call(@json['references'])
|
||||||
|
quote = @json['quote'] || @json['quoteUrl'] || @json['quoteURL'] || @json['_misskey_quote']
|
||||||
|
references << quote if quote
|
||||||
ProcessReferencesWorker.perform_async(@status.id, [], references)
|
ProcessReferencesWorker.perform_async(@status.id, [], references)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,12 @@ class ActivityPub::TagManager
|
||||||
account_status_replies_url(target.account, target, page_params)
|
account_status_replies_url(target.account, target, page_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def references_uri_for(target, page_params = nil)
|
||||||
|
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
|
||||||
|
|
||||||
|
account_status_references_url(target.account, target, page_params)
|
||||||
|
end
|
||||||
|
|
||||||
def followers_uri_for(target)
|
def followers_uri_for(target)
|
||||||
target.local? ? account_followers_url(target) : target.followers_url.presence
|
target.local? ? account_followers_url(target) : target.followers_url.presence
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
include FormattingHelper
|
include FormattingHelper
|
||||||
|
|
||||||
context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :searchable_by
|
context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :searchable_by, :references
|
||||||
|
|
||||||
attributes :id, :type, :summary,
|
attributes :id, :type, :summary,
|
||||||
:in_reply_to, :published, :url,
|
:in_reply_to, :published, :url,
|
||||||
:attributed_to, :to, :cc, :sensitive,
|
:attributed_to, :to, :cc, :sensitive,
|
||||||
:atom_uri, :in_reply_to_atom_uri,
|
:atom_uri, :in_reply_to_atom_uri,
|
||||||
:conversation, :searchable_by
|
:conversation, :searchable_by, :references
|
||||||
|
|
||||||
attribute :content
|
attribute :content
|
||||||
attribute :content_map, if: :language?
|
attribute :content_map, if: :language?
|
||||||
|
@ -64,6 +64,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def references
|
||||||
|
ActivityPub::TagManager.instance.references_uri_for(object)
|
||||||
|
end
|
||||||
|
|
||||||
def language?
|
def language?
|
||||||
object.language.present?
|
object.language.present?
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ class ActivityPub::FetchReferencesService < BaseService
|
||||||
|
|
||||||
all_items.concat(items)
|
all_items.concat(items)
|
||||||
|
|
||||||
break if all_items.size >= StatusReferenceValidator::LIMIT
|
break if all_items.size >= 5
|
||||||
|
|
||||||
collection = collection['next'].present? ? fetch_collection(collection['next']) : nil
|
collection = collection['next'].present? ? fetch_collection(collection['next']) : nil
|
||||||
end
|
end
|
||||||
|
@ -42,7 +42,8 @@ class ActivityPub::FetchReferencesService < BaseService
|
||||||
|
|
||||||
def fetch_collection(collection_or_uri)
|
def fetch_collection(collection_or_uri)
|
||||||
return collection_or_uri if collection_or_uri.is_a?(Hash)
|
return collection_or_uri if collection_or_uri.is_a?(Hash)
|
||||||
return if invalid_origin?(collection_or_uri)
|
return if unsupported_uri_scheme?(collection_or_uri)
|
||||||
|
return if ActivityPub::TagManager.instance.local_uri?(collection_or_uri)
|
||||||
|
|
||||||
fetch_resource_without_id_validation(collection_or_uri, nil, true)
|
fetch_resource_without_id_validation(collection_or_uri, nil, true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,6 +45,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
||||||
create_edits!
|
create_edits!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
update_references!
|
||||||
download_media_files!
|
download_media_files!
|
||||||
queue_poll_notifications!
|
queue_poll_notifications!
|
||||||
|
|
||||||
|
@ -240,6 +241,13 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_references!
|
||||||
|
references = @json['references'].nil? ? [] : ActivityPub::FetchReferencesService.new.call(@json['references'])
|
||||||
|
quote = @json['quote'] || @json['quoteUrl'] || @json['quoteURL'] || @json['_misskey_quote']
|
||||||
|
references << quote if quote
|
||||||
|
ProcessReferencesWorker.perform_async(@status.id, [], references)
|
||||||
|
end
|
||||||
|
|
||||||
def expected_type?
|
def expected_type?
|
||||||
equals_or_includes_any?(@json['type'], %w(Note Question))
|
equals_or_includes_any?(@json['type'], %w(Note Question))
|
||||||
end
|
end
|
||||||
|
|
|
@ -99,6 +99,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :replies, only: [:index], module: :activitypub
|
resources :replies, only: [:index], module: :activitypub
|
||||||
|
resources :references, only: [:index], module: :activitypub
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :followers, only: [:index], controller: :follower_accounts
|
resources :followers, only: [:index], controller: :follower_accounts
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue