Fixes and general progress
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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?
 | 
			
		||||
  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
	
	Block a user