From 6084461cd0a6f4e7e4a3c0aa660484b7eee72d14 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 25 Apr 2023 15:41:34 +0200 Subject: [PATCH] Change unauthenticated responses to be cached in REST API (#24348) --- app/controllers/api/base_controller.rb | 3 +++ .../api/v1/accounts/follower_accounts_controller.rb | 1 + .../v1/accounts/following_accounts_controller.rb | 1 + .../api/v1/accounts/lookup_controller.rb | 1 + .../api/v1/accounts/statuses_controller.rb | 1 + app/controllers/api/v1/accounts_controller.rb | 1 + app/controllers/api/v1/custom_emojis_controller.rb | 4 +++- app/controllers/api/v1/directories_controller.rb | 1 + .../api/v1/instances/activity_controller.rb | 4 +++- .../api/v1/instances/domain_blocks_controller.rb | 4 +++- .../instances/extended_descriptions_controller.rb | 4 +++- .../api/v1/instances/peers_controller.rb | 4 +++- .../api/v1/instances/privacy_policies_controller.rb | 4 +++- .../api/v1/instances/rules_controller.rb | 3 +++ .../instances/translation_languages_controller.rb | 4 +++- app/controllers/api/v1/instances_controller.rb | 4 +++- app/controllers/api/v1/polls_controller.rb | 1 + .../statuses/favourited_by_accounts_controller.rb | 1 + .../api/v1/statuses/histories_controller.rb | 1 + .../v1/statuses/reblogged_by_accounts_controller.rb | 1 + app/controllers/api/v1/statuses_controller.rb | 3 +++ app/controllers/api/v1/tags_controller.rb | 1 + .../api/v1/timelines/public_controller.rb | 1 + app/controllers/api/v1/timelines/tag_controller.rb | 1 + app/controllers/api/v1/trends/links_controller.rb | 3 +++ .../api/v1/trends/statuses_controller.rb | 3 +++ app/controllers/api/v1/trends/tags_controller.rb | 1 + app/controllers/api/v2/instances_controller.rb | 2 +- app/controllers/concerns/api_caching_concern.rb | 13 +++++++++++++ 29 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 app/controllers/concerns/api_caching_concern.rb diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 21a79f89268..56bf69c4021 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -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| diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb index 68952de893b..1a996d362aa 100644 --- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb @@ -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 diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb index 0a4d2ae7b30..6e6ebae43b0 100644 --- a/app/controllers/api/v1/accounts/following_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb @@ -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 diff --git a/app/controllers/api/v1/accounts/lookup_controller.rb b/app/controllers/api/v1/accounts/lookup_controller.rb index 8597f891d6d..6d63398781c 100644 --- a/app/controllers/api/v1/accounts/lookup_controller.rb +++ b/app/controllers/api/v1/accounts/lookup_controller.rb @@ -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 diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 7ed48cf6588..51f541bd23e 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -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 diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 7dff66efac9..8af4242ba30 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -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 diff --git a/app/controllers/api/v1/custom_emojis_controller.rb b/app/controllers/api/v1/custom_emojis_controller.rb index 380dbe8bf36..d4e7c43cb0a 100644 --- a/app/controllers/api/v1/custom_emojis_controller.rb +++ b/app/controllers/api/v1/custom_emojis_controller.rb @@ -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 diff --git a/app/controllers/api/v1/directories_controller.rb b/app/controllers/api/v1/directories_controller.rb index c91543e3a3e..c0585e8599a 100644 --- a/app/controllers/api/v1/directories_controller.rb +++ b/app/controllers/api/v1/directories_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb index 7ccfec70364..3d55d990af2 100644 --- a/app/controllers/api/v1/instances/activity_controller.rb +++ b/app/controllers/api/v1/instances/activity_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/domain_blocks_controller.rb b/app/controllers/api/v1/instances/domain_blocks_controller.rb index 37a6906fb6b..49fd8fa9875 100644 --- a/app/controllers/api/v1/instances/domain_blocks_controller.rb +++ b/app/controllers/api/v1/instances/domain_blocks_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/extended_descriptions_controller.rb b/app/controllers/api/v1/instances/extended_descriptions_controller.rb index c72e16cff2c..17cf0d790da 100644 --- a/app/controllers/api/v1/instances/extended_descriptions_controller.rb +++ b/app/controllers/api/v1/instances/extended_descriptions_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb index b2669a84eb7..20809d75533 100644 --- a/app/controllers/api/v1/instances/peers_controller.rb +++ b/app/controllers/api/v1/instances/peers_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/privacy_policies_controller.rb b/app/controllers/api/v1/instances/privacy_policies_controller.rb index dbd69f54d4a..36889f73354 100644 --- a/app/controllers/api/v1/instances/privacy_policies_controller.rb +++ b/app/controllers/api/v1/instances/privacy_policies_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/rules_controller.rb b/app/controllers/api/v1/instances/rules_controller.rb index 93cf3c75943..cd5cc7b0867 100644 --- a/app/controllers/api/v1/instances/rules_controller.rb +++ b/app/controllers/api/v1/instances/rules_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances/translation_languages_controller.rb b/app/controllers/api/v1/instances/translation_languages_controller.rb index 3910a499e82..c4680cccb8a 100644 --- a/app/controllers/api/v1/instances/translation_languages_controller.rb +++ b/app/controllers/api/v1/instances/translation_languages_controller.rb @@ -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 diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb index 3cdb404cb81..d4c822e64ae 100644 --- a/app/controllers/api/v1/instances_controller.rb +++ b/app/controllers/api/v1/instances_controller.rb @@ -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 diff --git a/app/controllers/api/v1/polls_controller.rb b/app/controllers/api/v1/polls_controller.rb index 6435e9f0dcd..ffc70a84963 100644 --- a/app/controllers/api/v1/polls_controller.rb +++ b/app/controllers/api/v1/polls_controller.rb @@ -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 diff --git a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb index b138fa26508..73eb11e711c 100644 --- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb @@ -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 diff --git a/app/controllers/api/v1/statuses/histories_controller.rb b/app/controllers/api/v1/statuses/histories_controller.rb index 7fe73a6f549..dff2425d062 100644 --- a/app/controllers/api/v1/statuses/histories_controller.rb +++ b/app/controllers/api/v1/statuses/histories_controller.rb @@ -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 diff --git a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb index 4b545f9826e..41672e75390 100644 --- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb @@ -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 diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index fadd1b0451e..064e7632a80 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -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 diff --git a/app/controllers/api/v1/tags_controller.rb b/app/controllers/api/v1/tags_controller.rb index a08fd21877a..284ec85937d 100644 --- a/app/controllers/api/v1/tags_controller.rb +++ b/app/controllers/api/v1/tags_controller.rb @@ -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 diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb index d253b744f99..5bbd92b9eec 100644 --- a/app/controllers/api/v1/timelines/public_controller.rb +++ b/app/controllers/api/v1/timelines/public_controller.rb @@ -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 diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index 64a1db58df3..9cd7b990467 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -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 diff --git a/app/controllers/api/v1/trends/links_controller.rb b/app/controllers/api/v1/trends/links_controller.rb index 3ce20fb7862..57cfa0b7e43 100644 --- a/app/controllers/api/v1/trends/links_controller.rb +++ b/app/controllers/api/v1/trends/links_controller.rb @@ -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 diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb index 3aab92477de..c186864c3b1 100644 --- a/app/controllers/api/v1/trends/statuses_controller.rb +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -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 diff --git a/app/controllers/api/v1/trends/tags_controller.rb b/app/controllers/api/v1/trends/tags_controller.rb index 75c3ed218bc..aca3dd7089c 100644 --- a/app/controllers/api/v1/trends/tags_controller.rb +++ b/app/controllers/api/v1/trends/tags_controller.rb @@ -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 diff --git a/app/controllers/api/v2/instances_controller.rb b/app/controllers/api/v2/instances_controller.rb index bcd90cff22e..8346e28830a 100644 --- a/app/controllers/api/v2/instances_controller.rb +++ b/app/controllers/api/v2/instances_controller.rb @@ -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 diff --git a/app/controllers/concerns/api_caching_concern.rb b/app/controllers/concerns/api_caching_concern.rb new file mode 100644 index 00000000000..705abce80fd --- /dev/null +++ b/app/controllers/concerns/api_caching_concern.rb @@ -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