federails 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +171 -7
  3. data/Rakefile +5 -5
  4. data/app/controllers/federails/application_controller.rb +19 -0
  5. data/app/controllers/federails/client/activities_controller.rb +21 -0
  6. data/app/controllers/federails/client/actors_controller.rb +37 -0
  7. data/app/controllers/federails/client/followings_controller.rb +93 -0
  8. data/app/controllers/federails/server/activities_controller.rb +65 -0
  9. data/app/controllers/federails/server/actors_controller.rb +34 -0
  10. data/app/controllers/federails/server/followings_controller.rb +18 -0
  11. data/app/controllers/federails/server/nodeinfo_controller.rb +22 -0
  12. data/app/controllers/federails/server/server_controller.rb +17 -0
  13. data/app/controllers/federails/server/web_finger_controller.rb +38 -0
  14. data/app/jobs/federails/notify_inbox_job.rb +12 -0
  15. data/app/mailers/federails/application_mailer.rb +2 -2
  16. data/app/models/concerns/federails/entity.rb +46 -0
  17. data/app/models/federails/activity.rb +40 -0
  18. data/app/models/federails/actor.rb +152 -0
  19. data/app/models/federails/following.rb +43 -0
  20. data/app/policies/federails/client/activity_policy.rb +6 -0
  21. data/app/policies/federails/client/actor_policy.rb +15 -0
  22. data/app/policies/federails/client/following_policy.rb +35 -0
  23. data/app/policies/federails/federails_policy.rb +59 -0
  24. data/app/policies/federails/server/activity_policy.rb +6 -0
  25. data/app/policies/federails/server/actor_policy.rb +23 -0
  26. data/app/policies/federails/server/following_policy.rb +6 -0
  27. data/app/views/federails/client/activities/_activity.html.erb +5 -0
  28. data/app/views/federails/client/activities/_activity.json.jbuilder +1 -0
  29. data/app/views/federails/client/activities/_index.json.jbuilder +1 -0
  30. data/app/views/federails/client/activities/feed.html.erb +4 -0
  31. data/app/views/federails/client/activities/feed.json.jbuilder +1 -0
  32. data/app/views/federails/client/activities/index.html.erb +5 -0
  33. data/app/views/federails/client/activities/index.json.jbuilder +1 -0
  34. data/app/views/federails/client/actors/_actor.json.jbuilder +14 -0
  35. data/app/views/federails/client/actors/_lookup_form.html.erb +5 -0
  36. data/app/views/federails/client/actors/index.html.erb +24 -0
  37. data/app/views/federails/client/actors/index.json.jbuilder +1 -0
  38. data/app/views/federails/client/actors/show.html.erb +100 -0
  39. data/app/views/federails/client/actors/show.json.jbuilder +1 -0
  40. data/app/views/federails/client/followings/_follow.html.erb +4 -0
  41. data/app/views/federails/client/followings/_follower.html.erb +7 -0
  42. data/app/views/federails/client/followings/_following.json.jbuilder +1 -0
  43. data/app/views/federails/client/followings/_form.html.erb +21 -0
  44. data/app/views/federails/client/followings/index.html.erb +29 -0
  45. data/app/views/federails/client/followings/index.json.jbuilder +1 -0
  46. data/app/views/federails/client/followings/show.html.erb +21 -0
  47. data/app/views/federails/client/followings/show.json.jbuilder +1 -0
  48. data/app/views/federails/server/activities/_activity.json.jbuilder +9 -0
  49. data/app/views/federails/server/activities/outbox.json.jbuilder +18 -0
  50. data/app/views/federails/server/activities/show.json.jbuilder +1 -0
  51. data/app/views/federails/server/actors/_actor.json.jbuilder +11 -0
  52. data/app/views/federails/server/actors/followers.json.jbuilder +18 -0
  53. data/app/views/federails/server/actors/following.json.jbuilder +18 -0
  54. data/app/views/federails/server/actors/show.json.jbuilder +1 -0
  55. data/app/views/federails/server/followings/_following.json.jbuilder +7 -0
  56. data/app/views/federails/server/followings/show.json.jbuilder +1 -0
  57. data/app/views/federails/server/nodeinfo/index.json.jbuilder +6 -0
  58. data/app/views/federails/server/nodeinfo/show.json.jbuilder +19 -0
  59. data/app/views/federails/server/web_finger/find.json.jbuilder +20 -0
  60. data/app/views/federails/server/web_finger/host_meta.xml.erb +5 -0
  61. data/config/routes.rb +43 -0
  62. data/db/migrate/20200712133150_create_federails_actors.rb +24 -0
  63. data/db/migrate/20200712143127_create_federails_followings.rb +14 -0
  64. data/db/migrate/20200712174938_create_federails_activities.rb +11 -0
  65. data/db/migrate/20240731145400_change_actor_entity_rel_to_polymorphic.rb +11 -0
  66. data/lib/federails/configuration.rb +88 -0
  67. data/lib/federails/engine.rb +6 -0
  68. data/lib/federails/utils/host.rb +54 -0
  69. data/lib/federails/version.rb +1 -1
  70. data/lib/federails.rb +33 -3
  71. data/lib/fediverse/inbox.rb +71 -0
  72. data/lib/fediverse/notifier.rb +21 -0
  73. data/lib/fediverse/request.rb +38 -0
  74. data/lib/fediverse/webfinger.rb +93 -0
  75. data/lib/generators/federails/install/USAGE +9 -0
  76. data/lib/generators/federails/install/install_generator.rb +10 -0
  77. data/lib/generators/federails/install/templates/federails.rb +1 -0
  78. data/lib/generators/federails/install/templates/federails.yml +23 -0
  79. data/lib/tasks/factory_bot.rake +15 -0
  80. metadata +165 -10
  81. data/app/views/layouts/federails/application.html.erb +0 -15
@@ -0,0 +1 @@
1
+ json.partial! 'federails/client/followings/following', following: @following
@@ -0,0 +1,9 @@
1
+ context = true unless context == false
2
+ json.set! '@context', 'https://www.w3.org/ns/activitystreams' if context
3
+
4
+ json.id Federails::Engine.routes.url_helpers.server_actor_activity_url activity.actor, activity
5
+ json.type activity.action
6
+ json.actor activity.actor.federated_url
7
+ json.to ['https://www.w3.org/ns/activitystreams#Public']
8
+ json.cc [activity.actor.followers_url]
9
+ json.object activity.entity.federated_url
@@ -0,0 +1,18 @@
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
@@ -0,0 +1 @@
1
+ json.partial! 'federails/server/activities/activity', activity: @activity
@@ -0,0 +1,11 @@
1
+ json.set! '@context', 'https://www.w3.org/ns/activitystreams'
2
+
3
+ json.id actor.federated_url
4
+ json.name actor.name
5
+ json.type actor.entity_configuration[:actor_type]
6
+ json.preferredUsername actor.username
7
+ json.inbox actor.inbox_url
8
+ json.outbox actor.outbox_url
9
+ json.followers actor.followers_url
10
+ json.following actor.followings_url
11
+ json.url actor.profile_url
@@ -0,0 +1,18 @@
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
@@ -0,0 +1,18 @@
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
@@ -0,0 +1 @@
1
+ json.partial! 'federails/server/actors/actor', actor: @actor
@@ -0,0 +1,7 @@
1
+ context = true unless context == false
2
+ json.set! '@context', 'https://www.w3.org/ns/activitystreams' if context
3
+
4
+ json.id following.federated_url
5
+ json.type 'Follow'
6
+ json.actor following.actor.federated_url
7
+ json.object following.target_actor.federated_url
@@ -0,0 +1 @@
1
+ json.partial! 'federails/server/followings/following', following: @following
@@ -0,0 +1,6 @@
1
+ json.links [
2
+ {
3
+ rel: 'https://nodeinfo.diaspora.software/ns/schema/2.0',
4
+ href: show_node_info_url,
5
+ },
6
+ ]
@@ -0,0 +1,19 @@
1
+ json.version '2.0'
2
+ # FIXME: Use configuration values when created
3
+ json.software name: Federails::Configuration.app_name,
4
+ version: Federails::Configuration.app_version
5
+ json.protocols [
6
+ 'activitypub',
7
+ ]
8
+ # FIXME: When server is in good shape: update outbounds
9
+ # http://nodeinfo.diaspora.software/ns/schema/2.0 for possible values
10
+ json.services inbound: [],
11
+ outbound: []
12
+ # FIXME: Don't hardcode this
13
+ json.openRegistrations true
14
+ json.usage users: {
15
+ total: @total,
16
+ activeMonth: @active_month,
17
+ activeHalfyear: @active_halfyear,
18
+ }
19
+ json.metadata({})
@@ -0,0 +1,20 @@
1
+ json.subject params[:resource]
2
+
3
+ links = [
4
+ # Federation actor URL
5
+ {
6
+ rel: 'self',
7
+ type: 'application/activity+json',
8
+ href: @user.actor.federated_url,
9
+ },
10
+ ]
11
+
12
+ # User profile URL if configured
13
+ # TODO: Add a profile controller/action in dummy to test this
14
+ if @user.actor.profile_url
15
+ links.push rel: 'https://webfinger.net/rel/profile-page',
16
+ type: 'text/html',
17
+ href: @user.actor.profile_url
18
+ end
19
+
20
+ json.links links
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <XRD
3
+ xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
4
+ <Link rel="lrdd" type="application/xrd+xml" template="<%= webfinger_url %>?resource={uri}" />
5
+ </XRD>
data/config/routes.rb CHANGED
@@ -1,2 +1,45 @@
1
1
  Federails::Engine.routes.draw do
2
+ if Federails.configuration.enable_discovery
3
+ scope path: '/' do
4
+ get '/.well-known/webfinger', to: 'server/web_finger#find', as: :webfinger
5
+ get '/.well-known/host-meta', to: 'server/web_finger#host_meta', as: :host_meta
6
+ get '/.well-known/nodeinfo', to: 'server/nodeinfo#index', as: :node_info
7
+ get '/nodeinfo/2.0', to: 'server/nodeinfo#show', as: :show_node_info
8
+ end
9
+ end
10
+
11
+ if Federails.configuration.client_routes_path
12
+ scope Federails.configuration.client_routes_path, module: :client, as: :client do
13
+ resources :activities, only: [:index, :feed]
14
+ resources :actors, only: [:index, :show] do
15
+ collection do
16
+ get :lookup, to: 'actors#lookup'
17
+ end
18
+ resources :activities, only: [:index]
19
+ end
20
+ get :feed, to: 'activities#feed'
21
+ resources :followings, only: [:create, :destroy] do
22
+ collection do
23
+ post :follow, to: 'followings#follow'
24
+ end
25
+
26
+ member do
27
+ put :accept, to: 'followings#accept'
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ scope Federails.configuration.server_routes_path, module: :server, as: :server do
34
+ resources :actors, only: [:show] do
35
+ member do
36
+ get :followers
37
+ get :following
38
+ end
39
+ get :outbox, to: 'activities#outbox'
40
+ post :inbox, to: 'activities#create'
41
+ resources :activities, only: [:show]
42
+ resources :followings, only: [:show]
43
+ end
44
+ end
2
45
  end
@@ -0,0 +1,24 @@
1
+ class CreateFederailsActors < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :federails_actors do |t|
4
+ t.string :name
5
+ t.string :federated_url
6
+ t.string :username
7
+ t.string :server
8
+ t.string :inbox_url
9
+ t.string :outbox_url
10
+ t.string :followers_url
11
+ t.string :followings_url
12
+ t.string :profile_url
13
+
14
+ t.references :user, null: true, foreign_key: { to_table: Federails.configuration.user_table }
15
+
16
+ t.timestamps
17
+ t.index :federated_url, unique: true
18
+ end
19
+ remove_foreign_key :federails_actors, :users if foreign_key_exists?(:federails_actors, :users)
20
+ remove_index :federails_actors, :user_id
21
+ add_index :federails_actors, :user_id, unique: true
22
+ add_foreign_key :federails_actors, :users
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ class CreateFederailsFollowings < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :federails_followings do |t|
4
+ t.references :actor, null: false, foreign_key: { to_table: :federails_actors }
5
+ t.references :target_actor, null: false, foreign_key: { to_table: :federails_actors }
6
+ t.integer :status, default: 0
7
+ t.string :federated_url
8
+
9
+ t.timestamps
10
+
11
+ t.index [:actor_id, :target_actor_id], unique: true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ class CreateFederailsActivities < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :federails_activities do |t|
4
+ t.references :entity, polymorphic: true, null: false
5
+ t.string :action, null: false, default: nil
6
+ t.references :actor, null: false, foreign_key: { to_table: :federails_actors }
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class ChangeActorEntityRelToPolymorphic < ActiveRecord::Migration[7.0]
2
+ def change
3
+ remove_foreign_key :federails_actors, column: :user_id, to_table: Federails::Configuration.user_table
4
+ remove_index :federails_actors, :user_id, unique: true
5
+ change_table :federails_actors do |t|
6
+ t.rename :user_id, :entity_id
7
+ t.string :entity_type, null: true, default: Federails::Configuration.user_class&.demodulize
8
+ t.index [:entity_type, :entity_id], name: 'index_federails_actors_on_entity', unique: true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,88 @@
1
+ module Federails
2
+ # rubocop:disable Style/ClassVars
3
+ module Configuration
4
+ # Application name, used in well-known and nodeinfo endpoints
5
+ mattr_accessor :app_name
6
+ @@app_name = nil
7
+
8
+ # Application version, used in well-known and nodeinfo endpoints
9
+ mattr_accessor :app_version
10
+ @@app_version = nil
11
+
12
+ # Force https urls in various rendered content (currently in webfinger views)
13
+ mattr_accessor :force_ssl
14
+ @@force_ssl = nil
15
+
16
+ # Site hostname
17
+ mattr_reader :site_host
18
+ @@site_host = nil
19
+
20
+ # Site port
21
+ mattr_reader :site_port
22
+ @@site_port = nil
23
+
24
+ # Whether to enable ".well-known" and "nodeinfo" endpoints
25
+ mattr_accessor :enable_discovery
26
+ @@enable_discovery = true
27
+
28
+ # Site port
29
+ mattr_accessor :app_layout
30
+ @@app_layout = nil
31
+
32
+ # User class name
33
+ # @deprecated Kept for upgrade compatibility only
34
+ mattr_accessor :user_class
35
+ @@user_class = '::User'
36
+
37
+ # Route path for the federation URLs (to "Federails::Server::*" controllers)
38
+ mattr_accessor :server_routes_path
39
+ @@server_routes_path = :federation
40
+
41
+ # Route path for the webapp URLs (to "Federails::Client::*" controllers)
42
+ mattr_accessor :client_routes_path
43
+ @@client_routes_path = :app
44
+
45
+ # Method to use for links to user profiles
46
+ # @deprecated Set profile_url_method option on acts_as_federails_actor instead
47
+ mattr_accessor :user_profile_url_method
48
+ @@user_profile_url_method = nil
49
+
50
+ # Attribute in the user model to use as the user's name
51
+ # @deprecated Set name_field option on acts_as_federails_actor instead
52
+ #
53
+ # It only have sense if you have a separate username attribute
54
+ mattr_accessor :user_name_field
55
+ @@user_name_field = nil
56
+
57
+ # Attribute in the user model to use as the username for local actors
58
+ # @deprecated Set username_field option on acts_as_federails_actor instead
59
+ mattr_accessor :user_username_field
60
+ @@user_username_field = :id
61
+
62
+ ##
63
+ # @return [String] Table used for user model
64
+ # @deprecated Kept for upgrade compatibility only
65
+ def self.user_table
66
+ @@user_class&.constantize&.table_name
67
+ end
68
+
69
+ def self.site_host=(value)
70
+ @@site_host = value
71
+ Federails::Engine.routes.default_url_options[:host] = value
72
+ end
73
+
74
+ def self.site_port=(value)
75
+ @@site_port = value
76
+ Federails::Engine.routes.default_url_options[:port] = value
77
+ end
78
+
79
+ # List of entity types
80
+ mattr_reader :entity_types
81
+ @@entity_types = {}
82
+
83
+ def self.register_entity(klass, config = {})
84
+ @@entity_types[klass.name] = config.merge(class: klass)
85
+ end
86
+ end
87
+ # rubocop:enable Style/ClassVars
88
+ end
@@ -1,5 +1,11 @@
1
1
  module Federails
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Federails
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_bot
8
+ g.factory_bot dir: 'spec/factories'
9
+ end
4
10
  end
5
11
  end
@@ -0,0 +1,54 @@
1
+ module Federails
2
+ module Utils
3
+ class Host
4
+ class << self
5
+ COMMON_PORTS = [80, 443].freeze
6
+
7
+ ##
8
+ # @return [String] Host and port of the current instance
9
+ def localhost
10
+ uri = URI.parse Federails.configuration.site_host
11
+ host_and_port (uri.host || 'localhost'), Federails.configuration.site_port
12
+ end
13
+
14
+ ##
15
+ # Checks if the given URL points somewhere on current instance
16
+ #
17
+ # @param url [String] URL to check
18
+ #
19
+ # @return [true, false]
20
+ def local_url?(url)
21
+ uri = URI.parse url
22
+ host = host_and_port uri.host, uri.port
23
+ localhost == host
24
+ end
25
+
26
+ ##
27
+ # Gets the route on the current instance, or nil
28
+ #
29
+ # @param url [String] URL to check
30
+ #
31
+ # @return [ActionDispatch::Routing::RouteSet, nil] nil when URL do not match a route
32
+ def local_route(url)
33
+ return nil unless local_url? url
34
+
35
+ Rails.application.routes.recognize_path(url)
36
+ rescue ActionController::RoutingError
37
+ nil
38
+ end
39
+
40
+ private
41
+
42
+ def host_and_port(host, port)
43
+ port_string = if port.present? && COMMON_PORTS.exclude?(port)
44
+ ":#{port}"
45
+ else
46
+ ''
47
+ end
48
+
49
+ "#{host}#{port_string}"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Federails
2
- VERSION = "0.0.1"
2
+ VERSION = '0.1.0'.freeze
3
3
  end
data/lib/federails.rb CHANGED
@@ -1,6 +1,36 @@
1
- require "federails/version"
2
- require "federails/engine"
1
+ require 'federails/version'
2
+ require 'federails/engine'
3
+ require 'federails/configuration'
3
4
 
5
+ # rubocop:disable Style/ClassVars
4
6
  module Federails
5
- # Your code goes here...
7
+ mattr_reader :configuration
8
+ @@configuration = Configuration
9
+
10
+ # Make factories available
11
+ config.factory_bot.definition_file_paths += [File.expand_path('spec/factories', __dir__)] if defined?(FactoryBotRails)
12
+
13
+ def self.configure
14
+ yield @@configuration
15
+ end
16
+
17
+ def self.config_from(name) # rubocop:disable Metrics/MethodLength
18
+ config = Rails.application.config_for name
19
+ [
20
+ :app_name,
21
+ :app_version,
22
+ :force_ssl,
23
+ :site_host,
24
+ :site_port,
25
+ :enable_discovery,
26
+ :app_layout,
27
+ :user_class, # @deprecated
28
+ :server_routes_path,
29
+ :client_routes_path,
30
+ :user_profile_url_method, # @deprecated
31
+ :user_name_field, # @deprecated
32
+ :user_username_field, # @deprecated
33
+ ].each { |key| Configuration.send :"#{key}=", config[key] }
34
+ end
6
35
  end
36
+ # rubocop:enable Style/ClassVars
@@ -0,0 +1,71 @@
1
+ require 'fediverse/request'
2
+
3
+ module Fediverse
4
+ class Inbox
5
+ class << self
6
+ def dispatch_request(payload)
7
+ case payload['type']
8
+ when 'Create'
9
+ handle_create_request payload
10
+ when 'Follow'
11
+ handle_create_follow_request payload
12
+ when 'Accept'
13
+ handle_accept_request payload
14
+ when 'Undo'
15
+ handle_undo_request payload
16
+ else
17
+ # FIXME: Fails silently
18
+ # raise NotImplementedError
19
+ Rails.logger.debug { "Unhandled activity type: #{payload['type']}" }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def handle_create_request(payload)
26
+ activity = Request.get(payload['object'])
27
+ case activity['type']
28
+ when 'Follow'
29
+ handle_create_follow_request activity
30
+ when 'Note'
31
+ handle_create_note activity
32
+ end
33
+ end
34
+
35
+ def handle_create_follow_request(activity)
36
+ actor = Federails::Actor.find_or_create_by_object activity['actor']
37
+ target_actor = Federails::Actor.find_or_create_by_object activity['object']
38
+
39
+ Federails::Following.create! actor: actor, target_actor: target_actor, federated_url: activity['id']
40
+ end
41
+
42
+ def handle_create_note(activity)
43
+ actor = Federails::Actor.find_or_create_by_object activity['attributedTo']
44
+ Note.create! actor: actor, content: activity['content'], federated_url: activity['id']
45
+ end
46
+
47
+ def handle_accept_request(payload)
48
+ activity = Request.get(payload['object'])
49
+ raise "Can't accept things that are not Follow" unless activity['type'] == 'Follow'
50
+
51
+ actor = Federails::Actor.find_or_create_by_object activity['actor']
52
+ target_actor = Federails::Actor.find_or_create_by_object activity['object']
53
+ raise 'Follow not accepted by target actor but by someone else' if payload['actor'] != target_actor.federated_url
54
+
55
+ follow = Federails::Following.find_by actor: actor, target_actor: target_actor
56
+ follow.accept!
57
+ end
58
+
59
+ def handle_undo_request(payload)
60
+ activity = payload['object']
61
+ raise "Can't undo things that are not Follow" unless activity['type'] == 'Follow'
62
+
63
+ actor = Federails::Actor.find_or_create_by_object activity['actor']
64
+ target_actor = Federails::Actor.find_or_create_by_object activity['object']
65
+
66
+ follow = Federails::Following.find_by actor: actor, target_actor: target_actor
67
+ follow&.destroy
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,21 @@
1
+ module Fediverse
2
+ class Notifier
3
+ class << self
4
+ def post_to_inboxes(activity)
5
+ actors = activity.recipients
6
+
7
+ Rails.logger.debug('Nobody to notice') && return if actors.count.zero?
8
+
9
+ message = Federails::ApplicationController.renderer.new.render(
10
+ template: 'federails/server/activities/show',
11
+ assigns: { activity: activity },
12
+ format: :json
13
+ )
14
+ actors.each do |actor|
15
+ Rails.logger.debug { "Sending activity ##{activity.id} to #{actor.inbox_url}" }
16
+ Faraday.post actor.inbox_url, message, 'Content-Type' => 'application/json', 'Accept' => 'application/json'
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ require 'json/ld'
2
+
3
+ module Fediverse
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
+ def get
15
+ Rails.logger.debug { "GET #{@id}" }
16
+ @response = Faraday.get(@id, nil, BASE_HEADERS)
17
+ response_to_json
18
+ end
19
+
20
+ class << self
21
+ def get(id)
22
+ new(id).get
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def response_to_json
29
+ begin
30
+ body = JSON.parse @response.body
31
+ rescue JSON::ParserError
32
+ return
33
+ end
34
+
35
+ JSON::LD::API.compact body, body['@context']
36
+ end
37
+ end
38
+ end