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