From 709c6685a90bb819696566cc9e42e587546d72dc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 22 Feb 2016 16:00:20 +0100 Subject: Made some progress --- app/api/mastodon/entities.rb | 2 + app/api/mastodon/ostatus.rb | 15 ++++--- app/api/mastodon/rest.rb | 25 +++++++++++ app/assets/javascripts/application.js | 1 - app/assets/javascripts/atom.coffee | 3 ++ app/assets/javascripts/home.coffee | 3 ++ app/assets/javascripts/profile.coffee | 3 ++ app/assets/javascripts/xrd.coffee | 3 ++ app/assets/stylesheets/atom.scss | 3 ++ app/assets/stylesheets/home.scss | 3 ++ app/assets/stylesheets/profile.scss | 3 ++ app/assets/stylesheets/xrd.scss | 3 ++ app/controllers/atom_controller.rb | 14 +++++++ app/controllers/home_controller.rb | 4 ++ app/controllers/profile_controller.rb | 4 ++ app/controllers/xrd_controller.rb | 39 +++++++++++++++++ app/helpers/application_helper.rb | 17 ++++++++ app/helpers/atom_helper.rb | 5 +++ app/helpers/home_helper.rb | 2 + app/helpers/profile_helper.rb | 2 + app/helpers/xrd_helper.rb | 2 + app/models/account.rb | 32 ++++++++++++++ app/models/follow.rb | 8 ++++ app/models/status.rb | 4 ++ app/models/stream_entry.rb | 33 +++++++++++++++ app/models/user.rb | 3 ++ app/services/fetch_feed_service.rb | 12 +++++- app/services/follow_remote_account_service.rb | 57 +++++++++++++++++++++++++ app/services/follow_remote_user_service.rb | 60 --------------------------- app/services/follow_service.rb | 12 ++++++ app/services/process_feed_service.rb | 20 +++++++++ app/services/process_feed_update_service.rb | 20 --------- app/services/process_interaction_service.rb | 38 +++++++++++++++++ app/services/setup_local_account_service.rb | 14 +++++++ app/views/atom/user_stream.xml.ruby | 35 ++++++++++++++++ app/views/home/index.html.haml | 1 + app/views/layouts/application.html.erb | 14 ------- app/views/layouts/application.html.haml | 10 +++++ app/views/profile/show.html.haml | 2 + app/views/xrd/host_meta.xml.ruby | 5 +++ app/views/xrd/webfinger.xml.ruby | 8 ++++ 41 files changed, 440 insertions(+), 104 deletions(-) create mode 100644 app/assets/javascripts/atom.coffee create mode 100644 app/assets/javascripts/home.coffee create mode 100644 app/assets/javascripts/profile.coffee create mode 100644 app/assets/javascripts/xrd.coffee create mode 100644 app/assets/stylesheets/atom.scss create mode 100644 app/assets/stylesheets/home.scss create mode 100644 app/assets/stylesheets/profile.scss create mode 100644 app/assets/stylesheets/xrd.scss create mode 100644 app/controllers/atom_controller.rb create mode 100644 app/controllers/home_controller.rb create mode 100644 app/controllers/profile_controller.rb create mode 100644 app/controllers/xrd_controller.rb create mode 100644 app/helpers/atom_helper.rb create mode 100644 app/helpers/home_helper.rb create mode 100644 app/helpers/profile_helper.rb create mode 100644 app/helpers/xrd_helper.rb create mode 100644 app/models/follow.rb create mode 100644 app/models/stream_entry.rb create mode 100644 app/models/user.rb create mode 100644 app/services/follow_remote_account_service.rb delete mode 100644 app/services/follow_remote_user_service.rb create mode 100644 app/services/follow_service.rb create mode 100644 app/services/process_feed_service.rb delete mode 100644 app/services/process_feed_update_service.rb create mode 100644 app/services/process_interaction_service.rb create mode 100644 app/services/setup_local_account_service.rb create mode 100644 app/views/atom/user_stream.xml.ruby create mode 100644 app/views/home/index.html.haml delete mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/layouts/application.html.haml create mode 100644 app/views/profile/show.html.haml create mode 100644 app/views/xrd/host_meta.xml.ruby create mode 100644 app/views/xrd/webfinger.xml.ruby (limited to 'app') diff --git a/app/api/mastodon/entities.rb b/app/api/mastodon/entities.rb index a3f40ec48..2e56a67df 100644 --- a/app/api/mastodon/entities.rb +++ b/app/api/mastodon/entities.rb @@ -3,6 +3,8 @@ module Mastodon class Account < Grape::Entity expose :username expose :domain + expose :display_name + expose :note end class Status < Grape::Entity diff --git a/app/api/mastodon/ostatus.rb b/app/api/mastodon/ostatus.rb index fcde980f7..4676bc429 100644 --- a/app/api/mastodon/ostatus.rb +++ b/app/api/mastodon/ostatus.rb @@ -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 diff --git a/app/api/mastodon/rest.rb b/app/api/mastodon/rest.rb index e011ab34d..eaf337938 100644 --- a/app/api/mastodon/rest.rb +++ b/app/api/mastodon/rest.rb @@ -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 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e07c5a830..646c5aba4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,5 +12,4 @@ // //= require jquery //= require jquery_ujs -//= require turbolinks //= require_tree . diff --git a/app/assets/javascripts/atom.coffee b/app/assets/javascripts/atom.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/atom.coffee @@ -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/ diff --git a/app/assets/javascripts/home.coffee b/app/assets/javascripts/home.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/home.coffee @@ -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/ diff --git a/app/assets/javascripts/profile.coffee b/app/assets/javascripts/profile.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/profile.coffee @@ -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/ diff --git a/app/assets/javascripts/xrd.coffee b/app/assets/javascripts/xrd.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/xrd.coffee @@ -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/ diff --git a/app/assets/stylesheets/atom.scss b/app/assets/stylesheets/atom.scss new file mode 100644 index 000000000..888698db3 --- /dev/null +++ b/app/assets/stylesheets/atom.scss @@ -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/ diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss new file mode 100644 index 000000000..7131aac4d --- /dev/null +++ b/app/assets/stylesheets/home.scss @@ -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/ diff --git a/app/assets/stylesheets/profile.scss b/app/assets/stylesheets/profile.scss new file mode 100644 index 000000000..22ee50876 --- /dev/null +++ b/app/assets/stylesheets/profile.scss @@ -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/ diff --git a/app/assets/stylesheets/xrd.scss b/app/assets/stylesheets/xrd.scss new file mode 100644 index 000000000..62391c7d3 --- /dev/null +++ b/app/assets/stylesheets/xrd.scss @@ -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/ diff --git a/app/controllers/atom_controller.rb b/app/controllers/atom_controller.rb new file mode 100644 index 000000000..e0b45c580 --- /dev/null +++ b/app/controllers/atom_controller.rb @@ -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 diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 000000000..95f29929c --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,4 @@ +class HomeController < ApplicationController + def index + end +end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb new file mode 100644 index 000000000..2374318eb --- /dev/null +++ b/app/controllers/profile_controller.rb @@ -0,0 +1,4 @@ +class ProfileController < ApplicationController + def show + end +end diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb new file mode 100644 index 000000000..4c8e958e6 --- /dev/null +++ b/app/controllers/xrd_controller.rb @@ -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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be7945..29e444a32 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/helpers/atom_helper.rb b/app/helpers/atom_helper.rb new file mode 100644 index 000000000..a42a49946 --- /dev/null +++ b/app/helpers/atom_helper.rb @@ -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 diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 000000000..23de56ac6 --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb new file mode 100644 index 000000000..5a0d6b31f --- /dev/null +++ b/app/helpers/profile_helper.rb @@ -0,0 +1,2 @@ +module ProfileHelper +end diff --git a/app/helpers/xrd_helper.rb b/app/helpers/xrd_helper.rb new file mode 100644 index 000000000..6b273e122 --- /dev/null +++ b/app/helpers/xrd_helper.rb @@ -0,0 +1,2 @@ +module XrdHelper +end diff --git a/app/models/account.rb b/app/models/account.rb index c0b153794..90e8d7610 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -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 diff --git a/app/models/follow.rb b/app/models/follow.rb new file mode 100644 index 000000000..eec01b9ba --- /dev/null +++ b/app/models/follow.rb @@ -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 diff --git a/app/models/status.rb b/app/models/status.rb index a1278ccaa..d98297643 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -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 diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb new file mode 100644 index 000000000..cee151a07 --- /dev/null +++ b/app/models/stream_entry.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..ccfa54e4f --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,3 @@ +class User < ActiveRecord::Base + belongs_to :account, inverse_of: :user +end diff --git a/app/services/fetch_feed_service.rb b/app/services/fetch_feed_service.rb index 3b8efbe3b..059d65925 100644 --- a/app/services/fetch_feed_service.rb +++ b/app/services/fetch_feed_service.rb @@ -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 diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb new file mode 100644 index 000000000..41f8fa4a0 --- /dev/null +++ b/app/services/follow_remote_account_service.rb @@ -0,0 +1,57 @@ +class FollowRemoteAccountService + include ApplicationHelper + + 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:#{uri}") + + account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href + account.salmon_url = data.link('salmon').href + account.public_key = magic_key_to_pem(data.link('magic-public-key').href) + account.private_key = nil + + account.secret = SecureRandom.hex + account.verify_token = SecureRandom.hex + + 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? + + account.uri = feed.at_xpath('/xmlns:author/xmlns:uri').content + account.hub_url = hubs.first.attribute('href').value + account.save! + + subscription = account.subscription(subscription_url(account)) + subscription.subscribe + rescue Goldfinger::Error, HTTP::Error => e + false + end + + private + + def get_feed(url) + response = http_client.get(Addressable::URI.parse(url)) + Nokogiri::XML(response) + 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 } } + + key = OpenSSL::PKey::RSA.new + key.n = modulus + key.e = exponent + + key.to_pem + end + + def http_client + HTTP + end +end diff --git a/app/services/follow_remote_user_service.rb b/app/services/follow_remote_user_service.rb deleted file mode 100644 index f3c0e68df..000000000 --- a/app/services/follow_remote_user_service.rb +++ /dev/null @@ -1,60 +0,0 @@ -class FollowRemoteUserService - include GrapeRouteHelpers::NamedRouteMatcher - - def call(user) - username, domain = user.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}") - - account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href - account.salmon_url = data.link('salmon').href - account.public_key = magic_key_to_pem(data.link('magic-public-key').href) - account.private_key = nil - - account.secret = SecureRandom.hex - account.verify_token = SecureRandom.hex - - feed = get_feed(account.remote_url) - hubs = feed.xpath('//xmlns:link[@rel="hub"]') - - return false if hubs.empty? || hubs.first.attribute('href').nil? - - account.hub_url = hubs.first.attribute('href').value - account.save! - - subscription = account.subscription(subscription_url(account)) - subscription.subscribe - rescue Goldfinger::Error, HTTP::Error => e - false - end - - private - - def get_feed(url) - response = http_client.get(Addressable::URI.parse(url)) - Nokogiri::XML(response) - 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 } } - - key = OpenSSL::PKey::RSA.new - key.n = modulus - key.d = exponent - - key.to_pem - end - - def http_client - HTTP - end - - def subscription_url(account) - "https://649841dc.ngrok.io/api#{subscriptions_path(id: account.id)}" - end -end diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb new file mode 100644 index 000000000..fc606730b --- /dev/null +++ b/app/services/follow_service.rb @@ -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 diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb new file mode 100644 index 000000000..f2523a313 --- /dev/null +++ b/app/services/process_feed_service.rb @@ -0,0 +1,20 @@ +class ProcessFeedService + def call(body, account) + xml = Nokogiri::XML(body) + + xml.xpath('/xmlns:feed/xmlns:entry').each do |entry| + uri = entry.at_xpath('./xmlns:id').content + status = Status.find_by(uri: uri) + + next unless status.nil? + + status = Status.new + status.account = account + status.uri = uri + status.text = entry.at_xpath('./xmlns:content').content + status.created_at = entry.at_xpath('./xmlns:published').content + status.updated_at = entry.at_xpath('./xmlns:updated').content + status.save! + end + end +end diff --git a/app/services/process_feed_update_service.rb b/app/services/process_feed_update_service.rb deleted file mode 100644 index 0585fad7a..000000000 --- a/app/services/process_feed_update_service.rb +++ /dev/null @@ -1,20 +0,0 @@ -class ProcessFeedUpdateService - def call(body, account) - xml = Nokogiri::XML(body) - - xml.xpath('/xmlns:feed/xmlns:entry').each do |entry| - uri = entry.at_xpath('./xmlns:id').content - status = Status.find_by(uri: uri) - - next unless status.nil? - - status = Status.new - status.account = account - status.uri = uri - status.text = entry.at_xpath('./xmlns:content').content - status.created_at = entry.at_xpath('./xmlns:published').content - status.updated_at = entry.at_xpath('./xmlns:updated').content - status.save! - end - end -end diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb new file mode 100644 index 000000000..8262ead8f --- /dev/null +++ b/app/services/process_interaction_service.rb @@ -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 diff --git a/app/services/setup_local_account_service.rb b/app/services/setup_local_account_service.rb new file mode 100644 index 000000000..c40e51855 --- /dev/null +++ b/app/services/setup_local_account_service.rb @@ -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 diff --git a/app/views/atom/user_stream.xml.ruby b/app/views/atom/user_stream.xml.ruby new file mode 100644 index 000000000..d418ea0ec --- /dev/null +++ b/app/views/atom/user_stream.xml.ruby @@ -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 diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml new file mode 100644 index 000000000..862374a98 --- /dev/null +++ b/app/views/home/index.html.haml @@ -0,0 +1 @@ +Mastodon diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb deleted file mode 100644 index ff0d4c865..000000000 --- a/app/views/layouts/application.html.erb +++ /dev/null @@ -1,14 +0,0 @@ - - - - Mastodon - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> - <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> - <%= csrf_meta_tags %> - - - -<%= yield %> - - - diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 000000000..0cb4e96e8 --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -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 diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml new file mode 100644 index 000000000..dcb5764ec --- /dev/null +++ b/app/views/profile/show.html.haml @@ -0,0 +1,2 @@ +%h1 Profile#show +%p Find me in app/views/profile/show.html.haml diff --git a/app/views/xrd/host_meta.xml.ruby b/app/views/xrd/host_meta.xml.ruby new file mode 100644 index 000000000..07d026471 --- /dev/null +++ b/app/views/xrd/host_meta.xml.ruby @@ -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 diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby new file mode 100644 index 000000000..7a1e9a1d3 --- /dev/null +++ b/app/views/xrd/webfinger.xml.ruby @@ -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 -- cgit