Made some progress
This commit is contained in:
		
							
								
								
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -38,6 +38,8 @@ group :development do | ||||
|   gem 'web-console', '~> 2.0' | ||||
|   gem 'spring' | ||||
|   gem 'rubocop', require: false | ||||
|   gem 'better_errors' | ||||
|   gem 'binding_of_caller' | ||||
| end | ||||
|  | ||||
| group :production do | ||||
|   | ||||
| @@ -43,6 +43,10 @@ GEM | ||||
|       descendants_tracker (~> 0.0.4) | ||||
|       ice_nine (~> 0.11.0) | ||||
|       thread_safe (~> 0.3, >= 0.3.1) | ||||
|     better_errors (2.1.1) | ||||
|       coderay (>= 1.0.0) | ||||
|       erubis (>= 2.6.6) | ||||
|       rack (>= 0.9.0) | ||||
|     binding_of_caller (0.7.2) | ||||
|       debug_inspector (>= 0.0.1) | ||||
|     builder (3.2.2) | ||||
| @@ -284,6 +288,8 @@ PLATFORMS | ||||
|  | ||||
| DEPENDENCIES | ||||
|   addressable | ||||
|   better_errors | ||||
|   binding_of_caller | ||||
|   byebug | ||||
|   coffee-rails (~> 4.1.0) | ||||
|   dotenv-rails | ||||
|   | ||||
| @@ -3,6 +3,8 @@ module Mastodon | ||||
|     class Account < Grape::Entity | ||||
|       expose :username | ||||
|       expose :domain | ||||
|       expose :display_name | ||||
|       expose :note | ||||
|     end | ||||
|  | ||||
|     class Status < Grape::Entity | ||||
|   | ||||
| @@ -8,12 +8,10 @@ module Mastodon | ||||
|  | ||||
|     resource :subscriptions do | ||||
|       helpers do | ||||
|         def subscription_url(account) | ||||
|           "https://649841dc.ngrok.io/api#{subscriptions_path(id: account.id)}" | ||||
|         end | ||||
|         include ApplicationHelper | ||||
|       end | ||||
|  | ||||
|       desc 'Receive updates from a feed' | ||||
|       desc 'Receive updates from an account' | ||||
|  | ||||
|       params do | ||||
|         requires :id, type: String, desc: 'Account ID' | ||||
| @@ -23,14 +21,14 @@ module Mastodon | ||||
|         body = request.body.read | ||||
|  | ||||
|         if @account.subscription(subscription_url(@account)).verify(body, env['HTTP_X_HUB_SIGNATURE']) | ||||
|           ProcessFeedUpdateService.new.(body, @account) | ||||
|           ProcessFeedService.new.(body, @account) | ||||
|           status 201 | ||||
|         else | ||||
|           status 202 | ||||
|         end | ||||
|       end | ||||
|  | ||||
|       desc 'Confirm PuSH subscription to a feed' | ||||
|       desc 'Confirm PuSH subscription to an account' | ||||
|  | ||||
|       params do | ||||
|         requires :id, type: String, desc: 'Account ID' | ||||
| @@ -49,14 +47,15 @@ module Mastodon | ||||
|     end | ||||
|  | ||||
|     resource :salmon do | ||||
|       desc 'Receive Salmon updates' | ||||
|       desc 'Receive Salmon updates targeted to account' | ||||
|  | ||||
|       params do | ||||
|         requires :id, type: String, desc: 'Account ID' | ||||
|       end | ||||
|  | ||||
|       post ':id' do | ||||
|         # todo | ||||
|         ProcessInteractionService.new.(request.body.read, @account) | ||||
|         status 201 | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|   | ||||
| @@ -5,9 +5,34 @@ module Mastodon | ||||
|  | ||||
|     resource :statuses do | ||||
|       desc 'Return a public timeline' | ||||
|  | ||||
|       get :all do | ||||
|         present Status.all, with: Mastodon::Entities::Status | ||||
|       end | ||||
|  | ||||
|       desc 'Return the home timeline of a logged in user' | ||||
|  | ||||
|       get :home do | ||||
|         # todo | ||||
|       end | ||||
|  | ||||
|       desc 'Return the notifications timeline of a logged in user' | ||||
|  | ||||
|       get :notifications do | ||||
|         # todo | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     resource :accounts do | ||||
|       desc 'Return a user profile' | ||||
|  | ||||
|       params do | ||||
|         requires :id, type: String, desc: 'Account ID' | ||||
|       end | ||||
|  | ||||
|       get ':id' do | ||||
|         present Account.find(params[:id]), with: Mastodon::Entities::Account | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -12,5 +12,4 @@ | ||||
| // | ||||
| //= require jquery | ||||
| //= require jquery_ujs | ||||
| //= require turbolinks | ||||
| //= require_tree . | ||||
|   | ||||
							
								
								
									
										3
									
								
								app/assets/javascripts/atom.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/atom.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # Place all the behaviors and hooks related to the matching controller here. | ||||
| # All this logic will automatically be available in application.js. | ||||
| # You can use CoffeeScript in this file: http://coffeescript.org/ | ||||
							
								
								
									
										3
									
								
								app/assets/javascripts/home.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/home.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # Place all the behaviors and hooks related to the matching controller here. | ||||
| # All this logic will automatically be available in application.js. | ||||
| # You can use CoffeeScript in this file: http://coffeescript.org/ | ||||
							
								
								
									
										3
									
								
								app/assets/javascripts/profile.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/profile.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # Place all the behaviors and hooks related to the matching controller here. | ||||
| # All this logic will automatically be available in application.js. | ||||
| # You can use CoffeeScript in this file: http://coffeescript.org/ | ||||
							
								
								
									
										3
									
								
								app/assets/javascripts/xrd.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/xrd.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # Place all the behaviors and hooks related to the matching controller here. | ||||
| # All this logic will automatically be available in application.js. | ||||
| # You can use CoffeeScript in this file: http://coffeescript.org/ | ||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/atom.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/atom.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Place all the styles related to the Atom controller here. | ||||
| // They will automatically be included in application.css. | ||||
| // You can use Sass (SCSS) here: http://sass-lang.com/ | ||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/home.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/home.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Place all the styles related to the Home controller here. | ||||
| // They will automatically be included in application.css. | ||||
| // You can use Sass (SCSS) here: http://sass-lang.com/ | ||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/profile.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/profile.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Place all the styles related to the Profile controller here. | ||||
| // They will automatically be included in application.css. | ||||
| // You can use Sass (SCSS) here: http://sass-lang.com/ | ||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/xrd.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/xrd.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Place all the styles related to the XRD controller here. | ||||
| // They will automatically be included in application.css. | ||||
| // You can use Sass (SCSS) here: http://sass-lang.com/ | ||||
							
								
								
									
										14
									
								
								app/controllers/atom_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/controllers/atom_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| class AtomController < ApplicationController | ||||
|   before_filter :set_format | ||||
|  | ||||
|   def user_stream | ||||
|     @account = Account.find_by!(id: params[:id], domain: nil) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def set_format | ||||
|     request.format = 'xml' | ||||
|     response.headers['Content-Type'] = 'application/atom+xml' | ||||
|   end | ||||
| end | ||||
							
								
								
									
										4
									
								
								app/controllers/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/controllers/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| class HomeController < ApplicationController | ||||
|   def index | ||||
|   end | ||||
| end | ||||
							
								
								
									
										4
									
								
								app/controllers/profile_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/controllers/profile_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| class ProfileController < ApplicationController | ||||
|   def show | ||||
|   end | ||||
| end | ||||
							
								
								
									
										39
									
								
								app/controllers/xrd_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/controllers/xrd_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| class XrdController < ApplicationController | ||||
|   before_filter :set_format | ||||
|  | ||||
|   def host_meta | ||||
|     @webfinger_template = "#{webfinger_url}?resource={uri}" | ||||
|   end | ||||
|  | ||||
|   def webfinger | ||||
|     @account = Account.find_by!(username: username_from_resource, domain: nil) | ||||
|     @canonical_account_uri = "acct:#{@account.username}#{LOCAL_DOMAIN}" | ||||
|     @magic_key = pem_to_magic_key(@account.keypair.public_key) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def set_format | ||||
|     request.format = 'xml' | ||||
|     response.headers['Content-Type'] = 'application/xrd+xml' | ||||
|   end | ||||
|  | ||||
|   def username_from_resource | ||||
|     params[:resource].split('@').first.gsub('acct:', '') | ||||
|   end | ||||
|  | ||||
|   def pem_to_magic_key(public_key) | ||||
|     modulus, exponent = [public_key.n, public_key.e].map do |component| | ||||
|       result = "" | ||||
|  | ||||
|       until component == 0 do | ||||
|         result << [component % 256].pack('C') | ||||
|         component >>= 8 | ||||
|       end | ||||
|  | ||||
|       result.reverse! | ||||
|     end | ||||
|  | ||||
|     (["RSA"] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.') | ||||
|   end | ||||
| end | ||||
| @@ -1,2 +1,19 @@ | ||||
| module ApplicationHelper | ||||
|   include GrapeRouteHelpers::NamedRouteMatcher | ||||
|  | ||||
|   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: '') | ||||
|   end | ||||
|  | ||||
|   def salmon_url(account) | ||||
|     add_base_url_prefix salmon_path(id: account.id, format: '') | ||||
|   end | ||||
|  | ||||
|   def add_base_url_prefix(suffix) | ||||
|     "#{root_url}api#{suffix}" | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										5
									
								
								app/helpers/atom_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/helpers/atom_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| module AtomHelper | ||||
|   def stream_updated_at | ||||
|     @account.stream_entries.last ? @account.stream_entries.last.created_at.iso8601 : @account.updated_at.iso8601 | ||||
|   end | ||||
| end | ||||
							
								
								
									
										2
									
								
								app/helpers/home_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/home_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| module HomeHelper | ||||
| end | ||||
							
								
								
									
										2
									
								
								app/helpers/profile_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/profile_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| module ProfileHelper | ||||
| end | ||||
							
								
								
									
										2
									
								
								app/helpers/xrd_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/xrd_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| module XrdHelper | ||||
| end | ||||
| @@ -1,6 +1,38 @@ | ||||
| class Account < ActiveRecord::Base | ||||
|   # Local users | ||||
|   has_one :user, inverse_of: :account | ||||
|  | ||||
|   # Timelines | ||||
|   has_many :stream_entries, inverse_of: :account | ||||
|   has_many :statuses, inverse_of: :account | ||||
|  | ||||
|   # Follow relations | ||||
|   has_many :active_relationships,  class_name: 'Follow', foreign_key: 'account_id',        dependent: :destroy | ||||
|   has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_account_id', dependent: :destroy | ||||
|  | ||||
|   has_many :following, through: :active_relationships,  source: :target_account | ||||
|   has_many :followers, through: :passive_relationships, source: :account | ||||
|  | ||||
|   def follow!(other_account) | ||||
|     self.active_relationships.create!(target_account: other_account) | ||||
|   end | ||||
|  | ||||
|   def unfollow!(other_account) | ||||
|     self.active_relationships.find_by(target_account: other_account).destroy | ||||
|   end | ||||
|  | ||||
|   def following?(other_account) | ||||
|     following.include?(other_account) | ||||
|   end | ||||
|  | ||||
|   def local? | ||||
|     self.domain.nil? | ||||
|   end | ||||
|  | ||||
|   def keypair | ||||
|     self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key) | ||||
|   end | ||||
|  | ||||
|   def subscription(webhook_url) | ||||
|     @subscription ||= OStatus2::Subscription.new(self.remote_url, secret: self.secret, token: self.verify_token, webhook: webhook_url, hub: self.hub_url) | ||||
|   end | ||||
|   | ||||
							
								
								
									
										8
									
								
								app/models/follow.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/models/follow.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| class Follow < ActiveRecord::Base | ||||
|   belongs_to :account | ||||
|   belongs_to :target_account, class_name: 'Account' | ||||
|  | ||||
|   after_create do | ||||
|     self.account.stream_entries.create!(activity: self) | ||||
|   end | ||||
| end | ||||
| @@ -1,3 +1,7 @@ | ||||
| class Status < ActiveRecord::Base | ||||
|   belongs_to :account, inverse_of: :statuses | ||||
|  | ||||
|   after_create do | ||||
|     self.account.stream_entries.create!(activity: self) | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/models/stream_entry.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/models/stream_entry.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| class StreamEntry < ActiveRecord::Base | ||||
|   belongs_to :account, inverse_of: :stream_entries | ||||
|   belongs_to :activity, polymorphic: true | ||||
|  | ||||
|   def object_type | ||||
|     case self.activity_type | ||||
|     when 'Status' | ||||
|       :note | ||||
|     when 'Follow' | ||||
|       :person | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def verb | ||||
|     case self.activity_type | ||||
|     when 'Status' | ||||
|       :post | ||||
|     when 'Follow' | ||||
|       :follow | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def target | ||||
|     case self.activity_type | ||||
|     when 'Follow' | ||||
|       self.activity.target_account | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def content | ||||
|     self.activity.text if self.activity_type == 'Status' | ||||
|   end | ||||
| end | ||||
							
								
								
									
										3
									
								
								app/models/user.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/models/user.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| class User < ActiveRecord::Base | ||||
|   belongs_to :account, inverse_of: :user | ||||
| end | ||||
| @@ -1,5 +1,15 @@ | ||||
| class FetchFeedService | ||||
|   def call(account) | ||||
|     # todo | ||||
|     process_service.(http_client.get(account.remote_url), account) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def process_service | ||||
|     ProcessFeedService.new | ||||
|   end | ||||
|  | ||||
|   def http_client | ||||
|     HTTP | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| class FollowRemoteUserService | ||||
|   include GrapeRouteHelpers::NamedRouteMatcher | ||||
| class FollowRemoteAccountService | ||||
|   include ApplicationHelper | ||||
| 
 | ||||
|   def call(user) | ||||
|     username, domain = user.split('@') | ||||
|   def call(uri) | ||||
|     username, domain = uri.split('@') | ||||
|     account = Account.where(username: username, domain: domain).first | ||||
| 
 | ||||
|     return account unless account.nil? | ||||
| 
 | ||||
|     account = Account.new(username: username, domain: domain) | ||||
|     data    = Goldfinger.finger("acct:#{user}") | ||||
|     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,8 +21,9 @@ class FollowRemoteUserService | ||||
|     feed = get_feed(account.remote_url) | ||||
|     hubs = feed.xpath('//xmlns:link[@rel="hub"]') | ||||
| 
 | ||||
|     return false if hubs.empty? || hubs.first.attribute('href').nil? | ||||
|     return false if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:author/xmlns:uri').nil? | ||||
| 
 | ||||
|     account.uri     = feed.at_xpath('/xmlns:author/xmlns:uri').content | ||||
|     account.hub_url = hubs.first.attribute('href').value | ||||
|     account.save! | ||||
| 
 | ||||
| @@ -45,7 +46,7 @@ class FollowRemoteUserService | ||||
| 
 | ||||
|     key   = OpenSSL::PKey::RSA.new | ||||
|     key.n = modulus | ||||
|     key.d = exponent | ||||
|     key.e = exponent | ||||
| 
 | ||||
|     key.to_pem | ||||
|   end | ||||
| @@ -53,8 +54,4 @@ class FollowRemoteUserService | ||||
|   def http_client | ||||
|     HTTP | ||||
|   end | ||||
| 
 | ||||
|   def subscription_url(account) | ||||
|     "https://649841dc.ngrok.io/api#{subscriptions_path(id: account.id)}" | ||||
|   end | ||||
| end | ||||
							
								
								
									
										12
									
								
								app/services/follow_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/services/follow_service.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| class FollowService | ||||
|   def call(source_account, uri) | ||||
|     target_account = follow_remote_account_service.(uri) | ||||
|     source_account.follow!(target_account) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def follow_remote_account_service | ||||
|     FollowRemoteAccountService.new | ||||
|   end | ||||
| end | ||||
| @@ -1,4 +1,4 @@ | ||||
| class ProcessFeedUpdateService | ||||
| class ProcessFeedService | ||||
|   def call(body, account) | ||||
|     xml = Nokogiri::XML(body) | ||||
| 
 | ||||
							
								
								
									
										38
									
								
								app/services/process_interaction_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/services/process_interaction_service.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| class ProcessInteractionService | ||||
|   def call(envelope, target_account) | ||||
|     body = salmon.unpack(envelope) | ||||
|     xml  = Nokogiri::XML(body) | ||||
|  | ||||
|     return if xml.at_xpath('//author/name').nil? || xml.at_xpath('//author/uri').nil? | ||||
|  | ||||
|     username = xml.at_xpath('//author/name').content | ||||
|     url      = xml.at_xpath('//author/uri').content | ||||
|     domain   = Addressable::URI.parse(url).host | ||||
|     account  = Account.find_by(username: username, domain: domain) | ||||
|  | ||||
|     if account.nil? | ||||
|       account = follow_remote_account_service.("acct:#{username}@#{domain}") | ||||
|     end | ||||
|  | ||||
|     if salmon.verify(envelope, account.keypair) | ||||
|       verb = xml.at_path('//activity:verb').content | ||||
|  | ||||
|       case verb | ||||
|       when 'http://activitystrea.ms/schema/1.0/follow', 'follow' | ||||
|         account.follow!(target_account) | ||||
|       when 'http://activitystrea.ms/schema/1.0/unfollow', 'unfollow' | ||||
|         account.unfollow!(target_account) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def salmon | ||||
|     OStatus2::Salmon.new | ||||
|   end | ||||
|  | ||||
|   def follow_remote_account_service | ||||
|     FollowRemoteAccountService.new | ||||
|   end | ||||
| end | ||||
							
								
								
									
										14
									
								
								app/services/setup_local_account_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/services/setup_local_account_service.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| class SetupLocalAccountService | ||||
|   def call(user, username) | ||||
|     user.build_account | ||||
|  | ||||
|     user.account.username = username | ||||
|     user.account.domain   = nil | ||||
|  | ||||
|     keypair = OpenSSL::PKey::RSA.new(2048) | ||||
|     user.account.private_key = keypair.to_pem | ||||
|     user.account.public_key  = keypair.public_key.to_pem | ||||
|  | ||||
|     user.save! | ||||
|   end | ||||
| end | ||||
							
								
								
									
										35
									
								
								app/views/atom/user_stream.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/views/atom/user_stream.xml.ruby
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| Nokogiri::XML::Builder.new do |xml| | ||||
|   xml.feed(xmlns: 'http://www.w3.org/2005/Atom', 'xmlns:thr': 'http://purl.org/syndication/thread/1.0', 'xmlns:activity': 'http://activitystrea.ms/spec/1.0/') do | ||||
|     xml.id_ atom_user_stream_url(id: @account.id) | ||||
|     xml.title @account.display_name | ||||
|     xml.subtitle @account.note | ||||
|     xml.updated stream_updated_at | ||||
|  | ||||
|     xml.author do | ||||
|       xml['activity'].send('object-type', 'http://activitystrea.ms/schema/1.0/person') | ||||
|       xml.uri profile_url(name: @account.username) | ||||
|       xml.name @account.username | ||||
|       xml.summary @account.note | ||||
|  | ||||
|       xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username)) | ||||
|     end | ||||
|  | ||||
|     xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username)) | ||||
|     xml.link(rel: 'hub', href: '') | ||||
|     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['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}") | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end.to_xml | ||||
							
								
								
									
										1
									
								
								app/views/home/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/views/home/index.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Mastodon | ||||
| @@ -1,14 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <title>Mastodon</title> | ||||
|   <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %> | ||||
|   <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> | ||||
|   <%= csrf_meta_tags %> | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
| <%= yield %> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										10
									
								
								app/views/layouts/application.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/views/layouts/application.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| !!! | ||||
| %html | ||||
|   %head | ||||
|     %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ | ||||
|     %title Mastodon | ||||
|     = stylesheet_link_tag    'application', media: 'all' | ||||
|     = javascript_include_tag 'application' | ||||
|     = csrf_meta_tags | ||||
|   %body | ||||
|     = yield | ||||
							
								
								
									
										2
									
								
								app/views/profile/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/views/profile/show.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| %h1 Profile#show | ||||
| %p Find me in app/views/profile/show.html.haml | ||||
							
								
								
									
										5
									
								
								app/views/xrd/host_meta.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/views/xrd/host_meta.xml.ruby
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| Nokogiri::XML::Builder.new do |xml| | ||||
|   xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do | ||||
|     xml.Link(rel: 'lrdd', type: 'application/xrd+xml', template: @webfinger_template) | ||||
|   end | ||||
| end.to_xml | ||||
							
								
								
									
										8
									
								
								app/views/xrd/webfinger.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/views/xrd/webfinger.xml.ruby
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +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.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) | ||||
|   end | ||||
| end.to_xml | ||||
| @@ -38,4 +38,6 @@ 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
									
								
								config/initializers/ostatus.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								config/initializers/ostatus.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| LOCAL_DOMAIN = ENV['LOCAL_DOMAIN'] || 'localhost' | ||||
| @@ -1,3 +1,11 @@ | ||||
| Rails.application.routes.draw do | ||||
|   get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta | ||||
|   get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger | ||||
|  | ||||
|   get 'atom/:id',   to: 'atom#user_stream', as: :atom_user_stream | ||||
|   get 'user/:name', to: 'profile#show', as: :profile | ||||
|  | ||||
|   mount Mastodon::API => '/api/' | ||||
|  | ||||
|   root 'home#index' | ||||
| end | ||||
|   | ||||
							
								
								
									
										12
									
								
								db/migrate/20160221003140_create_users.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								db/migrate/20160221003140_create_users.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| class CreateUsers < ActiveRecord::Migration | ||||
|   def change | ||||
|     create_table :users do |t| | ||||
|       t.string :email, null: false, default: '' | ||||
|       t.integer :account_id, null: false | ||||
|  | ||||
|       t.timestamps null: false | ||||
|     end | ||||
|  | ||||
|     add_index :users, :email, unique: true | ||||
|   end | ||||
| end | ||||
							
								
								
									
										12
									
								
								db/migrate/20160221003621_create_follows.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								db/migrate/20160221003621_create_follows.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| class CreateFollows < ActiveRecord::Migration | ||||
|   def change | ||||
|     create_table :follows do |t| | ||||
|       t.integer :account_id, null: false | ||||
|       t.integer :target_account_id, null: false | ||||
|  | ||||
|       t.timestamps null: false | ||||
|     end | ||||
|  | ||||
|     add_index :follows, [:account_id, :target_account_id], unique: true | ||||
|   end | ||||
| end | ||||
							
								
								
									
										11
									
								
								db/migrate/20160222122600_create_stream_entries.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								db/migrate/20160222122600_create_stream_entries.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| class CreateStreamEntries < ActiveRecord::Migration | ||||
|   def change | ||||
|     create_table :stream_entries do |t| | ||||
|       t.integer :account_id | ||||
|       t.integer :activity_id | ||||
|       t.string :activity_type | ||||
|  | ||||
|       t.timestamps null: false | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,7 @@ | ||||
| class AddProfileFieldsToAccounts < ActiveRecord::Migration | ||||
|   def change | ||||
|     add_column :accounts, :note, :text, null: false, default: '' | ||||
|     add_column :accounts, :display_name, :string, null: false, default: '' | ||||
|     add_column :accounts, :uri, :string, null: false, default: '' | ||||
|   end | ||||
| end | ||||
							
								
								
									
										31
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								db/schema.rb
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ | ||||
| # | ||||
| # It's strongly recommended that you check this file into your version control system. | ||||
|  | ||||
| ActiveRecord::Schema.define(version: 20160220211917) do | ||||
| ActiveRecord::Schema.define(version: 20160222143943) do | ||||
|  | ||||
|   # These are extensions that must be enabled in order to support this database | ||||
|   enable_extension "plpgsql" | ||||
| @@ -28,10 +28,22 @@ ActiveRecord::Schema.define(version: 20160220211917) do | ||||
|     t.string   "hub_url",      default: "", null: false | ||||
|     t.datetime "created_at",                null: false | ||||
|     t.datetime "updated_at",                null: false | ||||
|     t.text     "note",         default: "", null: false | ||||
|     t.string   "display_name", default: "", null: false | ||||
|     t.string   "uri",          default: "", null: false | ||||
|   end | ||||
|  | ||||
|   add_index "accounts", ["username", "domain"], name: "index_accounts_on_username_and_domain", unique: true, using: :btree | ||||
|  | ||||
|   create_table "follows", force: :cascade do |t| | ||||
|     t.integer  "account_id",        null: false | ||||
|     t.integer  "target_account_id", null: false | ||||
|     t.datetime "created_at",        null: false | ||||
|     t.datetime "updated_at",        null: false | ||||
|   end | ||||
|  | ||||
|   add_index "follows", ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree | ||||
|  | ||||
|   create_table "statuses", force: :cascade do |t| | ||||
|     t.string   "uri",        default: "", null: false | ||||
|     t.integer  "account_id",              null: false | ||||
| @@ -42,4 +54,21 @@ ActiveRecord::Schema.define(version: 20160220211917) do | ||||
|  | ||||
|   add_index "statuses", ["uri"], name: "index_statuses_on_uri", unique: true, using: :btree | ||||
|  | ||||
|   create_table "stream_entries", force: :cascade do |t| | ||||
|     t.integer  "account_id" | ||||
|     t.integer  "activity_id" | ||||
|     t.string   "activity_type" | ||||
|     t.datetime "created_at",    null: false | ||||
|     t.datetime "updated_at",    null: false | ||||
|   end | ||||
|  | ||||
|   create_table "users", force: :cascade do |t| | ||||
|     t.string   "email",      default: "", null: false | ||||
|     t.integer  "account_id",              null: false | ||||
|     t.datetime "created_at",              null: false | ||||
|     t.datetime "updated_at",              null: false | ||||
|   end | ||||
|  | ||||
|   add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree | ||||
|  | ||||
| end | ||||
|   | ||||
							
								
								
									
										5
									
								
								spec/controllers/atom_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/atom_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe AtomController, type: :controller do | ||||
|  | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/controllers/home_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/home_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe HomeController, type: :controller do | ||||
|  | ||||
| end | ||||
							
								
								
									
										12
									
								
								spec/controllers/profile_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								spec/controllers/profile_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe ProfileController, type: :controller do | ||||
|  | ||||
|   describe "GET #show" do | ||||
|     it "returns http success" do | ||||
|       get :show | ||||
|       expect(response).to have_http_status(:success) | ||||
|     end | ||||
|   end | ||||
|  | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/controllers/xrd_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/xrd_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe XrdController, type: :controller do | ||||
|  | ||||
| end | ||||
							
								
								
									
										15
									
								
								spec/helpers/atom_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/atom_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| # Specs in this file have access to a helper object that includes | ||||
| # the AtomHelper. For example: | ||||
| # | ||||
| # describe AtomHelper do | ||||
| #   describe "string concat" do | ||||
| #     it "concats two strings with spaces" do | ||||
| #       expect(helper.concat_strings("this","that")).to eq("this that") | ||||
| #     end | ||||
| #   end | ||||
| # end | ||||
| RSpec.describe AtomHelper, type: :helper do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										15
									
								
								spec/helpers/home_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/home_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| # Specs in this file have access to a helper object that includes | ||||
| # the HomeHelper. For example: | ||||
| # | ||||
| # describe HomeHelper do | ||||
| #   describe "string concat" do | ||||
| #     it "concats two strings with spaces" do | ||||
| #       expect(helper.concat_strings("this","that")).to eq("this that") | ||||
| #     end | ||||
| #   end | ||||
| # end | ||||
| RSpec.describe HomeHelper, type: :helper do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										15
									
								
								spec/helpers/profile_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/profile_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| # Specs in this file have access to a helper object that includes | ||||
| # the ProfileHelper. For example: | ||||
| # | ||||
| # describe ProfileHelper do | ||||
| #   describe "string concat" do | ||||
| #     it "concats two strings with spaces" do | ||||
| #       expect(helper.concat_strings("this","that")).to eq("this that") | ||||
| #     end | ||||
| #   end | ||||
| # end | ||||
| RSpec.describe ProfileHelper, type: :helper do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										15
									
								
								spec/helpers/xrd_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/xrd_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| # Specs in this file have access to a helper object that includes | ||||
| # the XrdHelper. For example: | ||||
| # | ||||
| # describe XrdHelper do | ||||
| #   describe "string concat" do | ||||
| #     it "concats two strings with spaces" do | ||||
| #       expect(helper.concat_strings("this","that")).to eq("this that") | ||||
| #     end | ||||
| #   end | ||||
| # end | ||||
| RSpec.describe XrdHelper, type: :helper do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/models/follow_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/follow_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe Follow, type: :model do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/models/stream_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/stream_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe Stream, type: :model do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/models/user_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/user_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe User, type: :model do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
							
								
								
									
										5
									
								
								spec/views/profile/show.html.haml_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/views/profile/show.html.haml_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe "profile/show.html.haml", type: :view do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
		Reference in New Issue
	
	Block a user