Fixes and general progress
parent
709c6685a9
commit
1dad72bf13
|
@ -7,7 +7,7 @@ class XrdController < ApplicationController
|
|||
|
||||
def webfinger
|
||||
@account = Account.find_by!(username: username_from_resource, domain: nil)
|
||||
@canonical_account_uri = "acct:#{@account.username}#{LOCAL_DOMAIN}"
|
||||
@canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
|
||||
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
module ApplicationHelper
|
||||
include GrapeRouteHelpers::NamedRouteMatcher
|
||||
include RoutingHelper
|
||||
|
||||
def unique_tag(date, id, type)
|
||||
"tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
|
||||
end
|
||||
|
||||
def subscription_url(account)
|
||||
add_base_url_prefix subscription_path(id: account.id, format: '')
|
||||
add_base_url_prefix subscriptions_path(id: account.id, format: '')
|
||||
end
|
||||
|
||||
def salmon_url(account)
|
||||
|
@ -14,6 +14,6 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def add_base_url_prefix(suffix)
|
||||
"#{root_url}api#{suffix}"
|
||||
File.join(root_url, "api", suffix)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
module RoutingHelper
|
||||
extend ActiveSupport::Concern
|
||||
include Rails.application.routes.url_helpers
|
||||
include GrapeRouteHelpers::NamedRouteMatcher
|
||||
|
||||
included do
|
||||
def default_url_options
|
||||
ActionMailer::Base.default_url_options
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,6 +29,18 @@ class Account < ActiveRecord::Base
|
|||
self.domain.nil?
|
||||
end
|
||||
|
||||
def acct
|
||||
local? ? self.username : "#{self.username}@#{self.domain}"
|
||||
end
|
||||
|
||||
def object_type
|
||||
:person
|
||||
end
|
||||
|
||||
def subscribed?
|
||||
!(self.secret.blank? || self.verify_token.blank?)
|
||||
end
|
||||
|
||||
def keypair
|
||||
self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,28 @@ class Follow < ActiveRecord::Base
|
|||
belongs_to :account
|
||||
belongs_to :target_account, class_name: 'Account'
|
||||
|
||||
validates :account, :target_account, presence: true
|
||||
|
||||
def verb
|
||||
:follow
|
||||
end
|
||||
|
||||
def object_type
|
||||
:person
|
||||
end
|
||||
|
||||
def target
|
||||
self.target_account
|
||||
end
|
||||
|
||||
def content
|
||||
"#{self.account.acct} started following #{self.target_account.acct}"
|
||||
end
|
||||
|
||||
def title
|
||||
content
|
||||
end
|
||||
|
||||
after_create do
|
||||
self.account.stream_entries.create!(activity: self)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
class Status < ActiveRecord::Base
|
||||
belongs_to :account, inverse_of: :statuses
|
||||
|
||||
validates :account, presence: true
|
||||
|
||||
def verb
|
||||
:post
|
||||
end
|
||||
|
||||
def object_type
|
||||
:note
|
||||
end
|
||||
|
||||
def content
|
||||
self.text
|
||||
end
|
||||
|
||||
def title
|
||||
content.truncate(80, omission: "...")
|
||||
end
|
||||
|
||||
after_create do
|
||||
self.account.stream_entries.create!(activity: self)
|
||||
end
|
||||
|
|
|
@ -2,32 +2,29 @@ class StreamEntry < ActiveRecord::Base
|
|||
belongs_to :account, inverse_of: :stream_entries
|
||||
belongs_to :activity, polymorphic: true
|
||||
|
||||
validates :account, :activity, presence: true
|
||||
|
||||
def object_type
|
||||
case self.activity_type
|
||||
when 'Status'
|
||||
:note
|
||||
when 'Follow'
|
||||
:person
|
||||
end
|
||||
self.activity.object_type
|
||||
end
|
||||
|
||||
def verb
|
||||
case self.activity_type
|
||||
when 'Status'
|
||||
:post
|
||||
when 'Follow'
|
||||
:follow
|
||||
self.activity.verb
|
||||
end
|
||||
|
||||
def targeted?
|
||||
[:follow].include? self.verb
|
||||
end
|
||||
|
||||
def target
|
||||
case self.activity_type
|
||||
when 'Follow'
|
||||
self.activity.target_account
|
||||
self.activity.target
|
||||
end
|
||||
|
||||
def title
|
||||
self.activity.title
|
||||
end
|
||||
|
||||
def content
|
||||
self.activity.text if self.activity_type == 'Status'
|
||||
self.activity.content
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
class User < ActiveRecord::Base
|
||||
belongs_to :account, inverse_of: :user
|
||||
|
||||
validates :account, presence: true
|
||||
end
|
||||
|
|
|
@ -5,9 +5,12 @@ class FollowRemoteAccountService
|
|||
username, domain = uri.split('@')
|
||||
account = Account.where(username: username, domain: domain).first
|
||||
|
||||
return account unless account.nil?
|
||||
|
||||
if account.nil?
|
||||
account = Account.new(username: username, domain: domain)
|
||||
elsif account.subscribed?
|
||||
return account
|
||||
end
|
||||
|
||||
data = Goldfinger.finger("acct:#{uri}")
|
||||
|
||||
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
|
||||
|
@ -21,16 +24,20 @@ class FollowRemoteAccountService
|
|||
feed = get_feed(account.remote_url)
|
||||
hubs = feed.xpath('//xmlns:link[@rel="hub"]')
|
||||
|
||||
return false if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:author/xmlns:uri').nil?
|
||||
return nil if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri').nil?
|
||||
|
||||
account.uri = feed.at_xpath('/xmlns:author/xmlns:uri').content
|
||||
account.uri = feed.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri').content
|
||||
account.hub_url = hubs.first.attribute('href').value
|
||||
|
||||
get_profile(feed, account)
|
||||
account.save!
|
||||
|
||||
subscription = account.subscription(subscription_url(account))
|
||||
subscription.subscribe
|
||||
|
||||
return account
|
||||
rescue Goldfinger::Error, HTTP::Error => e
|
||||
false
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -40,6 +47,20 @@ class FollowRemoteAccountService
|
|||
Nokogiri::XML(response)
|
||||
end
|
||||
|
||||
def get_profile(xml, account)
|
||||
author = xml.at_xpath('/xmlns:feed/xmlns:author')
|
||||
|
||||
if author.at_xpath('./poco:displayName').nil?
|
||||
account.display_name = account.username
|
||||
else
|
||||
account.display_name = author.at_xpath('./poco:displayName').content
|
||||
end
|
||||
|
||||
unless author.at_xpath('./poco:note').nil?
|
||||
account.note = author.at_xpath('./poco:note').content
|
||||
end
|
||||
end
|
||||
|
||||
def magic_key_to_pem(magic_key)
|
||||
_, modulus, exponent = magic_key.split('.')
|
||||
modulus, exponent = [modulus, exponent].map { |n| Base64.urlsafe_decode64(n).bytes.inject(0) { |num, byte| (num << 8) | byte } }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class FollowService
|
||||
def call(source_account, uri)
|
||||
target_account = follow_remote_account_service.(uri)
|
||||
source_account.follow!(target_account)
|
||||
source_account.follow!(target_account) unless target_account.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -15,21 +15,30 @@ Nokogiri::XML::Builder.new do |xml|
|
|||
end
|
||||
|
||||
xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username))
|
||||
xml.link(rel: 'hub', href: '')
|
||||
xml.link(rel: 'hub', href: HUB_URL)
|
||||
xml.link(rel: 'salmon', href: salmon_url(@account))
|
||||
xml.link(rel: 'self', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
|
||||
|
||||
@account.stream_entries.each do |stream_entry|
|
||||
xml.entry do
|
||||
xml.id_ unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type)
|
||||
|
||||
xml.published stream_entry.activity.created_at.iso8601
|
||||
xml.updated stream_entry.activity.updated_at.iso8601
|
||||
xml.content({ type: 'html' }, stream_entry.content)
|
||||
xml.title
|
||||
|
||||
xml.title stream_entry.title
|
||||
xml.content({ type: 'html' }, stream_entry.content)
|
||||
xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{stream_entry.verb}")
|
||||
|
||||
if stream_entry.targeted?
|
||||
xml['activity'].send('object') do
|
||||
xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.target.object_type}")
|
||||
xml.id_ stream_entry.target.uri
|
||||
end
|
||||
else
|
||||
xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.object_type}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
Nokogiri::XML::Builder.new do |xml|
|
||||
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
|
||||
xml.Subject @canonical_account_uri
|
||||
xml.Alias profile_url(name: @account.username)
|
||||
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_url(name: @account.username))
|
||||
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
|
||||
xml.Link(rel: 'salmon', href: salmon_url(@account))
|
||||
xml.Link(rel: 'magic-public-key', href: @magic_key)
|
||||
|
|
|
@ -6,6 +6,8 @@ require 'rails/all'
|
|||
# you've limited to :test, :development, or :production.
|
||||
Bundler.require(*Rails.groups)
|
||||
|
||||
Dotenv::Railtie.load
|
||||
|
||||
module Mastodon
|
||||
class Application < Rails::Application
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
|
|
|
@ -38,6 +38,4 @@ Rails.application.configure do
|
|||
|
||||
# Raises error for missing translations
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
config.action_mailer.default_url_options = { host: ENV['NGROK_HOST'] }
|
||||
end
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
LOCAL_DOMAIN = ENV['LOCAL_DOMAIN'] || 'localhost'
|
||||
HUB_URL = ENV['HUB_URL'] || 'https://pubsubhubbub.superfeedr.com'
|
||||
|
||||
Rails.application.configure do
|
||||
config.action_mailer.default_url_options = { host: LOCAL_DOMAIN }
|
||||
end
|
||||
|
|
Reference in New Issue