Merge commit 'c62604b5f6' into kb_migration

This commit is contained in:
KMY 2023-04-20 17:41:07 +09:00
commit a1f6ef00db
86 changed files with 929 additions and 221 deletions

View file

@ -71,7 +71,7 @@ module.exports = {
'comma-style': ['warn', 'last'],
'consistent-return': 'error',
'dot-notation': 'error',
eqeqeq: 'error',
eqeqeq: ['error', 'always', { 'null': 'ignore' }],
indent: ['warn', 2],
'jsx-quotes': ['error', 'prefer-single'],
'no-case-declarations': 'off',

53
.github/workflows/build-nightly.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Build nightly container image
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # run at 2 AM UTC
permissions:
contents: read
packages: write
jobs:
build-nightly-image:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: hadolint/hadolint-action@v3.1.0
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
- name: Log in to the Github Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v4
id: meta
with:
images: |
ghcr.io/mastodon/mastodon
flavor: |
latest=auto
tags: |
type=raw,value=nightly
labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes
- uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
provenance: false
builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -1224,9 +1224,6 @@ Rails/ActiveRecordCallbacksOrder:
Rails/ApplicationController:
Exclude:
- 'app/controllers/health_controller.rb'
- 'app/controllers/well_known/host_meta_controller.rb'
- 'app/controllers/well_known/nodeinfo_controller.rb'
- 'app/controllers/well_known/webfinger_controller.rb'
# Configuration parameters: Database, Include.
# SupportedDatabases: mysql, postgresql

View file

@ -120,7 +120,7 @@ end
group :test do
gem 'capybara', '~> 3.39'
gem 'climate_control'
gem 'faker', '~> 3.1'
gem 'faker', '~> 3.2'
gem 'json-schema', '~> 3.0'
gem 'rack-test', '~> 2.1'
gem 'rails-controller-testing', '~> 1.0'

View file

@ -243,7 +243,7 @@ GEM
tzinfo
excon (0.95.0)
fabrication (2.30.0)
faker (3.1.1)
faker (3.2.0)
i18n (>= 1.8.11, < 2)
faraday (1.10.3)
faraday-em_http (~> 1.0)
@ -346,19 +346,19 @@ GEM
ipaddress (0.8.3)
jmespath (1.6.2)
json (2.6.3)
json-canonicalization (0.3.0)
json-canonicalization (0.3.1)
json-jwt (1.15.3)
activesupport (>= 4.2)
aes_key_wrap
bindata
httpclient
json-ld (3.2.3)
json-ld (3.2.4)
htmlentities (~> 4.3)
json-canonicalization (~> 0.3)
link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15)
rack (~> 2.2)
rdf (~> 3.2, >= 3.2.9)
rack (>= 2.2, < 4)
rdf (~> 3.2, >= 3.2.10)
json-ld-preloaded (3.2.2)
json-ld (~> 3.2)
rdf (~> 3.2)
@ -477,7 +477,7 @@ GEM
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
orm_adapter (0.5.0)
ox (2.14.14)
ox (2.14.16)
parallel (1.22.1)
parser (3.2.2.0)
ast (~> 2.4.1)
@ -485,7 +485,7 @@ GEM
pastel (0.8.0)
tty-color (~> 0.5)
pg (1.4.6)
pghero (3.3.1)
pghero (3.3.2)
activerecord (>= 6)
pkg-config (1.5.1)
posix-spawn (0.3.15)
@ -555,7 +555,7 @@ GEM
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.6)
rdf (3.2.9)
rdf (3.2.10)
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.5.1)
rdf (~> 3.2)
@ -797,7 +797,7 @@ DEPENDENCIES
dotenv-rails (~> 2.8)
ed25519 (~> 1.3)
fabrication (~> 2.30)
faker (~> 3.1)
faker (~> 3.2)
fast_blank (~> 1.0)
fastimage
fog-core (<= 2.4.0)

View file

@ -7,8 +7,9 @@ class AccountsController < ApplicationController
include AccountControllerConcern
include SignatureAuthentication
vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
skip_before_action :require_functional!, unless: :whitelist_mode?

View file

@ -7,10 +7,6 @@ class ActivityPub::BaseController < Api::BaseController
private
def set_cache_headers
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
end
def skip_temporary_suspension_response?
false
end

View file

@ -4,11 +4,12 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_items
before_action :set_size
before_action :set_type
before_action :set_cache_headers
def show
expires_in 3.minutes, public: public_fetch_mode?

View file

@ -4,9 +4,10 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature!
before_action :set_items
before_action :set_cache_headers
def show
expires_in 0, public: false

View file

@ -6,9 +6,10 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
vary_by -> { 'Signature' if authorized_fetch_mode? || page_requested? }
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_statuses
before_action :set_cache_headers
def show
if page_requested?
@ -16,6 +17,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
else
expires_in(3.minutes, public: public_fetch_mode?)
end
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
@ -80,8 +82,4 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def set_account
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
end
def set_cache_headers
response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested?
end
end

View file

@ -7,9 +7,10 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
DESCENDANTS_LIMIT = 60
vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_status
before_action :set_cache_headers
before_action :set_replies
def index

View file

@ -8,6 +8,8 @@ module Admin
layout 'admin'
before_action :set_body_classes
before_action :set_cache_headers
after_action :verify_authorized
private
@ -16,6 +18,10 @@ module Admin
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end

View file

@ -12,7 +12,7 @@ class Api::BaseController < ApplicationController
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
before_action :require_not_suspended!
before_action :set_cache_headers
before_action :set_cache_control_defaults
protect_from_forgery with: :null_session
@ -148,8 +148,8 @@ class Api::BaseController < ApplicationController
doorkeeper_authorize!(*scopes) if doorkeeper_token
end
def set_cache_headers
response.headers['Cache-Control'] = 'private, no-store'
def set_cache_control_defaults
response.cache_control.replace(private: true, no_store: true)
end
def disallow_unauthenticated_api_access?

View file

@ -0,0 +1,72 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::Links::PreviewCardProvidersController < Api::BaseController
include Authorization
LIMIT = 100
before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
before_action :set_providers, only: :index
after_action :verify_authorized
after_action :insert_pagination_headers, only: :index
PAGINATION_PARAMS = %i(limit).freeze
def index
authorize :preview_card_provider, :index?
render json: @providers, each_serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
end
def approve
authorize :preview_card_provider, :review?
provider = PreviewCardProvider.find(params[:id])
provider.update(trendable: true, reviewed_at: Time.now.utc)
render json: provider, serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
end
def reject
authorize :preview_card_provider, :review?
provider = PreviewCardProvider.find(params[:id])
provider.update(trendable: false, reviewed_at: Time.now.utc)
render json: provider, serializer: REST::Admin::Trends::Links::PreviewCardProviderSerializer
end
private
def set_providers
@providers = PreviewCardProvider.all.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_trends_links_preview_card_providers_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_trends_links_preview_card_providers_url(pagination_params(min_id: pagination_since_id)) unless @providers.empty?
end
def pagination_max_id
@providers.last.id
end
def pagination_since_id
@providers.first.id
end
def records_continue?
@providers.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
end

View file

@ -1,7 +1,36 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::LinksController < Api::V1::Trends::LinksController
before_action -> { authorize_if_got_token! :'admin:read' }
include Authorization
before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
after_action :verify_authorized, except: :index
def index
if current_user&.can?(:manage_taxonomies)
render json: @links, each_serializer: REST::Admin::Trends::LinkSerializer
else
super
end
end
def approve
authorize :preview_card, :review?
link = PreviewCard.find(params[:id])
link.update(trendable: true)
render json: link, serializer: REST::Admin::Trends::LinkSerializer
end
def reject
authorize :preview_card, :review?
link = PreviewCard.find(params[:id])
link.update(trendable: false)
render json: link, serializer: REST::Admin::Trends::LinkSerializer
end
private

View file

@ -1,7 +1,36 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::StatusesController < Api::V1::Trends::StatusesController
before_action -> { authorize_if_got_token! :'admin:read' }
include Authorization
before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
after_action :verify_authorized, except: :index
def index
if current_user&.can?(:manage_taxonomies)
render json: @statuses, each_serializer: REST::Admin::Trends::StatusSerializer
else
super
end
end
def approve
authorize [:admin, :status], :review?
status = Status.find(params[:id])
status.update(trendable: true)
render json: status, serializer: REST::Admin::Trends::StatusSerializer
end
def reject
authorize [:admin, :status], :review?
status = Status.find(params[:id])
status.update(trendable: false)
render json: status, serializer: REST::Admin::Trends::StatusSerializer
end
private

View file

@ -1,7 +1,12 @@
# frozen_string_literal: true
class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
before_action -> { authorize_if_got_token! :'admin:read' }
include Authorization
before_action -> { authorize_if_got_token! :'admin:read' }, only: :index
before_action -> { authorize_if_got_token! :'admin:write' }, except: :index
after_action :verify_authorized, except: :index
def index
if current_user&.can?(:manage_taxonomies)
@ -11,6 +16,22 @@ class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
end
end
def approve
authorize :tag, :review?
tag = Tag.find(params[:id])
tag.update(trendable: true, reviewed_at: Time.now.utc)
render json: tag, serializer: REST::Admin::TagSerializer
end
def reject
authorize :tag, :review?
tag = Tag.find(params[:id])
tag.update(trendable: false, reviewed_at: Time.now.utc)
render json: tag, serializer: REST::Admin::TagSerializer
end
private
def enabled?

View file

@ -1,8 +1,6 @@
# frozen_string_literal: true
class Api::V1::CustomEmojisController < Api::BaseController
skip_before_action :set_cache_headers
def index
expires_in 3.minutes, public: true
render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.listed.includes(:category) }

View file

@ -3,7 +3,6 @@
class Api::V1::Instances::ActivityController < Api::BaseController
before_action :require_enabled_api!
skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def show

View file

@ -3,7 +3,6 @@
class Api::V1::Instances::PeersController < Api::BaseController
before_action :require_enabled_api!
skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def index

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class Api::V1::InstancesController < Api::BaseController
skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def show

View file

@ -152,6 +152,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end
def set_cache_headers
response.headers['Cache-Control'] = 'private, no-store'
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -155,8 +155,16 @@ module CacheConcern
end
end
class_methods do
def vary_by(value)
before_action do |controller|
response.headers['Vary'] = value.respond_to?(:call) ? controller.instance_exec(&value) : value
end
end
end
def render_with_cache(**options)
raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given?
raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given?
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':')
expires_in = options.delete(:expires_in) || 3.minutes
@ -176,10 +184,6 @@ module CacheConcern
end
end
def set_cache_headers
response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
end
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes)

View file

@ -1,18 +1,8 @@
# frozen_string_literal: true
class CustomCssController < ApplicationController
skip_before_action :store_current_location
skip_before_action :require_functional!
skip_before_action :update_user_sign_in
skip_before_action :set_session_activity
skip_around_action :set_locale
before_action :set_cache_headers
class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
def show
expires_in 3.minutes, public: true
request.session_options[:skip] = true
render content_type: 'text/css'
end
end

View file

@ -9,10 +9,15 @@ class Disputes::BaseController < ApplicationController
before_action :set_body_classes
before_action :authenticate_user!
before_action :set_cache_headers
private
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -2,15 +2,12 @@
class EmojisController < ApplicationController
before_action :set_emoji
before_action :set_cache_headers
vary_by -> { 'Signature' if authorized_fetch_mode? }
def show
respond_to do |format|
format.json do
expires_in 3.minutes, public: true
render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
end
end
expires_in 3.minutes, public: true
render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
end
private

View file

@ -7,6 +7,7 @@ class Filters::StatusesController < ApplicationController
before_action :set_filter
before_action :set_status_filters
before_action :set_body_classes
before_action :set_cache_headers
PER_PAGE = 20
@ -44,4 +45,8 @@ class Filters::StatusesController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -6,6 +6,7 @@ class FiltersController < ApplicationController
before_action :authenticate_user!
before_action :set_filter, only: [:edit, :update, :destroy]
before_action :set_body_classes
before_action :set_cache_headers
def index
@filters = current_account.custom_filters.includes(:keywords, :statuses).order(:phrase)
@ -54,4 +55,8 @@ class FiltersController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -5,8 +5,9 @@ class FollowerAccountsController < ApplicationController
include SignatureVerification
include WebAppControllerConcern
vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :whitelist_mode?

View file

@ -5,8 +5,9 @@ class FollowingAccountsController < ApplicationController
include SignatureVerification
include WebAppControllerConcern
vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :whitelist_mode?

View file

@ -7,6 +7,7 @@ class InvitesController < ApplicationController
before_action :authenticate_user!
before_action :set_body_classes
before_action :set_cache_headers
def index
authorize :invite, :create?
@ -49,4 +50,8 @@ class InvitesController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -1,9 +1,6 @@
# frozen_string_literal: true
class ManifestsController < ApplicationController
skip_before_action :store_current_location
skip_before_action :require_functional!
class ManifestsController < ActionController::Base # rubocop:disable Rails/ApplicationController
def show
expires_in 3.minutes, public: true
render json: InstancePresenter.new, serializer: ManifestSerializer, root: 'instance'

View file

@ -34,6 +34,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
end
def set_cache_headers
response.headers['Cache-Control'] = 'private, no-store'
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -7,6 +7,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :authenticate_resource_owner!
before_action :require_not_suspended!, only: :destroy
before_action :set_body_classes
before_action :set_cache_headers
skip_before_action :require_functional!
@ -30,4 +31,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def require_not_suspended!
forbidden if current_account.suspended?
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -7,6 +7,7 @@ class RelationshipsController < ApplicationController
before_action :set_accounts, only: :show
before_action :set_relationships, only: :show
before_action :set_body_classes
before_action :set_cache_headers
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
@ -70,4 +71,8 @@ class RelationshipsController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -14,7 +14,7 @@ class Settings::BaseController < ApplicationController
end
def set_cache_headers
response.headers['Cache-Control'] = 'private, no-store'
response.cache_control.replace(private: true, no_store: true)
end
def require_not_suspended!

View file

@ -6,6 +6,7 @@ class StatusesCleanupController < ApplicationController
before_action :authenticate_user!
before_action :set_policy
before_action :set_body_classes
before_action :set_cache_headers
def show; end
@ -36,4 +37,8 @@ class StatusesCleanupController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
end

View file

@ -6,11 +6,12 @@ class StatusesController < ApplicationController
include Authorization
include AccountOwnedConcern
vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_status
before_action :set_instance_presenter
before_action :redirect_to_original, only: :show
before_action :set_cache_headers
before_action :set_body_classes, only: :embed
after_action :set_link_headers

View file

@ -7,6 +7,8 @@ class TagsController < ApplicationController
PAGE_SIZE = 20
PAGE_SIZE_MAX = 200
vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :authenticate_user!, if: :whitelist_mode?
before_action :set_local

View file

@ -1,11 +1,9 @@
# frozen_string_literal: true
module WellKnown
class HostMetaController < ActionController::Base
class HostMetaController < ActionController::Base # rubocop:disable Rails/ApplicationController
include RoutingHelper
before_action { response.headers['Vary'] = 'Accept' }
def show
@webfinger_template = "#{webfinger_url}?resource={uri}"
expires_in 3.days, public: true

View file

@ -1,11 +1,9 @@
# frozen_string_literal: true
module WellKnown
class NodeInfoController < ActionController::Base
class NodeInfoController < ActionController::Base # rubocop:disable Rails/ApplicationController
include CacheConcern
before_action { response.headers['Vary'] = 'Accept' }
def index
expires_in 3.days, public: true
render_with_cache json: {}, serializer: NodeInfo::DiscoverySerializer, adapter: NodeInfo::Adapter, expires_in: 3.days, root: 'nodeinfo'

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
module WellKnown
class WebfingerController < ActionController::Base
class WebfingerController < ActionController::Base # rubocop:disable Rails/ApplicationController
include RoutingHelper
before_action :set_account
@ -34,7 +34,12 @@ module WellKnown
end
def check_account_suspension
expires_in(3.minutes, public: true) && gone if @account.suspended_permanently?
gone if @account.suspended_permanently?
end
def gone
expires_in(3.minutes, public: true)
head 410
end
def bad_request
@ -46,9 +51,5 @@ module WellKnown
expires_in(3.minutes, public: true)
head 404
end
def gone
head 410
end
end
end

View file

@ -32,17 +32,14 @@ function ShortNumber({ value, renderer, children }) {
const shortNumber = toShortNumber(value);
const [, division] = shortNumber;
// eslint-disable-next-line eqeqeq
if (children != null && renderer != null) {
console.warn('Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.');
}
// eslint-disable-next-line eqeqeq
const customRenderer = children != null ? children : renderer;
const displayNumber = <ShortNumberCounter value={shortNumber} />;
// eslint-disable-next-line eqeqeq
return customRenderer != null
? customRenderer(displayNumber, pluralReady(value, division))
: displayNumber;

View file

@ -60,7 +60,6 @@ export function toShortNumber(sourceNumber) {
* // => 1790
*/
export function pluralReady(sourceNumber, division) {
// eslint-disable-next-line eqeqeq
if (division == null || division < DECIMAL_UNITS.HUNDRED) {
return sourceNumber;
}

View file

@ -18,6 +18,7 @@
#
class PreviewCardProvider < ApplicationRecord
include Paginable
include DomainNormalizable
include Attachmentable

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class REST::Admin::Trends::LinkSerializer < REST::Trends::LinkSerializer
attributes :id, :requires_review
def requires_review
object.requires_review?
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class REST::Admin::Trends::Links::PreviewCardProviderSerializer < ActiveModel::Serializer
attributes :id, :domain, :trendable, :reviewed_at,
:requested_review_at, :requires_review
def requires_review
object.requires_review?
end
end

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class REST::Admin::Trends::StatusSerializer < REST::StatusSerializer
attributes :requires_review
def requires_review
object.requires_review?
end
end

View file

@ -43,6 +43,7 @@ require_relative '../lib/chewy/strategy/bypass_with_warning'
require_relative '../lib/webpacker/manifest_extensions'
require_relative '../lib/webpacker/helper_extensions'
require_relative '../lib/rails/engine_extensions'
require_relative '../lib/action_controller/conditional_get_extensions'
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/simple_navigation/item_extensions'

View file

@ -676,9 +676,33 @@ Rails.application.routes.draw do
resources :ip_blocks, only: [:index, :show, :update, :create, :destroy]
namespace :trends do
resources :tags, only: [:index]
resources :links, only: [:index]
resources :statuses, only: [:index]
resources :tags, only: [:index] do
member do
post :approve
post :reject
end
end
resources :links, only: [:index] do
member do
post :approve
post :reject
end
end
resources :statuses, only: [:index] do
member do
post :approve
post :reject
end
end
namespace :links do
resources :preview_card_providers, only: [:index], path: :publishers do
member do
post :approve
post :reject
end
end
end
end
post :measures, to: 'measures#create'

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
module ActionController
module ConditionalGetExtensions
def expires_in(*)
# This backports a fix from Rails 7 so that a more private Cache-Control
# can be overriden by calling expires_in on a specific controller action
response.cache_control.delete(:no_store)
super
end
end
end
ActionController::ConditionalGet.prepend(ActionController::ConditionalGetExtensions)

View file

@ -189,9 +189,10 @@ module Mastodon
user.disabled = true if options[:disable]
user.approved = true if options[:approve]
user.otp_required_for_login = false if options[:disable_2fa]
user.confirm if options[:confirm]
if user.save
user.confirm if options[:confirm]
say('OK', :green)
say("New password: #{password}") if options[:reset_password]
else

View file

@ -65,7 +65,7 @@
"fuzzysort": "^2.0.4",
"glob": "^10.0.0",
"history": "^4.10.1",
"http-link-header": "^1.1.0",
"http-link-header": "^1.1.1",
"immutable": "^4.3.0",
"imports-loader": "^1.2.0",
"intl": "^1.2.5",
@ -83,7 +83,7 @@
"path-complete-extname": "^1.0.0",
"pg": "^8.5.0",
"pg-connection-string": "^2.5.0",
"postcss": "^8.4.21",
"postcss": "^8.4.22",
"postcss-loader": "^4.3.0",
"promise.prototype.finally": "^3.1.4",
"prop-types": "^15.8.1",
@ -113,7 +113,7 @@
"redux-thunk": "^2.4.2",
"regenerator-runtime": "^0.13.11",
"requestidlecallback": "^0.3.0",
"reselect": "^4.1.7",
"reselect": "^4.1.8",
"rimraf": "^5.0.0",
"sass": "^1.61.0",
"sass-loader": "^10.2.0",

View file

@ -17,6 +17,10 @@ RSpec.describe AccountsController, type: :controller do
expect(session).to be_empty
end
it 'returns Vary header' do
expect(response.headers['Vary']).to include 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end

View file

@ -18,6 +18,14 @@ describe Admin::BaseController, type: :controller do
expect(response).to have_http_status(403)
end
it 'returns private cache control headers' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Moderator')))
get :success
expect(response.headers['Cache-Control']).to include('private, no-store')
end
it 'renders admin layout as a moderator' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Moderator')))

View file

@ -15,6 +15,12 @@ describe Api::BaseController do
end
end
it 'returns private cache control headers by default' do
routes.draw { get 'success' => 'api/base#success' }
get :success
expect(response.headers['Cache-Control']).to include('private, no-store')
end
describe 'forgery protection' do
before do
routes.draw { post 'success' => 'api/base#success' }

View file

@ -17,5 +17,9 @@ RSpec.describe Api::OEmbedController, type: :controller do
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
end

View file

@ -0,0 +1,68 @@
# frozen_string_literal: true
require 'rails_helper'
describe Api::V1::Admin::Trends::Links::PreviewCardProvidersController do
render_views
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
let(:preview_card_provider) { Fabricate(:preview_card_provider) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
post :approve, params: { id: preview_card_provider.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #reject' do
before do
post :reject, params: { id: preview_card_provider.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end

View file

@ -5,14 +5,33 @@ require 'rails_helper'
describe Api::V1::Admin::Trends::LinksController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
let(:preview_card) { Fabricate(:preview_card) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::LinksController do
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
post :approve, params: { id: preview_card.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #reject' do
before do
post :reject, params: { id: preview_card.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end

View file

@ -5,14 +5,33 @@ require 'rails_helper'
describe Api::V1::Admin::Trends::StatusesController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::StatusesController do
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
post :approve, params: { id: status.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #reject' do
before do
post :reject, params: { id: status.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end

View file

@ -5,14 +5,33 @@ require 'rails_helper'
describe Api::V1::Admin::Trends::TagsController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) }
let(:tag) { Fabricate(:tag) }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do
it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 }
@ -20,4 +39,30 @@ describe Api::V1::Admin::Trends::TagsController do
expect(response).to have_http_status(200)
end
end
describe 'POST #approve' do
before do
post :approve, params: { id: tag.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
describe 'POST #reject' do
before do
post :reject, params: { id: tag.id }
end
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
expect(response).to have_http_status(200)
end
end
end

View file

@ -33,27 +33,42 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
end
describe 'GET #edit' do
it 'returns http success' do
before do
request.env['devise.mapping'] = Devise.mappings[:user]
sign_in(Fabricate(:user))
get :edit
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control header' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'GET #update' do
it 'returns http success' do
let(:user) { Fabricate(:user) }
before do
request.env['devise.mapping'] = Devise.mappings[:user]
sign_in(Fabricate(:user), scope: :user)
sign_in(user, scope: :user)
post :update
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
context 'when suspended' do
let(:user) { Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }) }
it 'returns http forbidden' do
request.env['devise.mapping'] = Devise.mappings[:user]
sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user)
post :update
expect(response).to have_http_status(403)
end
end

View file

@ -6,9 +6,25 @@ describe CustomCssController do
render_views
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns public cache control header' do
expect(response.headers['Cache-Control']).to include('public')
end
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be_nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
end
end

View file

@ -18,21 +18,27 @@ describe Filters::StatusesController do
context 'with a signed in user' do
context 'with the filter user signed in' do
before { sign_in(filter.account.user) }
before do
sign_in(filter.account.user)
get :index, params: { filter_id: filter }
end
it 'returns http success' do
get :index, params: { filter_id: filter }
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
context 'with another user signed in' do
before { sign_in(Fabricate(:user)) }
before do
sign_in(Fabricate(:user))
get :index, params: { filter_id: filter }
end
it 'returns http not found' do
get :index, params: { filter_id: filter }
expect(response).to have_http_status(404)
end
end

View file

@ -7,21 +7,28 @@ describe FiltersController do
describe 'GET #index' do
context 'with signed out user' do
it 'redirects' do
before do
get :index
end
it 'redirects' do
expect(response).to be_redirect
end
end
context 'with a signed in user' do
before { sign_in(Fabricate(:user)) }
before do
sign_in(Fabricate(:user))
get :index
end
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
end
end

View file

@ -5,35 +5,40 @@ require 'rails_helper'
describe InvitesController do
render_views
let(:user) { Fabricate(:user) }
before do
sign_in user
end
describe 'GET #index' do
subject { get :index }
let(:user) { Fabricate(:user) }
let!(:invite) { Fabricate(:invite, user: user) }
before do
Fabricate(:invite, user: user)
end
context 'when everyone can invite' do
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
get :index
end
it 'renders index page' do
expect(subject).to render_template :index
expect(assigns(:invites)).to include invite
expect(assigns(:invites).count).to eq 1
it 'returns http success' do
expect(response).to have_http_status(:success)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
context 'when not everyone can invite' do
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
get :index
end
it 'returns 403' do
expect(subject).to have_http_status 403
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
end
@ -42,8 +47,6 @@ describe InvitesController do
subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
context 'when everyone can invite' do
let(:user) { Fabricate(:user) }
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
end
@ -56,26 +59,28 @@ describe InvitesController do
end
context 'when not everyone can invite' do
let(:user) { Fabricate(:user) }
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
end
it 'returns 403' do
expect(subject).to have_http_status 403
it 'returns http forbidden' do
expect(subject).to have_http_status(403)
end
end
end
describe 'DELETE #create' do
subject { delete :destroy, params: { id: invite.id } }
let(:invite) { Fabricate(:invite, user: user, expires_at: nil) }
let(:user) { Fabricate(:user) }
let!(:invite) { Fabricate(:invite, user: user, expires_at: nil) }
before do
delete :destroy, params: { id: invite.id }
end
it 'redirects' do
expect(response).to redirect_to invites_path
end
it 'expires invite' do
expect(subject).to redirect_to invites_path
expect(invite.reload).to be_expired
end
end

View file

@ -7,11 +7,24 @@ describe ManifestsController do
describe 'GET #show' do
before do
get :show, format: :json
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns public cache control header' do
expect(response.headers['Cache-Control']).to include('public')
end
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be_nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
end
end

View file

@ -31,6 +31,11 @@ RSpec.describe Oauth::AuthorizationsController, type: :controller do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
subject
expect(response.headers['Cache-Control']).to include('private, no-store')
end
it 'gives options to authorize and deny' do
subject
expect(response.body).to match(/Authorize/)

View file

@ -27,6 +27,11 @@ describe Oauth::AuthorizedApplicationsController do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
subject
expect(response.headers['Cache-Control']).to include('private, no-store')
end
include_examples 'stores location for user'
end

View file

@ -7,42 +7,39 @@ describe RelationshipsController do
let(:user) { Fabricate(:user) }
shared_examples 'authenticate user' do
it 'redirects when not signed in' do
expect(subject).to redirect_to '/auth/sign_in'
end
end
describe 'GET #show' do
subject { get :show, params: { page: 2, relationship: 'followed_by' } }
context 'when signed in' do
before do
sign_in user, scope: :user
get :show, params: { page: 2, relationship: 'followed_by' }
end
it 'assigns @accounts' do
Fabricate(:account, domain: 'old').follow!(user.account)
Fabricate(:account, domain: 'recent').follow!(user.account)
it 'returns http success' do
expect(response).to have_http_status(200)
end
sign_in user, scope: :user
subject
assigned = assigns(:accounts).per(1).to_a
expect(assigned.size).to eq 1
expect(assigned[0].domain).to eq 'old'
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
it 'returns http success' do
sign_in user, scope: :user
subject
expect(response).to have_http_status(200)
end
context 'when not signed in' do
before do
get :show, params: { page: 2, relationship: 'followed_by' }
end
include_examples 'authenticate user'
it 'redirects when not signed in' do
expect(response).to redirect_to '/auth/sign_in'
end
end
end
describe 'PATCH #update' do
let(:poopfeast) { Fabricate(:account, username: 'poopfeast', domain: 'example.com') }
let(:alice) { Fabricate(:account, username: 'alice', domain: 'example.com') }
shared_examples 'redirects back to followers page' do
it 'redirects back to followers page' do
poopfeast.follow!(user.account)
alice.follow!(user.account)
sign_in user, scope: :user
subject
@ -58,27 +55,36 @@ describe RelationshipsController do
end
context 'when select parameter is provided' do
subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, remove_domains_from_followers: '' } }
subject { patch :update, params: { form_account_batch: { account_ids: [alice.id] }, remove_domains_from_followers: '' } }
it 'soft-blocks followers from selected domains' do
poopfeast.follow!(user.account)
alice.follow!(user.account)
sign_in user, scope: :user
subject
expect(poopfeast.following?(user.account)).to be false
expect(alice.following?(user.account)).to be false
end
it 'does not unfollow users from selected domains' do
user.account.follow!(poopfeast)
user.account.follow!(alice)
sign_in user, scope: :user
subject
expect(user.account.following?(poopfeast)).to be true
expect(user.account.following?(alice)).to be true
end
context 'when not signed in' do
before do
subject
end
it 'redirects when not signed in' do
expect(response).to redirect_to '/auth/sign_in'
end
end
include_examples 'authenticate user'
include_examples 'redirects back to followers page'
end
end

View file

@ -13,10 +13,17 @@ describe Settings::AliasesController do
end
describe 'GET #index' do
it 'returns http success' do
before do
get :index
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'POST #create' do

View file

@ -13,13 +13,17 @@ describe Settings::ApplicationsController do
end
describe 'GET #index' do
let!(:other_app) { Fabricate(:application) }
it 'shows apps' do
before do
Fabricate(:application)
get :index
end
it 'returns http success' do
expect(response).to have_http_status(200)
expect(assigns(:applications)).to include(app)
expect(assigns(:applications)).to_not include(other_app)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end

View file

@ -11,20 +11,27 @@ describe Settings::DeletesController do
before do
sign_in user, scope: :user
get :show
end
it 'renders confirmation page' do
get :show
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
context 'when suspended' do
let(:user) { Fabricate(:user, account_attributes: { suspended_at: Time.now.utc }) }
it 'returns http forbidden' do
get :show
expect(response).to have_http_status(403)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
end

View file

@ -11,16 +11,16 @@ describe Settings::ExportsController do
before do
sign_in user, scope: :user
get :show
end
it 'renders export' do
get :show
export = assigns(:export)
expect(export).to be_instance_of Export
expect(export.account).to eq user.account
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
context 'when not signed in' do

View file

@ -10,10 +10,17 @@ RSpec.describe Settings::ImportsController, type: :controller do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'POST #create' do

View file

@ -12,9 +12,16 @@ describe Settings::LoginActivitiesController do
end
describe 'GET #index' do
it 'returns http success' do
before do
get :index
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
end

View file

@ -12,10 +12,17 @@ describe Settings::Migration::RedirectsController do
end
describe 'GET #new' do
it 'returns http success' do
before do
get :new
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'POST #create' do

View file

@ -12,11 +12,17 @@ describe Settings::Preferences::AppearanceController do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do

View file

@ -12,10 +12,17 @@ describe Settings::Preferences::NotificationsController do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do

View file

@ -12,10 +12,17 @@ describe Settings::Preferences::OtherController do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do

View file

@ -13,10 +13,17 @@ RSpec.describe Settings::ProfilesController, type: :controller do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do

View file

@ -26,23 +26,25 @@ describe Settings::TwoFactorAuthenticationMethodsController do
describe 'when user has enabled otp' do
before do
user.update(otp_required_for_login: true)
get :index
end
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'when user has not enabled otp' do
before do
user.update(otp_required_for_login: false)
get :index
end
it 'redirects to enable otp' do
get :index
expect(response).to redirect_to(settings_otp_authentication_path)
end
end

View file

@ -11,19 +11,32 @@ RSpec.describe StatusesCleanupController, type: :controller do
end
describe 'GET #show' do
it 'returns http success' do
before do
get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns private cache control headers' do
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
describe 'PUT #update' do
it 'updates the account status cleanup policy' do
before do
put :update, params: { account_statuses_cleanup_policy: { enabled: true, min_status_age: 2.weeks.seconds, keep_direct: false, keep_polls: true } }
expect(response).to redirect_to(statuses_cleanup_path)
end
it 'updates the account status cleanup policy' do
expect(@user.account.statuses_cleanup_policy.enabled).to be true
expect(@user.account.statuses_cleanup_policy.keep_direct).to be false
expect(@user.account.statuses_cleanup_policy.keep_polls).to be true
end
it 'redirects' do
expect(response).to redirect_to(statuses_cleanup_path)
end
end
end

View file

@ -15,6 +15,10 @@ describe StatusesController do
expect(session).to be_empty
end
it 'returns Vary header' do
expect(response.headers['Vary']).to include 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end

View file

@ -6,21 +6,50 @@ RSpec.describe TagsController, type: :controller do
render_views
describe 'GET #show' do
let!(:tag) { Fabricate(:tag, name: 'test') }
let!(:local) { Fabricate(:status, tags: [tag], text: 'local #test') }
let!(:remote) { Fabricate(:status, tags: [tag], text: 'remote #test', account: Fabricate(:account, domain: 'remote')) }
let!(:late) { Fabricate(:status, tags: [tag], text: 'late #test') }
let(:format) { 'html' }
let(:tag) { Fabricate(:tag, name: 'test') }
let(:tag_name) { tag&.name }
before do
get :show, params: { id: tag_name, format: format }
end
context 'when tag exists' do
it 'returns http success' do
get :show, params: { id: 'test', max_id: late.id }
expect(response).to have_http_status(200)
context 'when requested as HTML' do
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
context 'when requested as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
end
context 'when tag does not exist' do
let(:tag_name) { 'hoge' }
it 'returns http not found' do
get :show, params: { id: 'none' }
expect(response).to have_http_status(404)
end
end

View file

@ -6032,10 +6032,10 @@ http-errors@~1.6.2:
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
http-link-header@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/http-link-header/-/http-link-header-1.1.0.tgz#a1ca87efdbcb7778d8d0d4525de1e6964ec1f129"
integrity sha512-pj6N1yxOz/ANO8HHsWGg/OoIL1kmRYvQnXQ7PIRpgp+15AnEsRH8fmIJE6D1OdWG2Bov+BJHVla1fFXxg1JbbA==
http-link-header@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/http-link-header/-/http-link-header-1.1.1.tgz#f0e6971b0ed86e858d2077066ecb7ba4f2e50de9"
integrity sha512-mW3N/rTYpCn99s1do0zx6nzFZSwLH9HGfUM4ZqLWJ16ylmYaC2v5eYGqrNTQlByx8AzUgGI+V/32gXPugs1+Sw==
http-parser-js@>=0.5.1:
version "0.5.3"
@ -8005,10 +8005,10 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanoid@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
nanomatch@^1.2.9:
version "1.2.13"
@ -9007,12 +9007,12 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.2.15, postcss@^8.4.21:
version "8.4.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
postcss@^8.2.15, postcss@^8.4.21, postcss@^8.4.22:
version "8.4.22"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.22.tgz#c29e6776b60ab3af602d4b513d5bd2ff9aa85dc1"
integrity sha512-XseknLAfRHzVWjCEtdviapiBtfLdgyzExD50Rg2ePaucEesyh8Wv4VPdW0nbyDa1ydbrAxV19jvMT4+LFmcNUA==
dependencies:
nanoid "^3.3.4"
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
@ -9789,10 +9789,10 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
reselect@^4.1.7:
version "4.1.7"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.7.tgz#56480d9ff3d3188970ee2b76527bd94a95567a42"
integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==
reselect@^4.1.8:
version "4.1.8"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
resolve-cwd@^2.0.0:
version "2.0.0"