* Add federation relay support * Add admin UI for managing relays * Include actor on relay-related activities * Fix i18npull/4/head
@ -0,0 +1,58 @@ | |||||
# frozen_string_literal: true | |||||
module Admin | |||||
class RelaysController < BaseController | |||||
before_action :set_relay, except: [:index, :new, :create] | |||||
def index | |||||
authorize :relay, :update? | |||||
@relays = Relay.all | |||||
end | |||||
def new | |||||
authorize :relay, :update? | |||||
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY) | |||||
end | |||||
def create | |||||
authorize :relay, :update? | |||||
@relay = Relay.new(resource_params) | |||||
if @relay.save | |||||
@relay.enable! | |||||
redirect_to admin_relays_path | |||||
else | |||||
render action: :new | |||||
end | |||||
end | |||||
def destroy | |||||
authorize :relay, :update? | |||||
@relay.destroy | |||||
redirect_to admin_relays_path | |||||
end | |||||
def enable | |||||
authorize :relay, :update? | |||||
@relay.enable! | |||||
redirect_to admin_relays_path | |||||
end | |||||
def disable | |||||
authorize :relay, :update? | |||||
@relay.disable! | |||||
redirect_to admin_relays_path | |||||
end | |||||
private | |||||
def set_relay | |||||
@relay = Relay.find(params[:id]) | |||||
end | |||||
def resource_params | |||||
params.require(:relay).permit(:inbox_url) | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,74 @@ | |||||
# frozen_string_literal: true | |||||
# == Schema Information | |||||
# | |||||
# Table name: relays | |||||
# | |||||
# id :bigint(8) not null, primary key | |||||
# inbox_url :string default(""), not null | |||||
# enabled :boolean default(FALSE), not null | |||||
# follow_activity_id :string | |||||
# created_at :datetime not null | |||||
# updated_at :datetime not null | |||||
# | |||||
class Relay < ApplicationRecord | |||||
PRESET_RELAY = 'https://relay.joinmastodon.org/inbox' | |||||
validates :inbox_url, presence: true, uniqueness: true, url: true, if: :will_save_change_to_inbox_url? | |||||
scope :enabled, -> { where(enabled: true) } | |||||
before_destroy :ensure_disabled | |||||
def enable! | |||||
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) | |||||
payload = Oj.dump(follow_activity(activity_id)) | |||||
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) | |||||
update(enabled: true, follow_activity_id: activity_id) | |||||
end | |||||
def disable! | |||||
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) | |||||
payload = Oj.dump(unfollow_activity(activity_id)) | |||||
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url) | |||||
update(enabled: false, follow_activity_id: nil) | |||||
end | |||||
private | |||||
def follow_activity(activity_id) | |||||
{ | |||||
'@context': ActivityPub::TagManager::CONTEXT, | |||||
id: activity_id, | |||||
type: 'Follow', | |||||
actor: ActivityPub::TagManager.instance.uri_for(some_local_account), | |||||
object: ActivityPub::TagManager::COLLECTIONS[:public], | |||||
} | |||||
end | |||||
def unfollow_activity(activity_id) | |||||
{ | |||||
'@context': ActivityPub::TagManager::CONTEXT, | |||||
id: activity_id, | |||||
type: 'Undo', | |||||
actor: ActivityPub::TagManager.instance.uri_for(some_local_account), | |||||
object: { | |||||
id: follow_activity_id, | |||||
type: 'Follow', | |||||
actor: ActivityPub::TagManager.instance.uri_for(some_local_account), | |||||
object: ActivityPub::TagManager::COLLECTIONS[:public], | |||||
}, | |||||
} | |||||
end | |||||
def some_local_account | |||||
@some_local_account ||= Account.local.find_by(suspended: false) | |||||
end | |||||
def ensure_disabled | |||||
return unless enabled? | |||||
disable! | |||||
end | |||||
end |
@ -0,0 +1,7 @@ | |||||
# frozen_string_literal: true | |||||
class RelayPolicy < ApplicationPolicy | |||||
def update? | |||||
admin? | |||||
end | |||||
end |
@ -0,0 +1,21 @@ | |||||
%tr | |||||
%td | |||||
%samp= relay.inbox_url | |||||
%td | |||||
- if relay.enabled? | |||||
%span.positive-hint | |||||
= fa_icon('check') | |||||
= ' ' | |||||
= t 'admin.relays.enabled' | |||||
- else | |||||
%span.negative-hint | |||||
= fa_icon('times') | |||||
= ' ' | |||||
= t 'admin.relays.disabled' | |||||
%td | |||||
- if relay.enabled? | |||||
= table_link_to 'power-off', t('admin.relays.disable'), disable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } | |||||
- else | |||||
= table_link_to 'power-off', t('admin.relays.enable'), enable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } | |||||
= table_link_to 'times', t('admin.relays.delete'), admin_relay_path(relay), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } |
@ -0,0 +1,20 @@ | |||||
- content_for :page_title do | |||||
= t('admin.relays.title') | |||||
.simple_form | |||||
%p.hint= t('admin.relays.description_html') | |||||
= link_to @relays.empty? ? t('admin.relays.setup') : t('admin.relays.add_new'), new_admin_relay_path, class: 'block-button' | |||||
- unless @relays.empty? | |||||
%hr.spacer | |||||
.table-wrapper | |||||
%table.table | |||||
%thead | |||||
%tr | |||||
%th= t('admin.relays.inbox_url') | |||||
%th= t('admin.relays.status') | |||||
%th | |||||
%tbody | |||||
= render @relays | |||||
@ -0,0 +1,13 @@ | |||||
- content_for :page_title do | |||||
= t('admin.relays.add_new') | |||||
= simple_form_for @relay, url: admin_relays_path do |f| | |||||
= render 'shared/error_messages', object: @relay | |||||
.field-group | |||||
= f.input :inbox_url, as: :string, wrapper: :with_block_label | |||||
.actions | |||||
= f.button :button, t('admin.relays.save_and_enable'), type: :submit | |||||
%p.hint.subtle-hint= t('admin.relays.enable_hint') |
@ -0,0 +1,12 @@ | |||||
class CreateRelays < ActiveRecord::Migration[5.2] | |||||
def change | |||||
create_table :relays do |t| | |||||
t.string :inbox_url, default: '', null: false | |||||
t.boolean :enabled, default: false, null: false, index: true | |||||
t.string :follow_activity_id | |||||
t.timestamps | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,4 @@ | |||||
Fabricator(:relay) do | |||||
inbox_url "https://example.com/inbox" | |||||
enabled true | |||||
end |
@ -0,0 +1,4 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe Relay, type: :model do | |||||
end |