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
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
|