federails 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/app/controllers/concerns/federails/server/render_collections.rb +19 -0
  4. data/app/controllers/federails/client/actors_controller.rb +1 -1
  5. data/app/controllers/federails/client/followings_controller.rb +4 -4
  6. data/app/controllers/federails/server/activities_controller.rb +14 -6
  7. data/app/controllers/federails/server/actors_controller.rb +16 -7
  8. data/app/controllers/federails/server/nodeinfo_controller.rb +2 -2
  9. data/app/helpers/federails/server_helper.rb +6 -0
  10. data/app/jobs/federails/application_job.rb +2 -0
  11. data/app/jobs/federails/fetch_nodeinfo_job.rb +10 -0
  12. data/app/jobs/federails/notify_inbox_job.rb +0 -2
  13. data/app/models/concerns/federails/actor_entity.rb +5 -1
  14. data/app/models/concerns/federails/data_entity.rb +80 -8
  15. data/app/models/concerns/federails/handles_delete_requests.rb +5 -0
  16. data/app/models/federails/activity.rb +15 -22
  17. data/app/models/federails/actor.rb +44 -3
  18. data/app/models/federails/following.rb +3 -3
  19. data/app/models/federails/host.rb +48 -0
  20. data/app/views/federails/client/common/_client_links.html.erb +3 -1
  21. data/app/views/federails/client/followings/_follow_actions.html.erb +5 -1
  22. data/app/views/federails/client/followings/_follower.html.erb +1 -1
  23. data/app/views/federails/server/activities/_activity.activitypub.jbuilder +7 -3
  24. data/app/views/federails/server/actors/_actor.activitypub.jbuilder +7 -6
  25. data/app/views/federails/server/actors/_tombstone.activitypub.jbuilder +1 -4
  26. data/app/views/federails/server/followings/_following.activitypub.jbuilder +1 -1
  27. data/app/views/federails/server/nodeinfo/index.nodeinfo.jbuilder +1 -1
  28. data/app/views/federails/server/nodeinfo/show.nodeinfo.jbuilder +2 -3
  29. data/app/views/federails/server/published/_publishable.activitypub.jbuilder +2 -2
  30. data/app/views/federails/server/published/_tombstone.activitypub.jbuilder +1 -4
  31. data/app/views/federails/server/shared/ordered_collection.activitypub.jbuilder +6 -0
  32. data/app/views/federails/server/shared/ordered_collection_page.activitypub.jbuilder +12 -0
  33. data/app/views/users/show.html.erb +4 -0
  34. data/db/migrate/20250426061729_create_federails_hosts.rb +22 -0
  35. data/db/migrate/20251121160720_add_to_and_cc_to_federails_activities.rb +6 -0
  36. data/lib/federails/configuration.rb +25 -0
  37. data/lib/federails/data_transformer/note.rb +6 -1
  38. data/lib/federails/maintenance/actors_updater.rb +3 -4
  39. data/lib/federails/maintenance/hosts_updater.rb +19 -0
  40. data/lib/federails/utils/actor.rb +33 -1
  41. data/lib/federails/utils/context.rb +12 -0
  42. data/lib/federails/utils/json_request.rb +41 -0
  43. data/lib/federails/utils/object.rb +1 -1
  44. data/lib/federails/utils/response_codes.rb +11 -0
  45. data/lib/federails/version.rb +1 -1
  46. data/lib/federails.rb +16 -3
  47. data/lib/fediverse/collection.rb +31 -0
  48. data/lib/fediverse/inbox.rb +10 -0
  49. data/lib/fediverse/node_info.rb +38 -0
  50. data/lib/fediverse/notifier.rb +39 -12
  51. data/lib/fediverse/request.rb +6 -28
  52. data/lib/fediverse/signature.rb +1 -1
  53. data/lib/fediverse/webfinger.rb +10 -33
  54. data/lib/fediverse.rb +2 -0
  55. data/lib/generators/federails/copy_client_policies/USAGE +8 -0
  56. data/lib/generators/federails/copy_client_policies/copy_client_policies_generator.rb +9 -0
  57. data/lib/generators/federails/copy_factories/USAGE +8 -0
  58. data/lib/generators/federails/copy_factories/copy_factories_generator.rb +43 -0
  59. data/lib/generators/federails/install/templates/federails.yml +2 -0
  60. data/lib/tasks/federails_tasks.rake +5 -0
  61. metadata +38 -6
  62. data/app/views/federails/server/activities/outbox.activitypub.jbuilder +0 -18
  63. data/app/views/federails/server/actors/followers.activitypub.jbuilder +0 -18
  64. data/app/views/federails/server/actors/following.activitypub.jbuilder +0 -18
@@ -2,27 +2,7 @@ require 'json/ld'
2
2
 
3
3
  module Fediverse
4
4
  class Request
5
- BASE_HEADERS = {
6
- 'Content-Type' => 'application/json',
7
- 'Accept' => 'application/json',
8
- }.freeze
9
-
10
- def initialize(id)
11
- @id = id
12
- end
13
-
14
- # FIXME: Replace by `Webfinger.get_json` (move other method here as class method)
15
- def get
16
- Rails.logger.debug { "GET #{@id}" }
17
- @response = Faraday.get(@id, nil, BASE_HEADERS)
18
- response_to_json
19
- end
20
-
21
5
  class << self
22
- def get(id)
23
- new(id).get
24
- end
25
-
26
6
  # Dereferences a value
27
7
  #
28
8
  # @param value [String, Hash]
@@ -34,18 +14,16 @@ module Fediverse
34
14
 
35
15
  raise "Unhandled object type #{value.class}"
36
16
  end
37
- end
38
17
 
39
- private
18
+ private
40
19
 
41
- def response_to_json
42
- begin
43
- body = JSON.parse @response.body
20
+ def get(id)
21
+ json = Federails::Utils::JsonRequest.get_json(id)
22
+
23
+ JSON::LD::API.compact json, json['@context']
44
24
  rescue JSON::ParserError
45
- return
25
+ nil
46
26
  end
47
-
48
- JSON::LD::API.compact body, body['@context']
49
27
  end
50
28
  end
51
29
  end
@@ -20,7 +20,7 @@ module Fediverse
20
20
  raise 'Unsigned headers' unless request.headers['Signature']
21
21
 
22
22
  signature_header = request.headers['Signature'].split(',').to_h do |pair|
23
- /\A(?<key>[\w]+)="(?<value>.*)"\z/ =~ pair
23
+ /\A(?<key>\w+)="(?<value>.*)"\z/ =~ pair
24
24
  [key, value]
25
25
  end
26
26
 
@@ -1,7 +1,5 @@
1
- require 'faraday'
2
- require 'faraday/follow_redirects'
3
-
4
1
  require 'federails/utils/host'
2
+ require 'federails/utils/json_request'
5
3
 
6
4
  module Fediverse
7
5
  # Methods related to Webfinger: find accounts, fetch actors,...
@@ -122,42 +120,21 @@ module Fediverse
122
120
  # Makes a simple GET request and returns a +Hash+ from the parsed body
123
121
  # @return [Hash]
124
122
  # @raise [ActiveRecord::RecordNotFound] when the response is invalid
125
- def get_json(url, payload = {})
126
- response = get(url, payload: payload, headers: { accept: 'application/json' })
123
+ def get_json(url, params = {})
124
+ Federails::Utils::JsonRequest.get_json(url, params: params, follow_redirects: true, headers: { accept: 'application/json' })
125
+ rescue Federails::Utils::JsonRequest::UnhandledResponseStatus => e
126
+ Rails.logger.debug { e.message }
127
127
 
128
- if response.status != 200
129
- Rails.logger.debug { "Unhandled status code #{response.status} for GET #{url}" }
130
- raise ActiveRecord::RecordNotFound
131
- end
128
+ raise ActiveRecord::RecordNotFound
129
+ rescue Faraday::ConnectionFailed
130
+ Rails.logger.debug { "Failed to reach server for GET #{url}" }
132
131
 
133
- JSON.parse(response.body)
132
+ raise ActiveRecord::RecordNotFound
134
133
  rescue JSON::ParserError
135
- Rails.logger.debug { "Invalid JSON response GET #{url}" }
134
+ Rails.logger.debug { "Invalid JSON response for GET #{url}" }
136
135
 
137
136
  raise ActiveRecord::RecordNotFound
138
137
  end
139
-
140
- # Only perform a GET request and throws an ActiveRecord::RecordNotFound on error.
141
- #
142
- # That's "ok-ish"; when an actor is unavailable, whatever the reason is, it's not found...
143
- #
144
- # @return [Faraday::Response]
145
- # @raise [ActiveRecord::RecordNotFound] when the response is invalid
146
- def get(url, payload: {}, headers: {})
147
- connection = Faraday.new url: url, params: payload, headers: headers do |faraday|
148
- faraday.response :follow_redirects # use Faraday::FollowRedirects::Middleware
149
- faraday.adapter Faraday.default_adapter
150
- end
151
-
152
- begin
153
- response = connection.get
154
- rescue Faraday::ConnectionFailed
155
- Rails.logger.debug { "Failed to reach server for GET #{url}" }
156
- raise ActiveRecord::RecordNotFound
157
- end
158
-
159
- response
160
- end
161
138
  end
162
139
  end
163
140
  end
data/lib/fediverse.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'fediverse/collection'
2
+
1
3
  # This module includes classes and helpers to interact with the Fediverse.
2
4
  module Fediverse
3
5
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies the Pundit client policies to be overridden in main application.
3
+
4
+ Example:
5
+ bin/rails generate copy_client_policies
6
+
7
+ This will create:
8
+ app/policies/federails/client/*
@@ -0,0 +1,9 @@
1
+ module Federails
2
+ class CopyClientPoliciesGenerator < Rails::Generators::Base
3
+ source_root File.expand_path('../../../../app/policies/federails', __dir__)
4
+
5
+ def copy_policies
6
+ directory 'client', Rails.root.join('app', 'policies', 'federails', 'client')
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies FactoryBot factories in the "spec" directory
3
+
4
+ Example:
5
+ bin/rails generate copy_factories
6
+
7
+ This will create:
8
+ spec/factories/*
@@ -0,0 +1,43 @@
1
+ module Federails
2
+ class CopyFactoriesGenerator < Rails::Generators::Base
3
+ SOURCE_DIRECTORY = File.expand_path('../../../../spec/factories/federails', __dir__)
4
+ FACTORY_DEFINITION_REGEX = /(FactoryBot.define do\n\s+factory) :(\w+),/
5
+
6
+ source_root SOURCE_DIRECTORY
7
+
8
+ def initialize
9
+ super
10
+ @files = []
11
+ @factories = []
12
+ end
13
+
14
+ def copy_factories
15
+ dest = Rails.root.join('spec', 'factories')
16
+
17
+ Dir.entries(SOURCE_DIRECTORY).each do |node|
18
+ source_path = File.join(SOURCE_DIRECTORY, node)
19
+ next unless File.file?(source_path) && node.match?(/\.rb\Z/)
20
+
21
+ file_path = File.join(dest, "federails_#{node}")
22
+ copy_file node, file_path
23
+ @files << file_path
24
+ @factories << File.read(file_path).match(FACTORY_DEFINITION_REGEX)&.[](2)
25
+ end
26
+
27
+ substitute_values!
28
+ end
29
+
30
+ private
31
+
32
+ def substitute_values!
33
+ @factories.compact!
34
+ @files.each do |file|
35
+ gsub_file file, FACTORY_DEFINITION_REGEX, '\1 :federails_\2,'
36
+
37
+ @factories.each do |factory|
38
+ gsub_file file, ":#{factory}", ":federails_#{factory}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -10,6 +10,8 @@ defaults: &defaults
10
10
  app_layout: 'layouts/application'
11
11
  server_routes_path: federation
12
12
  client_routes_path: app
13
+ job_queue: default
14
+ #remote_entities_cache_duration: 86400 # 1 day in seconds
13
15
  #base_client_controller: ::ActionController::Base
14
16
 
15
17
  development:
@@ -5,4 +5,9 @@ namespace :federails do
5
5
  puts "#{actor.federated_url}: #{status}"
6
6
  end
7
7
  end
8
+
9
+ desc 'Re-fetches every host and completes missing ones'
10
+ task sync_hosts: :environment do
11
+ Federails::Maintenance::HostsUpdater.run
12
+ end
8
13
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: federails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Tancoigne
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-04-18 00:00:00.000000000 Z
11
+ date: 2026-03-25 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: faraday
@@ -37,6 +38,20 @@ dependencies:
37
38
  - - ">="
38
39
  - !ruby/object:Gem::Version
39
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jbuilder
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.7'
40
55
  - !ruby/object:Gem::Dependency
41
56
  name: json-ld
42
57
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +134,7 @@ files:
119
134
  - Rakefile
120
135
  - app/assets/config/federails_manifest.js
121
136
  - app/assets/stylesheets/federails/application.css
137
+ - app/controllers/concerns/federails/server/render_collections.rb
122
138
  - app/controllers/federails/client/activities_controller.rb
123
139
  - app/controllers/federails/client/actors_controller.rb
124
140
  - app/controllers/federails/client/followings_controller.rb
@@ -132,6 +148,7 @@ files:
132
148
  - app/controllers/federails/server_controller.rb
133
149
  - app/helpers/federails/server_helper.rb
134
150
  - app/jobs/federails/application_job.rb
151
+ - app/jobs/federails/fetch_nodeinfo_job.rb
135
152
  - app/jobs/federails/notify_inbox_job.rb
136
153
  - app/mailers/federails/application_mailer.rb
137
154
  - app/models/concerns/federails/actor_entity.rb
@@ -142,6 +159,7 @@ files:
142
159
  - app/models/federails/actor.rb
143
160
  - app/models/federails/application_record.rb
144
161
  - app/models/federails/following.rb
162
+ - app/models/federails/host.rb
145
163
  - app/policies/federails/client/activity_policy.rb
146
164
  - app/policies/federails/client/actor_policy.rb
147
165
  - app/policies/federails/client/following_policy.rb
@@ -175,12 +193,9 @@ files:
175
193
  - app/views/federails/client/followings/show.html.erb
176
194
  - app/views/federails/client/followings/show.json.jbuilder
177
195
  - app/views/federails/server/activities/_activity.activitypub.jbuilder
178
- - app/views/federails/server/activities/outbox.activitypub.jbuilder
179
196
  - app/views/federails/server/activities/show.activitypub.jbuilder
180
197
  - app/views/federails/server/actors/_actor.activitypub.jbuilder
181
198
  - app/views/federails/server/actors/_tombstone.activitypub.jbuilder
182
- - app/views/federails/server/actors/followers.activitypub.jbuilder
183
- - app/views/federails/server/actors/following.activitypub.jbuilder
184
199
  - app/views/federails/server/actors/show.activitypub.jbuilder
185
200
  - app/views/federails/server/followings/_following.activitypub.jbuilder
186
201
  - app/views/federails/server/followings/show.activitypub.jbuilder
@@ -189,8 +204,11 @@ files:
189
204
  - app/views/federails/server/published/_publishable.activitypub.jbuilder
190
205
  - app/views/federails/server/published/_tombstone.activitypub.jbuilder
191
206
  - app/views/federails/server/published/show.activitypub.jbuilder
207
+ - app/views/federails/server/shared/ordered_collection.activitypub.jbuilder
208
+ - app/views/federails/server/shared/ordered_collection_page.activitypub.jbuilder
192
209
  - app/views/federails/server/web_finger/find.jrd.jbuilder
193
210
  - app/views/federails/server/web_finger/host_meta.xrd.erb
211
+ - app/views/users/show.html.erb
194
212
  - config/initializers/mime_types.rb
195
213
  - config/routes.rb
196
214
  - db/migrate/20200712133150_create_federails_actors.rb
@@ -202,23 +220,35 @@ files:
202
220
  - db/migrate/20250301082500_add_local_to_actors.rb
203
221
  - db/migrate/20250329123939_add_actor_type_to_actors.rb
204
222
  - db/migrate/20250329123940_add_tombstoned_at_to_actors.rb
223
+ - db/migrate/20250426061729_create_federails_hosts.rb
224
+ - db/migrate/20251121160720_add_to_and_cc_to_federails_activities.rb
205
225
  - lib/federails.rb
206
226
  - lib/federails/configuration.rb
207
227
  - lib/federails/data_transformer/note.rb
208
228
  - lib/federails/engine.rb
209
229
  - lib/federails/maintenance/actors_updater.rb
230
+ - lib/federails/maintenance/hosts_updater.rb
210
231
  - lib/federails/utils/actor.rb
232
+ - lib/federails/utils/context.rb
211
233
  - lib/federails/utils/host.rb
234
+ - lib/federails/utils/json_request.rb
212
235
  - lib/federails/utils/object.rb
236
+ - lib/federails/utils/response_codes.rb
213
237
  - lib/federails/version.rb
214
238
  - lib/fediverse.rb
239
+ - lib/fediverse/collection.rb
215
240
  - lib/fediverse/inbox.rb
241
+ - lib/fediverse/node_info.rb
216
242
  - lib/fediverse/notifier.rb
217
243
  - lib/fediverse/request.rb
218
244
  - lib/fediverse/signature.rb
219
245
  - lib/fediverse/webfinger.rb
246
+ - lib/generators/federails/copy_client_policies/USAGE
247
+ - lib/generators/federails/copy_client_policies/copy_client_policies_generator.rb
220
248
  - lib/generators/federails/copy_client_views/USAGE
221
249
  - lib/generators/federails/copy_client_views/copy_client_views_generator.rb
250
+ - lib/generators/federails/copy_factories/USAGE
251
+ - lib/generators/federails/copy_factories/copy_factories_generator.rb
222
252
  - lib/generators/federails/install/USAGE
223
253
  - lib/generators/federails/install/install_generator.rb
224
254
  - lib/generators/federails/install/templates/federails.rb
@@ -233,6 +263,7 @@ metadata:
233
263
  homepage_uri: https://experimentslabs.com
234
264
  source_code_uri: https://gitlab.com/experimentslabs/federails/
235
265
  changelog_uri: https://gitlab.com/experimentslabs/federails/-/blob/main/CHANGELOG.md
266
+ post_install_message:
236
267
  rdoc_options: []
237
268
  require_paths:
238
269
  - lib
@@ -247,7 +278,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
278
  - !ruby/object:Gem::Version
248
279
  version: '0'
249
280
  requirements: []
250
- rubygems_version: 3.6.5
281
+ rubygems_version: 3.3.7
282
+ signing_key:
251
283
  specification_version: 4
252
284
  summary: An ActivityPub engine for Ruby on Rails
253
285
  test_files: []
@@ -1,18 +0,0 @@
1
- json.set!('@context', 'https://www.w3.org/ns/activitystreams')
2
- collection_id = @actor.outbox_url
3
- json.id collection_id
4
- json.type 'OrderedCollectionPage'
5
- json.totalItems @total_activities
6
- json.first collection_id
7
- json.last @activities.total_pages == 1 ? Federails::Engine.routes.url_helpers.server_actor_outbox_url(@actor) : Federails::Engine.routes.url_helpers.server_actor_outbox_url(@actor, page: @activities.total_pages)
8
- json.current do |j|
9
- j.type 'OrderedCollectionPage'
10
- j.id @activities.current_page == 1 ? Federails::Engine.routes.url_helpers.server_actor_outbox_url(@actor) : Federails::Engine.routes.url_helpers.server_actor_outbox_url(@actor, page: @activities.current_page)
11
- j.partOf collection_id
12
- j.next @activities.next_page
13
- j.prev @activities.prev_page
14
- j.totalItems @total_activities
15
- j.orderedItems do
16
- json.array! @activities, partial: 'federails/server/activities/activity', as: :activity, context: false
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- json.set!('@context', 'https://www.w3.org/ns/activitystreams')
2
- collection_id = @actor.followers_url
3
- json.id collection_id
4
- json.type 'OrderedCollectionPage'
5
- json.totalItems @total_actors
6
- json.first Federails::Engine.routes.url_helpers.followers_server_actor_url(@actor)
7
- json.last @actors.total_pages == 1 ? Federails::Engine.routes.url_helpers.followers_server_actor_url(@actor) : Federails::Engine.routes.url_helpers.followers_server_actor_url(@actor, page: @actors.total_pages)
8
- json.current do |j|
9
- j.type 'OrderedCollectionPage'
10
- j.id @actors.current_page == 1 ? Federails::Engine.routes.url_helpers.followers_server_actor_url(@actor) : Federails::Engine.routes.url_helpers.followers_server_actor_url(@actor, page: @actors.current_page)
11
- j.partOf collection_id
12
- j.next @actors.next_page
13
- j.prev @actors.prev_page
14
- j.totalItems @total_actors
15
- j.orderedItems do
16
- json.array! @actors.map(&:federated_url)
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- json.set!('@context', 'https://www.w3.org/ns/activitystreams')
2
- collection_id = @actor.followings_url
3
- json.id collection_id
4
- json.type 'OrderedCollectionPage'
5
- json.totalItems @total_actors
6
- json.first Federails::Engine.routes.url_helpers.following_server_actor_url(@actor)
7
- json.last @actors.total_pages == 1 ? Federails::Engine.routes.url_helpers.following_server_actor_url(@actor) : Federails::Engine.routes.url_helpers.following_server_actor_url(@actor, page: @actors.total_pages)
8
- json.current do |j|
9
- j.type 'OrderedCollectionPage'
10
- j.id @actors.current_page == 1 ? Federails::Engine.routes.url_helpers.following_server_actor_url(@actor) : Federails::Engine.routes.url_helpers.following_server_actor_url(@actor, page: @actors.current_page)
11
- j.partOf collection_id
12
- j.next @actors.next_page
13
- j.prev @actors.prev_page
14
- j.totalItems @total_actors
15
- j.orderedItems do
16
- json.array! @actors.map(&:federated_url)
17
- end
18
- end