federails 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +182 -7
  3. data/Rakefile +5 -5
  4. data/app/controllers/federails/application_controller.rb +23 -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 +101 -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 +19 -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/helpers/federails/application_helper.rb +8 -0
  15. data/app/jobs/federails/notify_inbox_job.rb +12 -0
  16. data/app/mailers/federails/application_mailer.rb +2 -2
  17. data/app/models/concerns/federails/entity.rb +57 -0
  18. data/app/models/concerns/federails/has_uuid.rb +35 -0
  19. data/app/models/federails/activity.rb +35 -0
  20. data/app/models/federails/actor.rb +189 -0
  21. data/app/models/federails/following.rb +52 -0
  22. data/app/policies/federails/client/activity_policy.rb +6 -0
  23. data/app/policies/federails/client/actor_policy.rb +15 -0
  24. data/app/policies/federails/client/following_policy.rb +35 -0
  25. data/app/policies/federails/federails_policy.rb +59 -0
  26. data/app/policies/federails/server/activity_policy.rb +6 -0
  27. data/app/policies/federails/server/actor_policy.rb +23 -0
  28. data/app/policies/federails/server/following_policy.rb +6 -0
  29. data/app/views/federails/client/activities/_activity.html.erb +5 -0
  30. data/app/views/federails/client/activities/_activity.json.jbuilder +1 -0
  31. data/app/views/federails/client/activities/_index.json.jbuilder +1 -0
  32. data/app/views/federails/client/activities/feed.html.erb +4 -0
  33. data/app/views/federails/client/activities/feed.json.jbuilder +1 -0
  34. data/app/views/federails/client/activities/index.html.erb +5 -0
  35. data/app/views/federails/client/activities/index.json.jbuilder +1 -0
  36. data/app/views/federails/client/actors/_actor.json.jbuilder +14 -0
  37. data/app/views/federails/client/actors/_lookup_form.html.erb +5 -0
  38. data/app/views/federails/client/actors/index.html.erb +24 -0
  39. data/app/views/federails/client/actors/index.json.jbuilder +1 -0
  40. data/app/views/federails/client/actors/show.html.erb +100 -0
  41. data/app/views/federails/client/actors/show.json.jbuilder +1 -0
  42. data/app/views/federails/client/followings/_follow.html.erb +4 -0
  43. data/app/views/federails/client/followings/_follower.html.erb +7 -0
  44. data/app/views/federails/client/followings/_following.json.jbuilder +1 -0
  45. data/app/views/federails/client/followings/_form.html.erb +21 -0
  46. data/app/views/federails/client/followings/index.html.erb +29 -0
  47. data/app/views/federails/client/followings/index.json.jbuilder +1 -0
  48. data/app/views/federails/client/followings/show.html.erb +21 -0
  49. data/app/views/federails/client/followings/show.json.jbuilder +1 -0
  50. data/app/views/federails/server/activities/_activity.activitypub.jbuilder +14 -0
  51. data/app/views/federails/server/activities/outbox.activitypub.jbuilder +18 -0
  52. data/app/views/federails/server/activities/show.activitypub.jbuilder +1 -0
  53. data/app/views/federails/server/actors/_actor.activitypub.jbuilder +21 -0
  54. data/app/views/federails/server/actors/followers.activitypub.jbuilder +18 -0
  55. data/app/views/federails/server/actors/following.activitypub.jbuilder +18 -0
  56. data/app/views/federails/server/actors/show.activitypub.jbuilder +1 -0
  57. data/app/views/federails/server/followings/_following.activitypub.jbuilder +7 -0
  58. data/app/views/federails/server/followings/show.activitypub.jbuilder +1 -0
  59. data/app/views/federails/server/nodeinfo/index.nodeinfo.jbuilder +6 -0
  60. data/app/views/federails/server/nodeinfo/show.nodeinfo.jbuilder +19 -0
  61. data/app/views/federails/server/web_finger/find.jrd.jbuilder +24 -0
  62. data/app/views/federails/server/web_finger/host_meta.xrd.erb +5 -0
  63. data/config/initializers/mime_types.rb +21 -0
  64. data/config/routes.rb +43 -0
  65. data/db/migrate/20200712133150_create_federails_actors.rb +24 -0
  66. data/db/migrate/20200712143127_create_federails_followings.rb +14 -0
  67. data/db/migrate/20200712174938_create_federails_activities.rb +11 -0
  68. data/db/migrate/20240731145400_change_actor_entity_rel_to_polymorphic.rb +11 -0
  69. data/db/migrate/20241002094500_add_uuids.rb +13 -0
  70. data/db/migrate/20241002094501_add_keypair_to_actors.rb +8 -0
  71. data/lib/federails/configuration.rb +92 -0
  72. data/lib/federails/engine.rb +6 -0
  73. data/lib/federails/utils/host.rb +54 -0
  74. data/lib/federails/version.rb +1 -1
  75. data/lib/federails.rb +34 -3
  76. data/lib/fediverse/inbox.rb +71 -0
  77. data/lib/fediverse/notifier.rb +60 -0
  78. data/lib/fediverse/request.rb +38 -0
  79. data/lib/fediverse/signature.rb +49 -0
  80. data/lib/fediverse/webfinger.rb +117 -0
  81. data/lib/generators/federails/install/USAGE +9 -0
  82. data/lib/generators/federails/install/install_generator.rb +10 -0
  83. data/lib/generators/federails/install/templates/federails.rb +1 -0
  84. data/lib/generators/federails/install/templates/federails.yml +23 -0
  85. data/lib/tasks/factory_bot.rake +15 -0
  86. metadata +170 -10
  87. data/app/views/layouts/federails/application.html.erb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b172418ad3d12834c3c724244a83fe3f30115923c3182608d670d164af0f23c3
4
- data.tar.gz: 3e401d421423152b900733c0ad2729b0f2087532e20e416ee69c58393e378ad9
3
+ metadata.gz: 070da5d2cc3d016475a69f797c6280595618d5f60de95cf7506bb2a1e9cbce4c
4
+ data.tar.gz: a954535a092fa71fdb911f973363d8372b6d56e42b32190394e92026567c6913
5
5
  SHA512:
6
- metadata.gz: e32dfc49219c5719c2ccdb56db44c6bbbcc026374cbf8b3bb9e0545e0f925397988b242407e2b57c4e597a7aca2796d88ce3573c3ba66c85933da9dc6ddb319c
7
- data.tar.gz: f98b93107cbf37b63ebd26d128fc4a1f9b9b929f12054406fc4c2e9ea416ce72f2edb273c59e2ac58241cf36782872b81b83311939b054b64ec79c24d4919c1d
6
+ metadata.gz: 69fba1438beef26a150079c2a39eb185903025dcad3c24f5dfee941d844710d12938cf5546f0dc488f725096a2c4e6f32dfba4d1d3c825b9ba9418f063bb4ec9
7
+ data.tar.gz: 01ef6821f7b1aa9422f013e23ed20b1225f0e630d4925ca81c7490f43d790a1d77a3b93e8d72783d93aee59f1760b48bbdd71a59feca353bca0a1277a6bd03c0
data/README.md CHANGED
@@ -1,10 +1,30 @@
1
1
  # Federails
2
- Short description and motivation.
3
2
 
4
- ## Usage
5
- How to use my plugin.
3
+ Federails is an engine that brings ActivityPub to Ruby on Rails application.
4
+
5
+ ## Community
6
+
7
+ You can join the [matrix chat room](https://matrix.to/#/#federails:matrix.org) to chat with humans.
8
+
9
+ Open issues or feature requests on the [issue tracker](https://gitlab.com/experimentslabs/federails/-/issues)
10
+
11
+ ## Features
12
+
13
+ This engine is meant to be used in Rails applications to add the ability to act as an ActivityPub server.
14
+
15
+ As the project is in an early stage of development we're unable to provide a clean list of what works and what is missing.
16
+
17
+ The general direction is to be able to:
18
+
19
+ - publish and subscribe to any type of content
20
+ - have a discovery endpoint (`webfinger`)
21
+ - have a following/followers system
22
+ - implement all the parts of the (RFC) labelled with **MUST** and **MUST NOT**
23
+ - implement some or all the parts of the RFC labelled with **SHOULD** and **SHOULD NOT**
24
+ - maybe implement the parts of the RFC labelled with **MAY**
6
25
 
7
26
  ## Installation
27
+
8
28
  Add this line to your application's Gemfile:
9
29
 
10
30
  ```ruby
@@ -12,17 +32,172 @@ gem "federails"
12
32
  ```
13
33
 
14
34
  And then execute:
35
+
15
36
  ```bash
16
37
  $ bundle
17
38
  ```
18
39
 
19
- Or install it yourself as:
20
- ```bash
21
- $ gem install federails
40
+ ### Configuration
41
+
42
+ Generate configuration files:
43
+
44
+ ```sh
45
+ bundle exec rails generate federails:install
46
+ ```
47
+
48
+ It creates an initializer and a configuration file:
49
+ - `config/initializers/federails.rb`
50
+ - `config/federails.yml`
51
+
52
+ By default, Federails is configured using `config_from` method, that loads the appropriate YAML file, but you may want
53
+ to configure it differently:
54
+
55
+ ```rb
56
+ # config/initializers/federails.rb
57
+ Federails.configure do |config|
58
+ config.host = 'localhost'
59
+ # ...
60
+ end
61
+ ```
62
+
63
+ For now, refer to [the source code](lib/federails/configuration.rb) for the full list of options.
64
+
65
+ ### Routes
66
+
67
+ Mount the engine on `/`: routes to `/.well-known/*` and `/nodeinfo/*` must be at the root of the site.
68
+ Federails routes are then available under the configured path (`routes_path`):
69
+
70
+ ```rb
71
+ # config/routes.rb
72
+ mount Federails::Engine => '/'
73
+ ```
74
+
75
+ With `routes_path = 'federation'`, routes will be:
76
+
77
+ ```txt
78
+ /.well-known/webfinger(.:format)
79
+ /.well-known/host-meta(.:format)
80
+ /.well-known/nodeinfo(.:format)
81
+ /nodeinfo/2.0(.:format)
82
+ /federation/actors/:id/followers(.:format)
83
+ /federation/actors/:id/following(.:format)
84
+ /federation/actors/:actor_id/outbox(.:format)
85
+ /federation/actors/:actor_id/inbox(.:format)
86
+ /federation/actors/:actor_id/activities/:id(.:format)
87
+ /federation/actors/:actor_id/followings/:id(.:format)
88
+ /federation/actors/:actor_id/notes/:id(.:format)
89
+ /federation/actors/:id(.:format)
90
+ ...
91
+ ```
92
+
93
+ Some routes can be disabled in configuration if you don't want to expose particular features:
94
+
95
+ ```
96
+ Federails.configure do |config|
97
+ # Disable routing for .well-known and nodeinfo
98
+ config.enable_discovery = false
99
+
100
+ # Disable web client UI routes
101
+ config.client_routes_path = nil
102
+ end
103
+ ```
104
+
105
+ #### Remote following
106
+
107
+ By default, remote follow requests (where you press a follow button on another server and get redirected home to complete the follow)
108
+ will use the built-in client paths. If you're not using the client, or want to provide your own user interface, you can set the path like this, assuming that `new_follow_url` is a valid route in your app. A `uri` query parameter template will be automatically appended, you don't need to specify that.
109
+
110
+ ```
111
+ Federails.configure do |config|
112
+ config.remote_follow_url_method = :new_follow_url
113
+ end
114
+ ```
115
+
116
+ ### Migrations
117
+
118
+ Copy the migrations:
119
+
120
+ ```sh
121
+ bundle exec rails federails:install:migrations
122
+ ```
123
+
124
+ ### User model
125
+
126
+ In the ActivityPub world, we refer to _actors_ to represent the thing that publishes or subscribe to _other actors_.
127
+
128
+ Federails provides a concern to include in your "user" model or whatever will publish data:
129
+
130
+ ```rb
131
+ # app/models/user.rb
132
+
133
+ class User < ApplicationRecord
134
+ # Include the concern here:
135
+ include Federails::Entity
136
+
137
+ # Configure field names
138
+ acts_as_federails_actor username_field: :username, name_field: :name, profile_url_method: :user_url
139
+ end
140
+ ```
141
+
142
+ This concern automatically create a `Federails::Actor` after a user creation, as well as the `actor` reference. When adding it to
143
+ an existing model with existing data, you will need to generate the corresponding actors yourself in a migration.
144
+
145
+ Usage example:
146
+
147
+ ```rb
148
+ actor = User.find(1).actor
149
+
150
+ actor.inbox
151
+ actor.outbox
152
+ actor.followers
153
+ actor.following
154
+ #...
22
155
  ```
23
156
 
24
157
  ## Contributing
25
- Contribution directions go here.
158
+
159
+ Contributions are welcome, may it be issues, ideas, code or whatever you want to share. Please note:
160
+
161
+ - This project is _fast forward_ only: we don't do merge commits
162
+ - We adhere to [semantic versioning](). Please update the changelog in your commits
163
+ - We try to adhere to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) principles
164
+ - We _may_ rename your commits before merging them
165
+ - We _may_ split your commits before merging them
166
+
167
+ To contribute:
168
+
169
+ 1. Fork this repository
170
+ 2. Create small commits
171
+ 3. Ideally create small pull requests. Don't hesitate to open them early so we all can follow how it's going
172
+ 4. Get congratulated
173
+
174
+ ### Tooling
175
+
176
+ #### RSpec
177
+
178
+ RSpec is the test suite. Start it with
179
+
180
+ ```sh
181
+ bundle exec rspec
182
+ ```
183
+
184
+ #### Rubocop
185
+
186
+ Rubocop is a linter. Start it with
187
+
188
+ ```sh
189
+ bundle exec rubocop
190
+ ```
191
+
192
+ #### FactoryBot
193
+
194
+ FactoryBot is a factory generator used in tests and development.
195
+ A rake task checks the replayability of the factories and traits:
196
+
197
+ ```sh
198
+ bundle exec app:factory_bot:lint
199
+ ```
26
200
 
27
201
  ## License
202
+
28
203
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
- require "bundler/setup"
1
+ require 'bundler/setup'
2
2
 
3
- APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
- load "rails/tasks/engine.rake"
3
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
4
+ load 'rails/tasks/engine.rake'
5
5
 
6
- load "rails/tasks/statistics.rake"
6
+ load 'rails/tasks/statistics.rake'
7
7
 
8
- require "bundler/gem_tasks"
8
+ require 'bundler/gem_tasks'
@@ -1,4 +1,27 @@
1
1
  module Federails
2
2
  class ApplicationController < ActionController::Base
3
+ include Pundit::Authorization
4
+
5
+ rescue_from ActiveRecord::RecordNotFound, with: :error_not_found
6
+
7
+ layout Federails.configuration.app_layout if Federails.configuration.app_layout
8
+
9
+ private
10
+
11
+ def error_fallback(exception, fallback_message, status)
12
+ message = exception&.message || fallback_message
13
+ respond_to do |format|
14
+ format.jrd { head status }
15
+ format.xrd { head status }
16
+ format.activitypub { head status }
17
+ format.nodeinfo { head status }
18
+ format.json { render json: { error: message }, status: status }
19
+ format.html { raise exception }
20
+ end
21
+ end
22
+
23
+ def error_not_found(exception = nil)
24
+ error_fallback(exception, 'Resource not found', :not_found)
25
+ end
3
26
  end
4
27
  end
@@ -0,0 +1,21 @@
1
+ module Federails
2
+ module Client
3
+ class ActivitiesController < Federails::ApplicationController
4
+ before_action :authenticate_user!, only: [:feed]
5
+ # layout 'layouts/application'
6
+
7
+ # GET /app/activities
8
+ # GET /app/activities.json
9
+ def index
10
+ @activities = policy_scope(Federails::Activity, policy_scope_class: Federails::Client::ActivityPolicy::Scope).all
11
+ @activities = @activities.where actor: Actor.find_param(params[:actor_id]) if params[:actor_id]
12
+ end
13
+
14
+ # GET /app/feed
15
+ # GET /app/feed.json
16
+ def feed
17
+ @activities = Activity.feed_for(current_user.actor)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module Federails
2
+ module Client
3
+ class ActorsController < Federails::ApplicationController
4
+ before_action :set_actor, only: [:show]
5
+
6
+ # GET /app/actors
7
+ # GET /app/actors.json
8
+ def index
9
+ @actors = policy_scope(Federails::Actor, policy_scope_class: Federails::Client::ActorPolicy::Scope).all
10
+ end
11
+
12
+ # GET /app/actors/1
13
+ # GET /app/actors/1.json
14
+ def show; end
15
+
16
+ # GET /app/explorer/lookup
17
+ # GET /app/explorer/lookup.json
18
+ def lookup
19
+ @actor = Federails::Actor.find_by_account account_param
20
+ authorize @actor, policy_class: Federails::Client::ActorPolicy
21
+ render :show
22
+ end
23
+
24
+ private
25
+
26
+ # Use callbacks to share common setup or constraints between actions.
27
+ def set_actor
28
+ @actor = Federails::Actor.find_param(params[:id])
29
+ authorize @actor, policy_class: Federails::Client::ActorPolicy
30
+ end
31
+
32
+ def account_param
33
+ params.require('account')
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,101 @@
1
+ module Federails
2
+ module Client
3
+ class FollowingsController < Federails::ApplicationController
4
+ before_action :authenticate_user!
5
+ before_action :set_following, only: [:accept, :destroy]
6
+
7
+ # GET /app/followings/new?uri={uri}
8
+ def new
9
+ # Find actor (and fetch if necessary)
10
+ actor = Actor.find_or_create_by_federation_url(params[:uri])
11
+ # Redirect to local profile page which will have a follow button on it
12
+ redirect_to federails.client_actor_url(actor)
13
+ end
14
+
15
+ # PUT /app/followings/:id/accept
16
+ # PUT /app/followings/:id/accept.json
17
+ def accept
18
+ respond_to do |format|
19
+ url = federails.client_actor_url @following.actor
20
+ if @following.accept!
21
+ format.html { redirect_to url, notice: I18n.t('controller.followings.accept.success') }
22
+ format.json { render :show, status: :ok, location: @following }
23
+ else
24
+ format.html { redirect_to url, alert: I18n.t('controller.followings.accept.error') }
25
+ format.json { render json: @following.errors, status: :unprocessable_entity }
26
+ end
27
+ end
28
+ end
29
+
30
+ # POST /app/followings
31
+ # POST /app/followings.json
32
+ def create
33
+ @following = Following.new(following_params)
34
+ @following.actor = current_user.actor
35
+ authorize @following, policy_class: Federails::Client::FollowingPolicy
36
+
37
+ save_and_render
38
+ end
39
+
40
+ # POST /app/followings/follow
41
+ # POST /app/followings/follow.json
42
+ def follow
43
+ begin
44
+ @following = Following.new_from_account following_account_params, actor: current_user.actor
45
+ authorize @following, policy_class: Federails::Client::FollowingPolicy
46
+ rescue ::ActiveRecord::RecordNotFound
47
+ # Renders a 422 instead of a 404
48
+ respond_to do |format|
49
+ format.html { redirect_to federails.client_actors_url, alert: I18n.t('controller.followings.follow.error') }
50
+ format.json { render json: { target_actor: ['does not exist'] }, status: :unprocessable_entity }
51
+ end
52
+
53
+ return
54
+ end
55
+
56
+ save_and_render
57
+ end
58
+
59
+ # DELETE /app/followings/1
60
+ # DELETE /app/followings/1.json
61
+ def destroy
62
+ @following.destroy
63
+ respond_to do |format|
64
+ format.html { redirect_to federails.client_actor_url(@following.actor), notice: I18n.t('controller.followings.destroy.success') }
65
+ format.json { head :no_content }
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Use callbacks to share common setup or constraints between actions.
72
+ def set_following
73
+ @following = Following.find_param(params[:id])
74
+ authorize @following, policy_class: Federails::Client::FollowingPolicy
75
+ end
76
+
77
+ # Only allow a list of trusted parameters through.
78
+ def following_params
79
+ params.require(:following).permit(:target_actor_id)
80
+ end
81
+
82
+ def following_account_params
83
+ params.require(:account)
84
+ end
85
+
86
+ def save_and_render # rubocop:disable Metrics/AbcSize
87
+ url = federails.client_actor_url current_user.actor
88
+
89
+ respond_to do |format|
90
+ if @following.save
91
+ format.html { redirect_to url, notice: I18n.t('controller.followings.save_and_render.success') }
92
+ format.json { render :show, status: :created, location: @following }
93
+ else
94
+ format.html { redirect_to url, alert: I18n.t('controller.followings.save_and_render.error') }
95
+ format.json { render json: @following.errors, status: :unprocessable_entity }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,65 @@
1
+ require 'fediverse/inbox'
2
+
3
+ module Federails
4
+ module Server
5
+ class ActivitiesController < ServerController
6
+ before_action :set_activity, only: [:show]
7
+
8
+ # GET /federation/activities
9
+ # GET /federation/actors/1/outbox.json
10
+ def outbox
11
+ @actor = Actor.find_param(params[:actor_id])
12
+ @activities = policy_scope(Federails::Activity, policy_scope_class: Federails::Server::ActivityPolicy::Scope).where(actor: @actor).order(created_at: :desc)
13
+ @total_activities = @activities.count
14
+ @activities = @activities.page(params[:page])
15
+ end
16
+
17
+ # GET /federation/actors/1/activities/1.json
18
+ def show; end
19
+
20
+ # POST /federation/actors/1/inbox
21
+ def create
22
+ payload = payload_from_params
23
+ return head :unprocessable_entity unless payload
24
+
25
+ if Fediverse::Inbox.dispatch_request(payload)
26
+ head :created
27
+ else
28
+ head :unprocessable_entity
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Use callbacks to share common setup or constraints between actions.
35
+ def set_activity
36
+ @activity = Actor.find_param(params[:actor_id]).activities.find_param(params[:id])
37
+ end
38
+
39
+ # Only allow a list of trusted parameters through.
40
+ def activity_params
41
+ params.fetch(:activity, {})
42
+ end
43
+
44
+ def payload_from_params
45
+ payload_string = request.body.read
46
+ request.body.rewind if request.body.respond_to? :rewind
47
+
48
+ begin
49
+ payload = JSON.parse(payload_string)
50
+ rescue JSON::ParserError
51
+ return
52
+ end
53
+
54
+ hash = JSON::LD::API.compact payload, payload['@context']
55
+ validate_payload hash
56
+ end
57
+
58
+ def validate_payload(hash)
59
+ return unless hash['@context'] && hash['id'] && hash['type'] && hash['actor'] && hash['object']
60
+
61
+ hash
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,34 @@
1
+ module Federails
2
+ module Server
3
+ class ActorsController < ServerController
4
+ before_action :set_actor, only: [:show, :followers, :following]
5
+
6
+ # GET /federation/actors/1
7
+ # GET /federation/actors/1.json
8
+ def show; end
9
+
10
+ def followers
11
+ @actors = @actor.followers.order(created_at: :desc)
12
+ followings_queries
13
+ end
14
+
15
+ def following
16
+ @actors = @actor.follows.order(created_at: :desc)
17
+ followings_queries
18
+ end
19
+
20
+ private
21
+
22
+ # Use callbacks to share common setup or constraints between actions.
23
+ def set_actor
24
+ @actor = Actor.find_param(params[:id])
25
+ authorize @actor, policy_class: Federails::Server::ActorPolicy
26
+ end
27
+
28
+ def followings_queries
29
+ @total_actors = @actors.count
30
+ @actors = @actors.page(params[:page])
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ module Federails
2
+ module Server
3
+ class FollowingsController < ServerController
4
+ before_action :set_following, only: [:show]
5
+
6
+ # GET /federation/actors/1/followings/1.json
7
+ def show; end
8
+
9
+ private
10
+
11
+ # Use callbacks to share common setup or constraints between actions.
12
+ def set_following
13
+ actor = Actor.find_param(params[:actor_id])
14
+ @following = Following.find_by!(actor: actor, uuid: params[:id])
15
+ authorize @following, policy_class: Federails::Server::FollowingPolicy
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Federails
2
+ module Server
3
+ class NodeinfoController < ServerController
4
+ def index
5
+ render formats: [:nodeinfo]
6
+ end
7
+
8
+ def show # rubocop:todo Metrics/AbcSize
9
+ @total = @active_halfyear = @active_month = 0
10
+ Federails::Configuration.entity_types.each_value do |config|
11
+ next unless config[:include_in_user_count]
12
+
13
+ model = config[:class]
14
+ @total += model.count
15
+ @active_month += model.where(created_at: ((30.days.ago)...Time.current)).count
16
+ @active_halfyear += model.where(created_at: ((180.days.ago)...Time.current)).count
17
+ end
18
+ render formats: [:nodeinfo]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Federails
2
+ module Server
3
+ class ServerController < Federails::ApplicationController
4
+ protect_from_forgery with: :null_session
5
+
6
+ # def policy_scope(scope, policy_scope_class: nil)
7
+ # scope = [scope, :server] unless policy_scope_class
8
+ # super(scope, policy_scope_class: policy_scope_class)
9
+ # end
10
+
11
+ # def authorize(record, query = nil, policy_class: nil)
12
+ # record = [:server, record] unless policy_class
13
+ # super(record, query, policy_class: policy_class)
14
+ # end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ require 'fediverse/webfinger'
2
+
3
+ module Federails
4
+ module Server
5
+ class WebFingerController < ServerController
6
+ def find
7
+ resource = params.require(:resource)
8
+ case resource
9
+ when %r{^https?://.+}
10
+ @user = Federails::Actor.find_by_federation_url(resource)&.entity
11
+ when /^acct:.+/
12
+ Federails::Configuration.entity_types.each_value do |entity|
13
+ @user ||= entity[:class].find_by(entity[:username_field] => username)
14
+ end
15
+ end
16
+ raise ActiveRecord::RecordNotFound if @user.nil?
17
+
18
+ render formats: [:jrd]
19
+ end
20
+
21
+ def host_meta
22
+ render formats: [:xrd]
23
+ end
24
+
25
+ # TODO: complete missing endpoints
26
+
27
+ private
28
+
29
+ def username
30
+ account = Fediverse::Webfinger.split_resource_account params.require(:resource)
31
+ # Fail early if user don't _seems_ local
32
+ raise ActiveRecord::RecordNotFound unless account && Fediverse::Webfinger.local_user?(account)
33
+
34
+ account[:username]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,4 +1,12 @@
1
1
  module Federails
2
2
  module ApplicationHelper
3
+ def remote_follow_url
4
+ method_name = Federails.configuration.remote_follow_url_method.to_s
5
+ if method_name.starts_with? 'federails.'
6
+ send(method_name.gsub('federails.', ''))
7
+ else
8
+ Rails.application.routes.url_helpers.send(method_name)
9
+ end
10
+ end
3
11
  end
4
12
  end
@@ -0,0 +1,12 @@
1
+ require 'fediverse/notifier'
2
+
3
+ module Federails
4
+ class NotifyInboxJob < ApplicationJob
5
+ queue_as :default
6
+
7
+ def perform(activity)
8
+ activity.reload
9
+ Fediverse::Notifier.post_to_inboxes(activity)
10
+ end
11
+ end
12
+ end
@@ -1,6 +1,6 @@
1
1
  module Federails
2
2
  class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
4
- layout "mailer"
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
5
  end
6
6
  end