federails 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +171 -7
- data/Rakefile +5 -5
- data/app/controllers/federails/application_controller.rb +19 -0
- data/app/controllers/federails/client/activities_controller.rb +21 -0
- data/app/controllers/federails/client/actors_controller.rb +37 -0
- data/app/controllers/federails/client/followings_controller.rb +93 -0
- data/app/controllers/federails/server/activities_controller.rb +65 -0
- data/app/controllers/federails/server/actors_controller.rb +34 -0
- data/app/controllers/federails/server/followings_controller.rb +18 -0
- data/app/controllers/federails/server/nodeinfo_controller.rb +22 -0
- data/app/controllers/federails/server/server_controller.rb +17 -0
- data/app/controllers/federails/server/web_finger_controller.rb +38 -0
- data/app/jobs/federails/notify_inbox_job.rb +12 -0
- data/app/mailers/federails/application_mailer.rb +2 -2
- data/app/models/concerns/federails/entity.rb +46 -0
- data/app/models/federails/activity.rb +40 -0
- data/app/models/federails/actor.rb +152 -0
- data/app/models/federails/following.rb +43 -0
- data/app/policies/federails/client/activity_policy.rb +6 -0
- data/app/policies/federails/client/actor_policy.rb +15 -0
- data/app/policies/federails/client/following_policy.rb +35 -0
- data/app/policies/federails/federails_policy.rb +59 -0
- data/app/policies/federails/server/activity_policy.rb +6 -0
- data/app/policies/federails/server/actor_policy.rb +23 -0
- data/app/policies/federails/server/following_policy.rb +6 -0
- data/app/views/federails/client/activities/_activity.html.erb +5 -0
- data/app/views/federails/client/activities/_activity.json.jbuilder +1 -0
- data/app/views/federails/client/activities/_index.json.jbuilder +1 -0
- data/app/views/federails/client/activities/feed.html.erb +4 -0
- data/app/views/federails/client/activities/feed.json.jbuilder +1 -0
- data/app/views/federails/client/activities/index.html.erb +5 -0
- data/app/views/federails/client/activities/index.json.jbuilder +1 -0
- data/app/views/federails/client/actors/_actor.json.jbuilder +14 -0
- data/app/views/federails/client/actors/_lookup_form.html.erb +5 -0
- data/app/views/federails/client/actors/index.html.erb +24 -0
- data/app/views/federails/client/actors/index.json.jbuilder +1 -0
- data/app/views/federails/client/actors/show.html.erb +100 -0
- data/app/views/federails/client/actors/show.json.jbuilder +1 -0
- data/app/views/federails/client/followings/_follow.html.erb +4 -0
- data/app/views/federails/client/followings/_follower.html.erb +7 -0
- data/app/views/federails/client/followings/_following.json.jbuilder +1 -0
- data/app/views/federails/client/followings/_form.html.erb +21 -0
- data/app/views/federails/client/followings/index.html.erb +29 -0
- data/app/views/federails/client/followings/index.json.jbuilder +1 -0
- data/app/views/federails/client/followings/show.html.erb +21 -0
- data/app/views/federails/client/followings/show.json.jbuilder +1 -0
- data/app/views/federails/server/activities/_activity.json.jbuilder +9 -0
- data/app/views/federails/server/activities/outbox.json.jbuilder +18 -0
- data/app/views/federails/server/activities/show.json.jbuilder +1 -0
- data/app/views/federails/server/actors/_actor.json.jbuilder +11 -0
- data/app/views/federails/server/actors/followers.json.jbuilder +18 -0
- data/app/views/federails/server/actors/following.json.jbuilder +18 -0
- data/app/views/federails/server/actors/show.json.jbuilder +1 -0
- data/app/views/federails/server/followings/_following.json.jbuilder +7 -0
- data/app/views/federails/server/followings/show.json.jbuilder +1 -0
- data/app/views/federails/server/nodeinfo/index.json.jbuilder +6 -0
- data/app/views/federails/server/nodeinfo/show.json.jbuilder +19 -0
- data/app/views/federails/server/web_finger/find.json.jbuilder +20 -0
- data/app/views/federails/server/web_finger/host_meta.xml.erb +5 -0
- data/config/routes.rb +43 -0
- data/db/migrate/20200712133150_create_federails_actors.rb +24 -0
- data/db/migrate/20200712143127_create_federails_followings.rb +14 -0
- data/db/migrate/20200712174938_create_federails_activities.rb +11 -0
- data/db/migrate/20240731145400_change_actor_entity_rel_to_polymorphic.rb +11 -0
- data/lib/federails/configuration.rb +88 -0
- data/lib/federails/engine.rb +6 -0
- data/lib/federails/utils/host.rb +54 -0
- data/lib/federails/version.rb +1 -1
- data/lib/federails.rb +33 -3
- data/lib/fediverse/inbox.rb +71 -0
- data/lib/fediverse/notifier.rb +21 -0
- data/lib/fediverse/request.rb +38 -0
- data/lib/fediverse/webfinger.rb +93 -0
- data/lib/generators/federails/install/USAGE +9 -0
- data/lib/generators/federails/install/install_generator.rb +10 -0
- data/lib/generators/federails/install/templates/federails.rb +1 -0
- data/lib/generators/federails/install/templates/federails.yml +23 -0
- data/lib/tasks/factory_bot.rake +15 -0
- metadata +165 -10
- 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 @@
|
|
1
|
+
json.partial! 'federails/server/followings/following', following: @following
|
@@ -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
|
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
|
data/lib/federails/engine.rb
CHANGED
@@ -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
|
data/lib/federails/version.rb
CHANGED
data/lib/federails.rb
CHANGED
@@ -1,6 +1,36 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'federails/version'
|
2
|
+
require 'federails/engine'
|
3
|
+
require 'federails/configuration'
|
3
4
|
|
5
|
+
# rubocop:disable Style/ClassVars
|
4
6
|
module Federails
|
5
|
-
|
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
|