~cytrogen/masto-fe

6084461cd0a6f4e7e4a3c0aa660484b7eee72d14 — Eugen Rochko 2 years ago c35e3cb
Change unauthenticated responses to be cached in REST API (#24348)

29 files changed, 67 insertions(+), 9 deletions(-)

M app/controllers/api/base_controller.rb
M app/controllers/api/v1/accounts/follower_accounts_controller.rb
M app/controllers/api/v1/accounts/following_accounts_controller.rb
M app/controllers/api/v1/accounts/lookup_controller.rb
M app/controllers/api/v1/accounts/statuses_controller.rb
M app/controllers/api/v1/accounts_controller.rb
M app/controllers/api/v1/custom_emojis_controller.rb
M app/controllers/api/v1/directories_controller.rb
M app/controllers/api/v1/instances/activity_controller.rb
M app/controllers/api/v1/instances/domain_blocks_controller.rb
M app/controllers/api/v1/instances/extended_descriptions_controller.rb
M app/controllers/api/v1/instances/peers_controller.rb
M app/controllers/api/v1/instances/privacy_policies_controller.rb
M app/controllers/api/v1/instances/rules_controller.rb
M app/controllers/api/v1/instances/translation_languages_controller.rb
M app/controllers/api/v1/instances_controller.rb
M app/controllers/api/v1/polls_controller.rb
M app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
M app/controllers/api/v1/statuses/histories_controller.rb
M app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
M app/controllers/api/v1/statuses_controller.rb
M app/controllers/api/v1/tags_controller.rb
M app/controllers/api/v1/timelines/public_controller.rb
M app/controllers/api/v1/timelines/tag_controller.rb
M app/controllers/api/v1/trends/links_controller.rb
M app/controllers/api/v1/trends/statuses_controller.rb
M app/controllers/api/v1/trends/tags_controller.rb
M app/controllers/api/v2/instances_controller.rb
A app/controllers/concerns/api_caching_concern.rb
M app/controllers/api/base_controller.rb => app/controllers/api/base_controller.rb +3 -0
@@ 6,6 6,7 @@ class Api::BaseController < ApplicationController

  include RateLimitHeaders
  include AccessTokenTrackingConcern
  include ApiCachingConcern

  skip_before_action :store_current_location
  skip_before_action :require_functional!, unless: :whitelist_mode?


@@ 13,6 14,8 @@ class Api::BaseController < ApplicationController
  before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
  before_action :require_not_suspended!

  vary_by 'Authorization'

  protect_from_forgery with: :null_session

  content_security_policy do |p|

M app/controllers/api/v1/accounts/follower_accounts_controller.rb => app/controllers/api/v1/accounts/follower_accounts_controller.rb +1 -0
@@ 6,6 6,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
  after_action :insert_pagination_headers

  def index
    cache_if_unauthenticated!
    @accounts = load_accounts
    render json: @accounts, each_serializer: REST::AccountSerializer
  end

M app/controllers/api/v1/accounts/following_accounts_controller.rb => app/controllers/api/v1/accounts/following_accounts_controller.rb +1 -0
@@ 6,6 6,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
  after_action :insert_pagination_headers

  def index
    cache_if_unauthenticated!
    @accounts = load_accounts
    render json: @accounts, each_serializer: REST::AccountSerializer
  end

M app/controllers/api/v1/accounts/lookup_controller.rb => app/controllers/api/v1/accounts/lookup_controller.rb +1 -0
@@ 5,6 5,7 @@ class Api::V1::Accounts::LookupController < Api::BaseController
  before_action :set_account

  def show
    cache_if_unauthenticated!
    render json: @account, serializer: REST::AccountSerializer
  end


M app/controllers/api/v1/accounts/statuses_controller.rb => app/controllers/api/v1/accounts/statuses_controller.rb +1 -0
@@ 7,6 7,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
  after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }

  def index
    cache_if_unauthenticated!
    @statuses = load_statuses
    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
  end

M app/controllers/api/v1/accounts_controller.rb => app/controllers/api/v1/accounts_controller.rb +1 -0
@@ 18,6 18,7 @@ class Api::V1::AccountsController < Api::BaseController
  override_rate_limit_headers :follow, family: :follows

  def show
    cache_if_unauthenticated!
    render json: @account, serializer: REST::AccountSerializer
  end


M app/controllers/api/v1/custom_emojis_controller.rb => app/controllers/api/v1/custom_emojis_controller.rb +3 -1
@@ 1,8 1,10 @@
# frozen_string_literal: true

class Api::V1::CustomEmojisController < Api::BaseController
  vary_by ''

  def index
    expires_in 3.minutes, public: true
    cache_even_if_authenticated!
    render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.listed.includes(:category) }
  end
end

M app/controllers/api/v1/directories_controller.rb => app/controllers/api/v1/directories_controller.rb +1 -0
@@ 5,6 5,7 @@ class Api::V1::DirectoriesController < Api::BaseController
  before_action :set_accounts

  def show
    cache_if_unauthenticated!
    render json: @accounts, each_serializer: REST::AccountSerializer
  end


M app/controllers/api/v1/instances/activity_controller.rb => app/controllers/api/v1/instances/activity_controller.rb +3 -1
@@ 5,8 5,10 @@ class Api::V1::Instances::ActivityController < Api::BaseController

  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?

  vary_by ''

  def show
    expires_in 1.day, public: true
    cache_even_if_authenticated!
    render_with_cache json: :activity, expires_in: 1.day
  end


M app/controllers/api/v1/instances/domain_blocks_controller.rb => app/controllers/api/v1/instances/domain_blocks_controller.rb +3 -1
@@ 6,8 6,10 @@ class Api::V1::Instances::DomainBlocksController < Api::BaseController
  before_action :require_enabled_api!
  before_action :set_domain_blocks

  vary_by ''

  def index
    expires_in 3.minutes, public: true
    cache_even_if_authenticated!
    render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?))
  end


M app/controllers/api/v1/instances/extended_descriptions_controller.rb => app/controllers/api/v1/instances/extended_descriptions_controller.rb +3 -1
@@ 5,8 5,10 @@ class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController

  before_action :set_extended_description

  vary_by ''

  def show
    expires_in 3.minutes, public: true
    cache_even_if_authenticated!
    render json: @extended_description, serializer: REST::ExtendedDescriptionSerializer
  end


M app/controllers/api/v1/instances/peers_controller.rb => app/controllers/api/v1/instances/peers_controller.rb +3 -1
@@ 5,8 5,10 @@ class Api::V1::Instances::PeersController < Api::BaseController

  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?

  vary_by ''

  def index
    expires_in 1.day, public: true
    cache_even_if_authenticated!
    render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
  end


M app/controllers/api/v1/instances/privacy_policies_controller.rb => app/controllers/api/v1/instances/privacy_policies_controller.rb +3 -1
@@ 5,8 5,10 @@ class Api::V1::Instances::PrivacyPoliciesController < Api::BaseController

  before_action :set_privacy_policy

  vary_by ''

  def show
    expires_in 1.day, public: true
    cache_even_if_authenticated!
    render json: @privacy_policy, serializer: REST::PrivacyPolicySerializer
  end


M app/controllers/api/v1/instances/rules_controller.rb => app/controllers/api/v1/instances/rules_controller.rb +3 -0
@@ 5,7 5,10 @@ class Api::V1::Instances::RulesController < Api::BaseController

  before_action :set_rules

  vary_by ''

  def index
    cache_even_if_authenticated!
    render json: @rules, each_serializer: REST::RuleSerializer
  end


M app/controllers/api/v1/instances/translation_languages_controller.rb => app/controllers/api/v1/instances/translation_languages_controller.rb +3 -1
@@ 5,8 5,10 @@ class Api::V1::Instances::TranslationLanguagesController < Api::BaseController

  before_action :set_languages

  vary_by ''

  def show
    expires_in 1.day, public: true
    cache_even_if_authenticated!
    render json: @languages
  end


M app/controllers/api/v1/instances_controller.rb => app/controllers/api/v1/instances_controller.rb +3 -1
@@ 3,8 3,10 @@
class Api::V1::InstancesController < Api::BaseController
  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?

  vary_by ''

  def show
    expires_in 3.minutes, public: true
    cache_even_if_authenticated!
    render_with_cache json: InstancePresenter.new, serializer: REST::V1::InstanceSerializer, root: 'instance'
  end
end

M app/controllers/api/v1/polls_controller.rb => app/controllers/api/v1/polls_controller.rb +1 -0
@@ 8,6 8,7 @@ class Api::V1::PollsController < Api::BaseController
  before_action :refresh_poll

  def show
    cache_if_unauthenticated!
    render json: @poll, serializer: REST::PollSerializer, include_results: true
  end


M app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb => app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb +1 -0
@@ 8,6 8,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
  after_action :insert_pagination_headers

  def index
    cache_if_unauthenticated!
    @accounts = load_accounts
    render json: @accounts, each_serializer: REST::AccountSerializer
  end

M app/controllers/api/v1/statuses/histories_controller.rb => app/controllers/api/v1/statuses/histories_controller.rb +1 -0
@@ 7,6 7,7 @@ class Api::V1::Statuses::HistoriesController < Api::BaseController
  before_action :set_status

  def show
    cache_if_unauthenticated!
    render json: @status.edits.includes(:account, status: [:account]), each_serializer: REST::StatusEditSerializer
  end


M app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb => app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb +1 -0
@@ 8,6 8,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
  after_action :insert_pagination_headers

  def index
    cache_if_unauthenticated!
    @accounts = load_accounts
    render json: @accounts, each_serializer: REST::AccountSerializer
  end

M app/controllers/api/v1/statuses_controller.rb => app/controllers/api/v1/statuses_controller.rb +3 -0
@@ 24,11 24,14 @@ class Api::V1::StatusesController < Api::BaseController
  DESCENDANTS_DEPTH_LIMIT = 20

  def show
    cache_if_unauthenticated!
    @status = cache_collection([@status], Status).first
    render json: @status, serializer: REST::StatusSerializer
  end

  def context
    cache_if_unauthenticated!

    ancestors_limit         = CONTEXT_LIMIT
    descendants_limit       = CONTEXT_LIMIT
    descendants_depth_limit = nil

M app/controllers/api/v1/tags_controller.rb => app/controllers/api/v1/tags_controller.rb +1 -0
@@ 8,6 8,7 @@ class Api::V1::TagsController < Api::BaseController
  override_rate_limit_headers :follow, family: :follows

  def show
    cache_if_unauthenticated!
    render json: @tag, serializer: REST::TagSerializer
  end


M app/controllers/api/v1/timelines/public_controller.rb => app/controllers/api/v1/timelines/public_controller.rb +1 -0
@@ 5,6 5,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }

  def show
    cache_if_unauthenticated!
    @statuses = load_statuses
    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
  end

M app/controllers/api/v1/timelines/tag_controller.rb => app/controllers/api/v1/timelines/tag_controller.rb +1 -0
@@ 5,6 5,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }

  def show
    cache_if_unauthenticated!
    @statuses = load_statuses
    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
  end

M app/controllers/api/v1/trends/links_controller.rb => app/controllers/api/v1/trends/links_controller.rb +3 -0
@@ 1,6 1,8 @@
# frozen_string_literal: true

class Api::V1::Trends::LinksController < Api::BaseController
  vary_by 'Authorization, Accept-Language'

  before_action :set_links

  after_action :insert_pagination_headers


@@ 8,6 10,7 @@ class Api::V1::Trends::LinksController < Api::BaseController
  DEFAULT_LINKS_LIMIT = 10

  def index
    cache_if_unauthenticated!
    render json: @links, each_serializer: REST::Trends::LinkSerializer
  end


M app/controllers/api/v1/trends/statuses_controller.rb => app/controllers/api/v1/trends/statuses_controller.rb +3 -0
@@ 1,11 1,14 @@
# frozen_string_literal: true

class Api::V1::Trends::StatusesController < Api::BaseController
  vary_by 'Authorization, Accept-Language'

  before_action :set_statuses

  after_action :insert_pagination_headers

  def index
    cache_if_unauthenticated!
    render json: @statuses, each_serializer: REST::StatusSerializer
  end


M app/controllers/api/v1/trends/tags_controller.rb => app/controllers/api/v1/trends/tags_controller.rb +1 -0
@@ 8,6 8,7 @@ class Api::V1::Trends::TagsController < Api::BaseController
  DEFAULT_TAGS_LIMIT = 10

  def index
    cache_if_unauthenticated!
    render json: @tags, each_serializer: REST::TagSerializer, relationships: TagRelationshipsPresenter.new(@tags, current_user&.account_id)
  end


M app/controllers/api/v2/instances_controller.rb => app/controllers/api/v2/instances_controller.rb +1 -1
@@ 2,7 2,7 @@

class Api::V2::InstancesController < Api::V1::InstancesController
  def show
    expires_in 3.minutes, public: true
    cache_even_if_authenticated!
    render_with_cache json: InstancePresenter.new, serializer: REST::InstanceSerializer, root: 'instance'
  end
end

A app/controllers/concerns/api_caching_concern.rb => app/controllers/concerns/api_caching_concern.rb +13 -0
@@ 0,0 1,13 @@
# frozen_string_literal: true

module ApiCachingConcern
  extend ActiveSupport::Concern

  def cache_if_unauthenticated!
    expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
  end

  def cache_even_if_authenticated!
    expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless whitelist_mode?
  end
end