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 | ||||
|     end | ||||
|     self.activity.verb | ||||
|   end | ||||
|  | ||||
|   def targeted? | ||||
|     [:follow].include? self.verb | ||||
|   end | ||||
|  | ||||
|   def target | ||||
|     case self.activity_type | ||||
|     when 'Follow' | ||||
|       self.activity.target_account | ||||
|     end | ||||
|     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,10 +5,13 @@ 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 | ||||
|  | ||||
|     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.salmon_url  = data.link('salmon').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,20 +15,29 @@ 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}") | ||||
|         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 | ||||
|   | ||||
| @@ -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