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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc485556cd2ae749668f8f6a6ffec973361bdef6970e421845b9c2de64e8cecf
|
4
|
+
data.tar.gz: 34630a4b116e47b3d4c3ec4fd7599ba0264f4dd76fdff5db795d2be90c50c2cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b296b6db88b1077326722a187f6074fc8f9bbeb4f43d0f90a2c7811fb7bfd14b0e247f64149ce41c0e53128ce45d3e7e61ecfb0da8d47e64b6b7f188dfa33c8c
|
7
|
+
data.tar.gz: 948085a88295cf6c6699ed1dbea2a64a30d8695bb1a9d596cb83d66753eb4e26b5129d0d00574d21f230544b75a3f859411e64c785878abc2ce85021ef41a2b5
|
data/README.md
CHANGED
@@ -1,10 +1,30 @@
|
|
1
1
|
# Federails
|
2
|
-
Short description and motivation.
|
3
2
|
|
4
|
-
|
5
|
-
|
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,161 @@ gem "federails"
|
|
12
32
|
```
|
13
33
|
|
14
34
|
And then execute:
|
35
|
+
|
15
36
|
```bash
|
16
37
|
$ bundle
|
17
38
|
```
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
+
### Migrations
|
106
|
+
|
107
|
+
Copy the migrations:
|
108
|
+
|
109
|
+
```sh
|
110
|
+
bundle exec rails federails:install:migrations
|
111
|
+
```
|
112
|
+
|
113
|
+
### User model
|
114
|
+
|
115
|
+
In the ActivityPub world, we refer to _actors_ to represent the thing that publishes or subscribe to _other actors_.
|
116
|
+
|
117
|
+
Federails provides a concern to include in your "user" model or whatever will publish data:
|
118
|
+
|
119
|
+
```rb
|
120
|
+
# app/models/user.rb
|
121
|
+
|
122
|
+
class User < ApplicationRecord
|
123
|
+
# Include the concern here:
|
124
|
+
include Federails::Entity
|
125
|
+
|
126
|
+
# Configure field names
|
127
|
+
acts_as_federails_actor username_field: :username, name_field: :name, profile_url_method: :user_url
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
This concern automatically create a `Federails::Actor` after a user creation, as well as the `actor` reference. When adding it to
|
132
|
+
an existing model with existing data, you will need to generate the corresponding actors yourself in a migration.
|
133
|
+
|
134
|
+
Usage example:
|
135
|
+
|
136
|
+
```rb
|
137
|
+
actor = User.find(1).actor
|
138
|
+
|
139
|
+
actor.inbox
|
140
|
+
actor.outbox
|
141
|
+
actor.followers
|
142
|
+
actor.following
|
143
|
+
#...
|
22
144
|
```
|
23
145
|
|
24
146
|
## Contributing
|
25
|
-
|
147
|
+
|
148
|
+
Contributions are welcome, may it be issues, ideas, code or whatever you want to share. Please note:
|
149
|
+
|
150
|
+
- This project is _fast forward_ only: we don't do merge commits
|
151
|
+
- We adhere to [semantic versioning](). Please update the changelog in your commits
|
152
|
+
- We try to adhere to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) principles
|
153
|
+
- We _may_ rename your commits before merging them
|
154
|
+
- We _may_ split your commits before merging them
|
155
|
+
|
156
|
+
To contribute:
|
157
|
+
|
158
|
+
1. Fork this repository
|
159
|
+
2. Create small commits
|
160
|
+
3. Ideally create small pull requests. Don't hesitate to open them early so we all can follow how it's going
|
161
|
+
4. Get congratulated
|
162
|
+
|
163
|
+
### Tooling
|
164
|
+
|
165
|
+
#### RSpec
|
166
|
+
|
167
|
+
RSpec is the test suite. Start it with
|
168
|
+
|
169
|
+
```sh
|
170
|
+
bundle exec rspec
|
171
|
+
```
|
172
|
+
|
173
|
+
#### Rubocop
|
174
|
+
|
175
|
+
Rubocop is a linter. Start it with
|
176
|
+
|
177
|
+
```sh
|
178
|
+
bundle exec rubocop
|
179
|
+
```
|
180
|
+
|
181
|
+
#### FactoryBot
|
182
|
+
|
183
|
+
FactoryBot is a factory generator used in tests and development.
|
184
|
+
A rake task checks the replayability of the factories and traits:
|
185
|
+
|
186
|
+
```sh
|
187
|
+
bundle exec app:factory_bot:lint
|
188
|
+
```
|
26
189
|
|
27
190
|
## License
|
191
|
+
|
28
192
|
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
|
1
|
+
require 'bundler/setup'
|
2
2
|
|
3
|
-
APP_RAKEFILE = File.expand_path(
|
4
|
-
load
|
3
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
4
|
+
load 'rails/tasks/engine.rake'
|
5
5
|
|
6
|
-
load
|
6
|
+
load 'rails/tasks/statistics.rake'
|
7
7
|
|
8
|
-
require
|
8
|
+
require 'bundler/gem_tasks'
|
@@ -1,4 +1,23 @@
|
|
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.json { render json: { error: message }, status: status }
|
15
|
+
format.html { raise exception }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def error_not_found(exception = nil)
|
20
|
+
error_fallback(exception, 'Resource not found', :not_found)
|
21
|
+
end
|
3
22
|
end
|
4
23
|
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_id: 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(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,93 @@
|
|
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
|
+
# PUT /app/followings/:id/accept
|
8
|
+
# PUT /app/followings/:id/accept.json
|
9
|
+
def accept
|
10
|
+
respond_to do |format|
|
11
|
+
url = federails.client_actor_url @following.actor
|
12
|
+
if @following.accept!
|
13
|
+
format.html { redirect_to url, notice: I18n.t('controller.followings.accept.success') }
|
14
|
+
format.json { render :show, status: :ok, location: @following }
|
15
|
+
else
|
16
|
+
format.html { redirect_to url, alert: I18n.t('controller.followings.accept.error') }
|
17
|
+
format.json { render json: @following.errors, status: :unprocessable_entity }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# POST /app/followings
|
23
|
+
# POST /app/followings.json
|
24
|
+
def create
|
25
|
+
@following = Following.new(following_params)
|
26
|
+
@following.actor = current_user.actor
|
27
|
+
authorize @following, policy_class: Federails::Client::FollowingPolicy
|
28
|
+
|
29
|
+
save_and_render
|
30
|
+
end
|
31
|
+
|
32
|
+
# POST /app/followings/follow
|
33
|
+
# POST /app/followings/follow.json
|
34
|
+
def follow
|
35
|
+
begin
|
36
|
+
@following = Following.new_from_account following_account_params, actor: current_user.actor
|
37
|
+
authorize @following, policy_class: Federails::Client::FollowingPolicy
|
38
|
+
rescue ::ActiveRecord::RecordNotFound
|
39
|
+
# Renders a 422 instead of a 404
|
40
|
+
respond_to do |format|
|
41
|
+
format.html { redirect_to federails.client_actors_url, alert: I18n.t('controller.followings.follow.error') }
|
42
|
+
format.json { render json: { target_actor: ['does not exist'] }, status: :unprocessable_entity }
|
43
|
+
end
|
44
|
+
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
save_and_render
|
49
|
+
end
|
50
|
+
|
51
|
+
# DELETE /app/followings/1
|
52
|
+
# DELETE /app/followings/1.json
|
53
|
+
def destroy
|
54
|
+
@following.destroy
|
55
|
+
respond_to do |format|
|
56
|
+
format.html { redirect_to federails.client_actor_url(@following.actor), notice: I18n.t('controller.followings.destroy.success') }
|
57
|
+
format.json { head :no_content }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Use callbacks to share common setup or constraints between actions.
|
64
|
+
def set_following
|
65
|
+
@following = Following.find(params[:id])
|
66
|
+
authorize @following, policy_class: Federails::Client::FollowingPolicy
|
67
|
+
end
|
68
|
+
|
69
|
+
# Only allow a list of trusted parameters through.
|
70
|
+
def following_params
|
71
|
+
params.require(:following).permit(:target_actor_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
def following_account_params
|
75
|
+
params.require(:account)
|
76
|
+
end
|
77
|
+
|
78
|
+
def save_and_render # rubocop:disable Metrics/AbcSize
|
79
|
+
url = federails.client_actor_url current_user.actor
|
80
|
+
|
81
|
+
respond_to do |format|
|
82
|
+
if @following.save
|
83
|
+
format.html { redirect_to url, notice: I18n.t('controller.followings.save_and_render.success') }
|
84
|
+
format.json { render :show, status: :created, location: @following }
|
85
|
+
else
|
86
|
+
format.html { redirect_to url, alert: I18n.t('controller.followings.save_and_render.error') }
|
87
|
+
format.json { render json: @following.errors, status: :unprocessable_entity }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
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(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 render json: {}, status: :unprocessable_entity unless payload
|
24
|
+
|
25
|
+
if Fediverse::Inbox.dispatch_request(payload)
|
26
|
+
render json: {}, status: :created
|
27
|
+
else
|
28
|
+
render json: {}, status: :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 = Activity.find_by!(actor_id: params[:actor_id], id: 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(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,18 @@
|
|
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
|
+
@following = Following.find_by!(actor_id: params[:actor_id], id: params[:id])
|
14
|
+
authorize @following, policy_class: Federails::Server::FollowingPolicy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Federails
|
2
|
+
module Server
|
3
|
+
class NodeinfoController < ServerController
|
4
|
+
def index
|
5
|
+
render formats: [:json]
|
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: [:json]
|
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: [:json]
|
19
|
+
end
|
20
|
+
|
21
|
+
def host_meta
|
22
|
+
render content_type: 'application/xrd+xml', formats: [:xml]
|
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
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Federails
|
2
|
+
module Entity
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_one :actor, class_name: 'Federails::Actor', as: :entity, dependent: :destroy
|
7
|
+
|
8
|
+
after_create :create_actor
|
9
|
+
|
10
|
+
# Configures the mapping between entity and actor
|
11
|
+
# @param username_field [Symbol] The method or attribute name that returns the preferred username for ActivityPub
|
12
|
+
# @param name_field [Symbol] The method or attribute name that returns the preferred name for ActivityPub
|
13
|
+
# @param profile_url_method [Symbol] The route method name that will generate the profile URL for ActivityPub
|
14
|
+
# @param actor_type [String] The ActivityStreams Actor type for this entity; defaults to 'Person'
|
15
|
+
# @param include_in_user_count [boolean] Should this entity be included in the nodeinfo user count? Defaults to true
|
16
|
+
# @example
|
17
|
+
# acts_as_federails_actor username_field: :username, name_field: :display_name, profile_url_method: :url_for, actor_type: 'Person'
|
18
|
+
def self.acts_as_federails_actor(
|
19
|
+
username_field: Federails::Configuration.user_username_field,
|
20
|
+
name_field: Federails::Configuration.user_name_field,
|
21
|
+
profile_url_method: Federails.configuration.user_profile_url_method,
|
22
|
+
actor_type: 'Person',
|
23
|
+
include_in_user_count: true
|
24
|
+
)
|
25
|
+
Federails::Configuration.register_entity(
|
26
|
+
self,
|
27
|
+
username_field: username_field,
|
28
|
+
name_field: name_field,
|
29
|
+
profile_url_method: profile_url_method,
|
30
|
+
actor_type: actor_type,
|
31
|
+
include_in_user_count: include_in_user_count
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Automatically run default acts_as_federails_actor
|
36
|
+
# this can be optionally called again with different configuration in the entity
|
37
|
+
acts_as_federails_actor
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_actor
|
42
|
+
Federails::Actor.create! entity: self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|