Skip to content

Instantly share code, notes, and snippets.

@takeyuweb
Last active May 24, 2017 10:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takeyuweb/c7eb01576920e37c82853ddb6f3f49ee to your computer and use it in GitHub Desktop.
Save takeyuweb/c7eb01576920e37c82853ddb6f3f49ee to your computer and use it in GitHub Desktop.
authenticate :user, lambda { |u| u.admin? } do
mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
mount PgHero::Engine, at: 'pghero', as: :pghero
end
authenticate :user, lambda { |u| u.admin? } do
mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
mount PgHero::Engine, at: 'pghero', as: :pghero
end
Dotenv::Railtie.load
Dotenv::Railtie.load
def set_pagination_headers(next_path = nil, prev_path = nil)
links = []
links << [next_path, [%w(rel next)]] if next_path
links << [prev_path, [%w(rel prev)]] if prev_path
response.headers['Link'] = LinkHeader.new(links)
end
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
if xml.root.name == 'feed'
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 TagManager.instance.url_for(@account)
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account))
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom'))
xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
xml.Link(rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}")
xml.Link(rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_follow_url}?acct={uri}")
end
end.to_xml
# Send an Atom representation of an interaction to a remote Salmon endpoint
# @param [String] Entry XML
# @param [Account] source_account
# @param [Account] target_account
def call(xml, source_account, target_account)
envelope = salmon.pack(xml, source_account.keypair)
salmon.post(target_account.salmon_url, envelope)
end
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
if xml.root.name == 'feed'
document = Ox::Document.new(version: '1.0')
document << element
('<?xml version="1.0"?>' + Ox.dump(element, effort: :tolerant)).force_encoding('UTF-8')
# Send an Atom representation of an interaction to a remote Salmon endpoint
# @param [String] Entry XML
# @param [Account] source_account
# @param [Account] target_account
def call(xml, source_account, target_account)
envelope = salmon.pack(xml, source_account.keypair)
salmon.post(target_account.salmon_url, envelope)
end
# Rate limits for the API
throttle('api', limit: 300, period: 5.minutes) do |req|
req.ip if req.path =~ /\A\/api\/v/
end
# Rate limit logins
throttle('login', limit: 5, period: 5.minutes) do |req|
req.ip if req.path == '/auth/sign_in' && req.post?
end
document = Ox::Document.new(version: '1.0')
document << element
('<?xml version="1.0"?>' + Ox.dump(element, effort: :tolerant)).force_encoding('UTF-8')
now = Time.now.utc
match_data = request.env['rack.attack.throttle_data']['api']
response.headers['X-RateLimit-Limit'] = match_data[:limit].to_s
response.headers['X-RateLimit-Remaining'] = (match_data[:limit] - match_data[:count]).to_s
response.headers['X-RateLimit-Reset'] = (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6)
# Rate limits for the API
throttle('api', limit: 300, period: 5.minutes) do |req|
req.ip if req.path =~ /\A\/api\/v/
end
# Rate limit logins
throttle('login', limit: 5, period: 5.minutes) do |req|
req.ip if req.path == '/auth/sign_in' && req.post?
end
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '/api/*', headers: :any, methods: [:post, :put, :delete, :get, :options], credentials: false, expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id']
resource '/oauth/token', headers: :any, methods: [:post], credentials: false
end
end
now = Time.now.utc
match_data = request.env['rack.attack.throttle_data']['api']
response.headers['X-RateLimit-Limit'] = match_data[:limit].to_s
response.headers['X-RateLimit-Remaining'] = (match_data[:limit] - match_data[:count]).to_s
response.headers['X-RateLimit-Reset'] = (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6)
Rack::Timeout::Logger.disable
Rack::Timeout.service_timeout = false
if Rails.env.production?
Rack::Timeout.service_timeout = 90
end
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '/api/*', headers: :any, methods: [:post, :put, :delete, :get, :options], credentials: false, expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id']
resource '/oauth/token', headers: :any, methods: [:post], credentials: false
end
end
def deliver_to_hashtags(status)
Rails.logger.debug "Delivering status #{status.id} to hashtags"
status.tags.pluck(:name).each do |hashtag|
Redis.current.publish("timeline:hashtag:#{hashtag}", @payload)
Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local?
end
end
Rack::Timeout::Logger.disable
Rack::Timeout.service_timeout = false
if Rails.env.production?
Rack::Timeout.service_timeout = 90
end
namespace = ENV.fetch('REDIS_NAMESPACE') { nil }
if namespace
Redis.current = Redis::Namespace.new(namespace, redis: redis_connection)
else
Redis.current = redis_connection
end
def deliver_to_hashtags(status)
Rails.logger.debug "Delivering status #{status.id} to hashtags"
status.tags.pluck(:name).each do |hashtag|
Redis.current.publish("timeline:hashtag:#{hashtag}", @payload)
Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local?
end
end
- content_for :header_tags do
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
# Avatar upload
has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: t('admin.settings.contact_information.username')
# frozen_string_literal: true
object @stream_entry
node(:type) { 'rich' }
node(:version) { '1.0' }
node(:title, &:title)
node(:author_name) { |entry| entry.account.display_name.blank? ? entry.account.username : entry.account.display_name }
node(:author_url) { |entry| account_url(entry.account) }
node(:provider_name) { site_hostname }
node(:provider_url) { root_url }
node(:cache_age) { 86_400 }
node(:html) { |entry| "<iframe src=\"#{embed_account_stream_entry_url(entry.account, entry)}\" style=\"width: 100%; overflow: hidden\" frameborder=\"0\" width=\"#{@width}\" height=\"#{@height}\" scrolling=\"no\"></iframe>" }
node(:width) { @width }
node(:height) { @height }
namespace = ENV.fetch('REDIS_NAMESPACE') { nil }
if namespace
Redis.current = Redis::Namespace.new(namespace, redis: redis_connection)
else
Redis.current = redis_connection
end
if target_account.local?
NotifyService.new.call(target_account, follow_request)
else
NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id)
AfterRemoteFollowRequestWorker.perform_async(follow_request.id)
end
- content_for :header_tags do
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
# frozen_string_literal: true
object @stream_entry
node(:type) { 'rich' }
node(:version) { '1.0' }
node(:title, &:title)
node(:author_name) { |entry| entry.account.display_name.blank? ? entry.account.username : entry.account.display_name }
node(:author_url) { |entry| account_url(entry.account) }
node(:provider_name) { site_hostname }
node(:provider_url) { root_url }
node(:cache_age) { 86_400 }
node(:html) { |entry| "<iframe src=\"#{embed_account_stream_entry_url(entry.account, entry)}\" style=\"width: 100%; overflow: hidden\" frameborder=\"0\" width=\"#{@width}\" height=\"#{@height}\" scrolling=\"no\"></iframe>" }
node(:width) { @width }
node(:height) { @height }
card.html = Formatter.instance.sanitize(response.html, Sanitize::Config::MASTODON_OEMBED)
class AfterRemoteFollowRequestWorker
include Sidekiq::Worker
sidekiq_options queue: 'pull', retry: 5
def perform(follow_request_id)
follow_request = FollowRequest.find(follow_request_id)
updated_account = FetchRemoteAccountService.new.call(follow_request.target_account.remote_url)
MASTODON_OEMBED ||= freeze_config merge(
RELAXED,
elements: RELAXED[:elements] + %w(audio embed iframe source video),
attributes: merge(
RELAXED[:attributes],
'audio' => %w(controls),
'embed' => %w(height src type width),
'iframe' => %w(allowfullscreen frameborder height scrolling src width),
'source' => %w(src type),
'video' => %w(controls height loop width),
'div' => [:data]
),
if target_account.local?
NotifyService.new.call(target_account, follow_request)
else
NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id)
AfterRemoteFollowRequestWorker.perform_async(follow_request.id)
end
card.html = Formatter.instance.sanitize(response.html, Sanitize::Config::MASTODON_OEMBED)
class AfterRemoteFollowRequestWorker
include Sidekiq::Worker
sidekiq_options queue: 'pull', retry: 5
def perform(follow_request_id)
follow_request = FollowRequest.find(follow_request_id)
updated_account = FetchRemoteAccountService.new.call(follow_request.target_account.remote_url)
# frozen_string_literal: true
require 'sidekiq-scheduler'
class Scheduler::SubscriptionsScheduler
include Sidekiq::Worker
def perform
logger.info 'Queueing PuSH re-subscriptions'
expiring_accounts.pluck(:id).each do |id|
Pubsubhubbub::SubscribeWorker.perform_async(id)
end
end
private
def expiring_accounts
Account.expiring(1.day.from_now).partitioned
end
end
if target_account.local?
NotifyService.new.call(target_account, follow_request)
else
NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id)
AfterRemoteFollowRequestWorker.perform_async(follow_request.id)
end
ActiveSupport::Notifications.subscribe(/process_action.action_controller/) do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
# (snip)
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.total_duration", value: event.duration
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.db_time", value: event.payload[:db_runtime]
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.view_time", value: event.payload[:view_runtime]
ActiveSupport::Notifications.instrument :performance, measurement: "#{key}.status.#{status}"
end
class RegenerationWorker
include Sidekiq::Worker
sidekiq_options queue: 'pull', backtrace: true, unique: :until_executed
# frozen_string_literal: true
require 'sidekiq-scheduler'
class Scheduler::SubscriptionsScheduler
include Sidekiq::Worker
def perform
logger.info 'Queueing PuSH re-subscriptions'
expiring_accounts.pluck(:id).each do |id|
Pubsubhubbub::SubscribeWorker.perform_async(id)
end
end
private
def expiring_accounts
Account.expiring(1.day.from_now).partitioned
end
end
# If the sign in is after a two week break, we need to regenerate their feed
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
= f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
.actions
= f.button :button, t('auth.login'), type: :submit
class RegenerationWorker
include Sidekiq::Worker
sidekiq_options queue: 'pull', backtrace: true, unique: :until_executed
- content_for :header_tags do
%script#initial-state{:type => 'application/json'}!= json_escape(render(file: 'home/initial_state', formats: :json))
= javascript_include_tag 'application', integrity: true, crossorigin: 'anonymous'
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
def file_processors(f)
if f.file_content_type == 'image/gif'
[:gif_transcoder]
elsif VIDEO_MIME_TYPES.include? f.file_content_type
[:video_transcoder]
else
[:thumbnail]
end
end
# Avatar upload
has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
ActiveSupport::Notifications.subscribe(/process_action.action_controller/) do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
# (snip)
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.total_duration", value: event.duration
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.db_time", value: event.payload[:db_runtime]
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.view_time", value: event.payload[:view_runtime]
ActiveSupport::Notifications.instrument :performance, measurement: "#{key}.status.#{status}"
end
= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
= f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
.actions
= f.button :button, t('auth.login'), type: :submit
const Mastodon = React.createClass({
# (snip)
});
export default Mastodon;
ActiveSupport::Notifications.subscribe(/performance/) do |name, _start, _finish, _id, payload|
action = payload[:action] || :increment
measurement = payload[:measurement]
value = payload[:value]
key_name = clean_name("#{name}.#{measurement}")
StatsD.send(action.to_s, key_name, (value || 1))
end
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: t('admin.settings.contact_information.username')
//= require_tree ./components
window.Mastodon = require('./components/containers/mastodon');
ActiveSupport::Notifications.subscribe(/process_action.action_controller/) do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
# (snip)
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.total_duration", value: event.duration
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.db_time", value: event.payload[:db_runtime]
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.view_time", value: event.payload[:view_runtime]
ActiveSupport::Notifications.instrument :performance, measurement: "#{key}.status.#{status}"
end
- content_for :header_tags do
%script#initial-state{:type => 'application/json'}!= json_escape(render(file: 'home/initial_state', formats: :json))
= javascript_include_tag 'application', integrity: true, crossorigin: 'anonymous'
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
config.browserify_rails.source_map_environments << 'development'
config.browserify_rails.commandline_options = '--transform [ babelify --presets [ es2015 react ] ] --extension=".jsx"'
config.browserify_rails.evaluate_node_modules = true
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: t('admin.settings.contact_information.username')
const Mastodon = React.createClass({
# (snip)
});
export default Mastodon;
- content_for :header_tags do
%script#initial-state{:type => 'application/json'}!= json_escape(render(file: 'home/initial_state', formats: :json))
= javascript_include_tag 'application', integrity: true, crossorigin: 'anonymous'
= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
//= require_tree ./components
window.Mastodon = require('./components/containers/mastodon');
config.browserify_rails.source_map_environments << 'development'
config.browserify_rails.commandline_options = '--transform [ babelify --presets [ es2015 react ] ] --extension=".jsx"'
config.browserify_rails.evaluate_node_modules = true
const Mastodon = React.createClass({
# (snip)
});
export default Mastodon;
def to_iso_s
WhatLanguage.new(:all).language_iso(text) || default_locale.to_sym
end
//= require_tree ./components
window.Mastodon = require('./components/containers/mastodon');
def call(account, text, in_reply_to = nil, options = {})
media = validate_media!(options[:media_ids])
status = account.statuses.create!(text: text,
thread: in_reply_to,
sensitive: options[:sensitive],
spoiler_text: options[:spoiler_text] || '',
visibility: options[:visibility],
language: detect_language_for(text, account),
application: options[:application])
# (snip)
end
def detect_language_for(text, account)
LanguageDetector.new(text, account).to_iso_s
end
def to_iso_s
WhatLanguage.new(:all).language_iso(text) || default_locale.to_sym
end
def call(account, text, in_reply_to = nil, options = {})
media = validate_media!(options[:media_ids])
status = account.statuses.create!(text: text,
thread: in_reply_to,
sensitive: options[:sensitive],
spoiler_text: options[:spoiler_text] || '',
visibility: options[:visibility],
language: detect_language_for(text, account),
application: options[:application])
# (snip)
end
def detect_language_for(text, account)
LanguageDetector.new(text, account).to_iso_s
end
def compliant?(url)
parsed_url = Addressable::URI.parse(url)
!parsed_url.nil? && %w(http https).include?(parsed_url.scheme) && parsed_url.host
end
def file_processors(f)
if f.file_content_type == 'image/gif'
[:gif_transcoder]
elsif VIDEO_MIME_TYPES.include? f.file_content_type
[:video_transcoder]
else
[:thumbnail]
end
end
def compliant?(url)
parsed_url = Addressable::URI.parse(url)
!parsed_url.nil? && %w(http https).include?(parsed_url.scheme) && parsed_url.host
end
class LanguageDetector
attr_reader :text, :account
def initialize(text, account = nil)
@text = text
@account = account
@identifier = CLD3::NNetLanguageIdentifier.new(1, 2048)
end
# (snip)
@identifier.find_language(text_without_urls)
if @remote_follow.valid?
resource = Goldfinger.finger("acct:#{@remote_follow.acct}")
redirect_url_link = resource&.link('http://ostatus.org/schema/1.0/subscribe')
ApplicationRecord.transaction do
status = account.statuses.create!(text: text,
thread: in_reply_to,
sensitive: options[:sensitive],
spoiler_text: options[:spoiler_text] || '',
visibility: options[:visibility],
language: detect_language_for(text, account),
application: options[:application])
attach_media(status, media)
end
def user_supplied_locale
http_accept_language.language_region_compatible_from(I18n.available_locales)
end
def detect_language_for(text, account)
LanguageDetector.new(text, account).to_iso_s
end
if @remote_follow.valid?
resource = Goldfinger.finger("acct:#{@remote_follow.acct}")
redirect_url_link = resource&.link('http://ostatus.org/schema/1.0/subscribe')
def set_pagination_headers(next_path = nil, prev_path = nil)
links = []
links << [next_path, [%w(rel next)]] if next_path
links << [prev_path, [%w(rel prev)]] if prev_path
response.headers['Link'] = LinkHeader.new(links)
end
def user_supplied_locale
http_accept_language.language_region_compatible_from(I18n.available_locales)
end
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 TagManager.instance.url_for(@account)
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account))
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom'))
xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
xml.Link(rel: 'magic-public-key', href: "data:application/magic-public-key,#{@magic_key}")
xml.Link(rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_follow_url}?acct={uri}")
end
end.to_xml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment