federails 0.0.1 → 0.1.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.
- 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
|