From 84d802016587e8fce9b5b9d0e233d187ae8ace14 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 10:11:32 +0100 Subject: [PATCH] Store a remote actor's `featuredCollections` URI (#38166) --- app/models/account.rb | 1 + .../activitypub/process_account_service.rb | 1 + ...1152331_add_collections_url_to_accounts.rb | 7 +++++ db/schema.rb | 3 +- .../process_account_service_spec.rb | 28 +++++++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20260311152331_add_collections_url_to_accounts.rb diff --git a/app/models/account.rb b/app/models/account.rb index 0e80b99d8d..ceb5f857a9 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -15,6 +15,7 @@ # avatar_remote_url :string # avatar_storage_schema_version :integer # avatar_updated_at :datetime +# collections_url :string # discoverable :boolean # display_name :string default(""), not null # domain :string diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index efdaa26974..e23f4a15bb 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -125,6 +125,7 @@ class ActivityPub::ProcessAccountService < BaseService def set_immediate_attributes! @account.featured_collection_url = valid_collection_uri(@json['featured']) + @account.collections_url = valid_collection_uri(@json['featuredCollections']) @account.display_name = (@json['name'] || '')[0...(Account::DISPLAY_NAME_LENGTH_HARD_LIMIT)] @account.note = (@json['summary'] || '')[0...(Account::NOTE_LENGTH_HARD_LIMIT)] @account.locked = @json['manuallyApprovesFollowers'] || false diff --git a/db/migrate/20260311152331_add_collections_url_to_accounts.rb b/db/migrate/20260311152331_add_collections_url_to_accounts.rb new file mode 100644 index 0000000000..7fc0bb2944 --- /dev/null +++ b/db/migrate/20260311152331_add_collections_url_to_accounts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddCollectionsURLToAccounts < ActiveRecord::Migration[8.1] + def change + add_column :accounts, :collections_url, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d01d1af500..c85565211c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_03_10_095021) do +ActiveRecord::Schema[8.1].define(version: 2026_03_11_152331) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -162,6 +162,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_03_10_095021) do t.string "avatar_remote_url" t.integer "avatar_storage_schema_version" t.datetime "avatar_updated_at", precision: nil + t.string "collections_url" t.datetime "created_at", precision: nil, null: false t.boolean "discoverable" t.string "display_name", default: "", null: false diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index b2c6bc17b2..1d8d8f8ac5 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -63,6 +63,34 @@ RSpec.describe ActivityPub::ProcessAccountService do end end + context 'with collection URIs' do + let(:payload) do + { + 'id' => 'https://foo.test', + 'type' => 'Actor', + 'inbox' => 'https://foo.test/inbox', + 'featured' => 'https://foo.test/featured', + 'followers' => 'https://foo.test/followers', + 'following' => 'https://foo.test/following', + 'featuredCollections' => 'https://foo.test/featured_collections', + } + end + + before do + stub_request(:get, %r{^https://foo\.test/follow}) + .to_return(status: 200, body: '', headers: {}) + end + + it 'parses and sets the URIs' do + account = subject.call('alice', 'example.com', payload) + + expect(account.featured_collection_url).to eq 'https://foo.test/featured' + expect(account.followers_url).to eq 'https://foo.test/followers' + expect(account.following_url).to eq 'https://foo.test/following' + expect(account.collections_url).to eq 'https://foo.test/featured_collections' + end + end + context 'with attribution domains' do let(:payload) do {