Do not autoplay videos, display play button instead. Use expiring links when using S3. Do not keep originals
for avatars/headers, resize avatars down to 120x120 instead of 300x300. Set cache headers on S3 stuff, also make it private (aka only accessible via expiring links to prevent hotlinking)
This commit is contained in:
		
							parent
							
								
									290ffb63cd
								
							
						
					
					
						commit
						80c44ed9c1
					
				
					 18 changed files with 34 additions and 27 deletions
				
			
		| 
						 | 
					@ -53,7 +53,8 @@ const VideoPlayer = React.createClass({
 | 
				
			||||||
  propTypes: {
 | 
					  propTypes: {
 | 
				
			||||||
    media: ImmutablePropTypes.map.isRequired,
 | 
					    media: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    width: React.PropTypes.number,
 | 
					    width: React.PropTypes.number,
 | 
				
			||||||
    height: React.PropTypes.number
 | 
					    height: React.PropTypes.number,
 | 
				
			||||||
 | 
					    sensitive: React.PropTypes.bool
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getDefaultProps () {
 | 
					  getDefaultProps () {
 | 
				
			||||||
| 
						 | 
					@ -102,6 +103,12 @@ const VideoPlayer = React.createClass({
 | 
				
			||||||
          <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
 | 
					          <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					    } else if (!sensitive && !this.state.visible) {
 | 
				
			||||||
 | 
					      return (
 | 
				
			||||||
 | 
					        <div style={{ cursor: 'pointer', position: 'relative', marginTop: '8px', width: `${width}px`, height: `${height}px`, background: `url(${media.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }} onClick={this.handleOpen}>
 | 
				
			||||||
 | 
					          <div style={{ position: 'absolute', top: '50%', left: '50%', fontSize: '36px', transform: 'translate(-50%, -50%)', padding: '5px', borderRadius: '100px', color: 'rgba(255, 255, 255, 0.8)' }}><i className='fa fa-play' /></div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,13 +112,11 @@ module AtomBuilderHelper
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def link_enclosure(xml, media)
 | 
					  def link_enclosure(xml, media)
 | 
				
			||||||
    xml.link(rel: 'enclosure', href: full_asset_url(media.file.url), type: media.file_content_type, length: media.file_file_size)
 | 
					    xml.link(rel: 'enclosure', href: full_asset_url(media.file.url(:original, false)), type: media.file_content_type, length: media.file_file_size)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def link_avatar(xml, account)
 | 
					  def link_avatar(xml, account)
 | 
				
			||||||
    single_link_avatar(xml, account, :large, 300)
 | 
					    single_link_avatar(xml, account, :original, 120)
 | 
				
			||||||
    # single_link_avatar(xml, account, :medium, 96)
 | 
					 | 
				
			||||||
    # single_link_avatar(xml, account, :small,  48)
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def logo(xml, url)
 | 
					  def logo(xml, url)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ module StreamEntriesHelper
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def avatar_for_status_url(status)
 | 
					  def avatar_for_status_url(status)
 | 
				
			||||||
    status.reblog? ? status.reblog.account.avatar.url(:large) : status.account.avatar.url(:large)
 | 
					    status.reblog? ? status.reblog.account.avatar.expiring_url(3600, :original) : status.account.avatar.expiring_url(3600, :original)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def entry_classes(status, is_predecessor, is_successor, include_threads)
 | 
					  def entry_classes(status, is_predecessor, is_successor, include_threads)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,12 +13,12 @@ class Account < ApplicationRecord
 | 
				
			||||||
  validates :username, presence: true, uniqueness: { scope: :domain, case_sensitive: true }, unless: 'local?'
 | 
					  validates :username, presence: true, uniqueness: { scope: :domain, case_sensitive: true }, unless: 'local?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Avatar upload
 | 
					  # Avatar upload
 | 
				
			||||||
  has_attached_file :avatar, styles: { large: '300x300#' }, convert_options: { all: '-strip' }
 | 
					  has_attached_file :avatar, styles: { original: '120x120#' }, convert_options: { all: '-quality 80 -strip' }
 | 
				
			||||||
  validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
 | 
					  validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
 | 
				
			||||||
  validates_attachment_size :avatar, less_than: 2.megabytes
 | 
					  validates_attachment_size :avatar, less_than: 2.megabytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Header upload
 | 
					  # Header upload
 | 
				
			||||||
  has_attached_file :header, styles: { medium: '700x335#' }, convert_options: { all: '-strip' }
 | 
					  has_attached_file :header, styles: { original: '700x335#' }, convert_options: { all: '-quality 80 -strip' }
 | 
				
			||||||
  validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
 | 
					  validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
 | 
				
			||||||
  validates_attachment_size :header, less_than: 2.megabytes
 | 
					  validates_attachment_size :header, less_than: 2.megabytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ class MediaAttachment < ApplicationRecord
 | 
				
			||||||
  has_attached_file :file,
 | 
					  has_attached_file :file,
 | 
				
			||||||
                    styles: -> (f) { file_styles f },
 | 
					                    styles: -> (f) { file_styles f },
 | 
				
			||||||
                    processors: -> (f) { f.video? ? [:transcoder] : [:thumbnail] },
 | 
					                    processors: -> (f) { f.video? ? [:transcoder] : [:thumbnail] },
 | 
				
			||||||
                    convert_options: { all: '-strip' }
 | 
					                    convert_options: { all: '-quality 80 -strip' }
 | 
				
			||||||
  validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES
 | 
					  validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES
 | 
				
			||||||
  validates_attachment_size :file, less_than: 4.megabytes
 | 
					  validates_attachment_size :file, less_than: 4.megabytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
.account-grid-card
 | 
					.account-grid-card
 | 
				
			||||||
  .account-grid-card__header
 | 
					  .account-grid-card__header
 | 
				
			||||||
    .avatar= image_tag account.avatar.url(:medium)
 | 
					    .avatar= image_tag account.avatar.expiring_url(3600, :original)
 | 
				
			||||||
    .name
 | 
					    .name
 | 
				
			||||||
      = link_to TagManager.instance.url_for(account) do
 | 
					      = link_to TagManager.instance.url_for(account) do
 | 
				
			||||||
        %span.display_name= display_name(account)
 | 
					        %span.display_name= display_name(account)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
.card{ style: "background-image: url(#{@account.header.url(:medium)})" }
 | 
					.card{ style: "background-image: url(#{@account.header.expiring_url(3600, :original)})" }
 | 
				
			||||||
  - if user_signed_in? && current_account.id != @account.id
 | 
					  - if user_signed_in? && current_account.id != @account.id
 | 
				
			||||||
    .controls
 | 
					    .controls
 | 
				
			||||||
      - if current_account.following?(@account)
 | 
					      - if current_account.following?(@account)
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
      - else
 | 
					      - else
 | 
				
			||||||
        = link_to t('accounts.follow'), follow_account_path(@account), data: { method: :post }, class: 'button'
 | 
					        = link_to t('accounts.follow'), follow_account_path(@account), data: { method: :post }, class: 'button'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .avatar= image_tag @account.avatar.url(:large)
 | 
					  .avatar= image_tag @account.avatar.expiring_url(3600, :original)
 | 
				
			||||||
  %h1.name
 | 
					  %h1.name
 | 
				
			||||||
    = display_name(@account)
 | 
					    = display_name(@account)
 | 
				
			||||||
    %small= "@#{@account.username}"
 | 
					    %small= "@#{@account.username}"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ Nokogiri::XML::Builder.new do |xml|
 | 
				
			||||||
    title      xml, @account.display_name
 | 
					    title      xml, @account.display_name
 | 
				
			||||||
    subtitle   xml, @account.note
 | 
					    subtitle   xml, @account.note
 | 
				
			||||||
    updated_at xml, stream_updated_at
 | 
					    updated_at xml, stream_updated_at
 | 
				
			||||||
    logo       xml, full_asset_url(@account.avatar.url(:medium, false))
 | 
					    logo       xml, full_asset_url(@account.avatar.expiring_url(3600, :original))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    author(xml) do
 | 
					    author(xml) do
 | 
				
			||||||
      include_author xml, @account
 | 
					      include_author xml, @account
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,8 +4,8 @@ attributes :id, :username, :acct, :display_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node(:note)            { |account| Formatter.instance.simplified_format(account) }
 | 
					node(:note)            { |account| Formatter.instance.simplified_format(account) }
 | 
				
			||||||
node(:url)             { |account| TagManager.instance.url_for(account) }
 | 
					node(:url)             { |account| TagManager.instance.url_for(account) }
 | 
				
			||||||
node(:avatar)          { |account| full_asset_url(account.avatar.url(:large, false)) }
 | 
					node(:avatar)          { |account| full_asset_url(account.avatar.expiring_url(3600, :original)) }
 | 
				
			||||||
node(:header)          { |account| full_asset_url(account.header.url(:medium, false)) }
 | 
					node(:header)          { |account| full_asset_url(account.header.expiring_url(3600, :original)) }
 | 
				
			||||||
node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : (account.try(:followers_count) || account.followers.count) }
 | 
					node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : (account.try(:followers_count) || account.followers.count) }
 | 
				
			||||||
node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : (account.try(:following_count) || account.following.count) }
 | 
					node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : (account.try(:following_count) || account.following.count) }
 | 
				
			||||||
node(:statuses_count)  { |account| defined?(@statuses_counts_map)  ? (@statuses_counts_map[account.id]  || 0) : (account.try(:statuses_count)  || account.statuses.count) }
 | 
					node(:statuses_count)  { |account| defined?(@statuses_counts_map)  ? (@statuses_counts_map[account.id]  || 0) : (account.try(:statuses_count)  || account.statuses.count) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
object @media
 | 
					object @media
 | 
				
			||||||
attribute :id, :type
 | 
					attribute :id, :type
 | 
				
			||||||
node(:url) { |media| full_asset_url(media.file.url) }
 | 
					node(:url) { |media| full_asset_url(media.file.expiring_url(3600, :original)) }
 | 
				
			||||||
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
 | 
					node(:preview_url) { |media| full_asset_url(media.file.expiring_url(3600, :small)) }
 | 
				
			||||||
node(:text_url) { |media| medium_url(media) }
 | 
					node(:text_url) { |media| medium_url(media) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
attributes :id, :remote_url, :type
 | 
					attributes :id, :remote_url, :type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node(:url)         { |media| full_asset_url(media.file.url) }
 | 
					node(:url)         { |media| full_asset_url(media.file.expiring_url(3600, :original)) }
 | 
				
			||||||
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
 | 
					node(:preview_url) { |media| full_asset_url(media.file.expiring_url(3600, :small)) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@
 | 
				
			||||||
      - if (status.reblog? ? status.reblog : status).media_attachments.size > 0
 | 
					      - if (status.reblog? ? status.reblog : status).media_attachments.size > 0
 | 
				
			||||||
        %ul.media-attachments
 | 
					        %ul.media-attachments
 | 
				
			||||||
          - (status.reblog? ? status.reblog : status).media_attachments.each do |media|
 | 
					          - (status.reblog? ? status.reblog : status).media_attachments.each do |media|
 | 
				
			||||||
            %li.transparent-background= link_to '', media.file.url, style: "background-image: url(#{media.file.url(:small)})", target: '_blank'
 | 
					            %li.transparent-background= link_to '', media.file.expiring_url(3600, :original), style: "background-image: url(#{media.file.expiring_url(3600, :small)})", target: '_blank'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- if include_threads
 | 
					- if include_threads
 | 
				
			||||||
  = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true }
 | 
					  = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
  %meta{ name: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
 | 
					  %meta{ name: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
 | 
				
			||||||
  %meta{ name: 'og:article:author', content: @account.username }/
 | 
					  %meta{ name: 'og:article:author', content: @account.username }/
 | 
				
			||||||
  %meta{ name: 'og:description', content: @stream_entry.activity.content }/
 | 
					  %meta{ name: 'og:description', content: @stream_entry.activity.content }/
 | 
				
			||||||
  %meta{ name: 'og:image', content: @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0 ? full_asset_url(@stream_entry.activity.media_attachments.first.file.url(:small)) : full_asset_url(@account.avatar.url(:large)) }/
 | 
					  %meta{ name: 'og:image', content: @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0 ? full_asset_url(@stream_entry.activity.media_attachments.first.file.expiring_url(3600, :small)) : full_asset_url(@account.avatar.expiring_url(3600, :original)) }/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.activity-stream.activity-stream-headless
 | 
					.activity-stream.activity-stream-headless
 | 
				
			||||||
  = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
 | 
					  = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
if ENV['S3_ENABLED'] == 'true'
 | 
					if ENV['S3_ENABLED'] == 'true'
 | 
				
			||||||
  Aws.eager_autoload!(services: %w(S3))
 | 
					  Aws.eager_autoload!(services: %w(S3))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Paperclip::Attachment.default_options[:storage]      = :s3
 | 
					  Paperclip::Attachment.default_options[:storage]        = :s3
 | 
				
			||||||
  Paperclip::Attachment.default_options[:s3_protocol]  = 'https'
 | 
					  Paperclip::Attachment.default_options[:s3_protocol]    = 'https'
 | 
				
			||||||
  Paperclip::Attachment.default_options[:url]          = ':s3_domain_url'
 | 
					  Paperclip::Attachment.default_options[:url]            = ':s3_domain_url'
 | 
				
			||||||
  Paperclip::Attachment.default_options[:s3_host_name] = "s3-#{ENV.fetch('S3_REGION')}.amazonaws.com"
 | 
					  Paperclip::Attachment.default_options[:s3_host_name]   = "s3-#{ENV.fetch('S3_REGION')}.amazonaws.com"
 | 
				
			||||||
  Paperclip::Attachment.default_options[:path]         = '/:class/:attachment/:id_partition/:style/:filename'
 | 
					  Paperclip::Attachment.default_options[:path]           = '/:class/:attachment/:id_partition/:style/:filename'
 | 
				
			||||||
 | 
					  Paperclip::Attachment.default_options[:s3_headers]     = { 'Cache-Control' => 'max-age=315576000', 'Expires' => 10.years.from_now.httpdate }
 | 
				
			||||||
 | 
					  Paperclip::Attachment.default_options[:s3_permissions] = :private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  unless ENV['S3_CLOUDFRONT_HOST'].blank?
 | 
					  unless ENV['S3_CLOUDFRONT_HOST'].blank?
 | 
				
			||||||
    Paperclip::Attachment.default_options[:url]           = ':s3_alias_url'
 | 
					    Paperclip::Attachment.default_options[:url]           = ':s3_alias_url'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1 KiB  | 
| 
		 Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 681 B  | 
| 
						 | 
					@ -162,7 +162,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do
 | 
				
			||||||
    let(:account) { Fabricate(:account, username: 'alice') }
 | 
					    let(:account) { Fabricate(:account, username: 'alice') }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'creates a link' do
 | 
					    it 'creates a link' do
 | 
				
			||||||
      expect(used_with_namespaces { |xml| helper.link_avatar(xml, account) }).to match '<link rel="avatar" type="" media:width="300" media:height="300" href="http://test.host/avatars/large/missing.png"/>'
 | 
					      expect(used_with_namespaces { |xml| helper.link_avatar(xml, account) }).to match '<link rel="avatar" type="" media:width="120" media:height="120" href="http://test.host/avatars/original/missing.png"/>'
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue