Merge pull request #1519 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
df326b8b5c
1
Gemfile
1
Gemfile
|
@ -54,7 +54,6 @@ gem 'fast_blank', '~> 1.0'
|
|||
gem 'fastimage'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'redis-namespace', '~> 1.8'
|
||||
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 4.4'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
|
|
11
Gemfile.lock
11
Gemfile.lock
|
@ -21,14 +21,6 @@ GIT
|
|||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/ianheggie/health_check
|
||||
revision: 0b799ead604f900ed50685e9b2d469cd2befba5b
|
||||
ref: 0b799ead604f900ed50685e9b2d469cd2befba5b
|
||||
specs:
|
||||
health_check (4.0.0.pre)
|
||||
rails (>= 4.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/nsommer/pluck_each
|
||||
revision: 73be0947c52fc54bf6d7085378db008358aac5eb
|
||||
|
@ -567,7 +559,7 @@ GEM
|
|||
rspec-support (3.10.2)
|
||||
rspec_junit_formatter (0.4.1)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.12.0)
|
||||
rubocop (1.12.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
|
@ -756,7 +748,6 @@ DEPENDENCIES
|
|||
fog-openstack (~> 0.3)
|
||||
fuubar (~> 2.5)
|
||||
hamlit-rails (~> 0.2)
|
||||
health_check!
|
||||
hiredis (~> 0.6)
|
||||
htmlentities (~> 4.3)
|
||||
http (~> 4.4)
|
||||
|
|
|
@ -3,13 +3,8 @@ require 'sidekiq/api'
|
|||
|
||||
module Admin
|
||||
class DashboardController < BaseController
|
||||
SIDEKIQ_QUEUES = %w(default push mailers pull scheduler).freeze
|
||||
|
||||
def index
|
||||
missing_queues = Sidekiq::ProcessSet.new.reduce(SIDEKIQ_QUEUES) { |queues, process| queues - process['queues'] }
|
||||
|
||||
flash.now[:alert] = I18n.t('admin.dashboard.misconfigured_sidekiq_alert', queues: missing_queues.join(', ')) unless missing_queues.empty?
|
||||
|
||||
@system_checks = Admin::SystemCheck.perform
|
||||
@users_count = User.count
|
||||
@pending_users_count = User.pending.count
|
||||
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HealthController < ActionController::Base
|
||||
def show
|
||||
render plain: 'OK'
|
||||
end
|
||||
end
|
|
@ -90,7 +90,11 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (this.mediaQuery) {
|
||||
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
|
||||
if (this.mediaQuery.addEventListener) {
|
||||
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
|
||||
} else {
|
||||
this.mediaQuery.addListener(this.handleLayoutChange);
|
||||
}
|
||||
this.setState({ renderComposePanel: !this.mediaQuery.matches });
|
||||
}
|
||||
|
||||
|
@ -125,7 +129,11 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (this.mediaQuery) {
|
||||
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
|
||||
if (this.mediaQuery.removeEventListener) {
|
||||
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
|
||||
} else {
|
||||
this.mediaQuery.removeListener(this.handleLayouteChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -595,6 +595,12 @@ code {
|
|||
color: $valid-value-color;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
border: 1px solid rgba($gold-star, 0.5);
|
||||
background: rgba($gold-star, 0.25);
|
||||
color: $gold-star;
|
||||
}
|
||||
|
||||
&.alert {
|
||||
border: 1px solid rgba($error-value-color, 0.5);
|
||||
background: rgba($error-value-color, 0.1);
|
||||
|
@ -616,6 +622,19 @@ code {
|
|||
}
|
||||
}
|
||||
|
||||
&.warning a {
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
@ -672,6 +691,29 @@ code {
|
|||
}
|
||||
}
|
||||
|
||||
.flash-message-stack {
|
||||
margin-bottom: 30px;
|
||||
|
||||
.flash-message {
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
border-top-width: 0;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
|
|
|
@ -90,7 +90,11 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (this.mediaQuery) {
|
||||
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
|
||||
if (this.mediaQuery.addEventListener) {
|
||||
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
|
||||
} else {
|
||||
this.mediaQuery.addListener(this.handleLayoutChange);
|
||||
}
|
||||
this.setState({ renderComposePanel: !this.mediaQuery.matches });
|
||||
}
|
||||
|
||||
|
@ -125,7 +129,11 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (this.mediaQuery) {
|
||||
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
|
||||
if (this.mediaQuery.removeEventListener) {
|
||||
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
|
||||
} else {
|
||||
this.mediaQuery.removeListener(this.handleLayouteChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -604,6 +604,12 @@ code {
|
|||
color: $valid-value-color;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
border: 1px solid rgba($gold-star, 0.5);
|
||||
background: rgba($gold-star, 0.25);
|
||||
color: $gold-star;
|
||||
}
|
||||
|
||||
&.alert {
|
||||
border: 1px solid rgba($error-value-color, 0.5);
|
||||
background: rgba($error-value-color, 0.1);
|
||||
|
@ -625,6 +631,19 @@ code {
|
|||
}
|
||||
}
|
||||
|
||||
&.warning a {
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
@ -681,6 +700,29 @@ code {
|
|||
}
|
||||
}
|
||||
|
||||
.flash-message-stack {
|
||||
margin-bottom: 30px;
|
||||
|
||||
.flash-message {
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
border-top-width: 0;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck
|
||||
ACTIVE_CHECKS = [
|
||||
Admin::SystemCheck::DatabaseSchemaCheck,
|
||||
Admin::SystemCheck::SidekiqProcessCheck,
|
||||
Admin::SystemCheck::RulesCheck,
|
||||
].freeze
|
||||
|
||||
def self.perform
|
||||
ACTIVE_CHECKS.each_with_object([]) do |klass, arr|
|
||||
check = klass.new
|
||||
|
||||
if check.pass?
|
||||
arr
|
||||
else
|
||||
arr << check.message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::BaseCheck
|
||||
def pass?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def message
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::DatabaseSchemaCheck < Admin::SystemCheck::BaseCheck
|
||||
def pass?
|
||||
!ActiveRecord::Base.connection.migration_context.needs_migration?
|
||||
end
|
||||
|
||||
def message
|
||||
Admin::SystemCheck::Message.new(:database_schema_check)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::Message
|
||||
attr_reader :key, :value, :action
|
||||
|
||||
def initialize(key, value = nil, action = nil)
|
||||
@key = key
|
||||
@value = value
|
||||
@action = action
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::RulesCheck < Admin::SystemCheck::BaseCheck
|
||||
include RoutingHelper
|
||||
|
||||
def pass?
|
||||
Rule.kept.exists?
|
||||
end
|
||||
|
||||
def message
|
||||
Admin::SystemCheck::Message.new(:rules_check, nil, admin_rules_path)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::SidekiqProcessCheck < Admin::SystemCheck::BaseCheck
|
||||
SIDEKIQ_QUEUES = %w(
|
||||
default
|
||||
push
|
||||
mailers
|
||||
pull
|
||||
scheduler
|
||||
ingress
|
||||
).freeze
|
||||
|
||||
def pass?
|
||||
missing_queues.empty?
|
||||
end
|
||||
|
||||
def message
|
||||
Admin::SystemCheck::Message.new(:sidekiq_process_check, missing_queues.join(', '))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def missing_queues
|
||||
@missing_queues ||= Sidekiq::ProcessSet.new.reduce(SIDEKIQ_QUEUES) { |queues, process| queues - process['queues'] }
|
||||
end
|
||||
end
|
|
@ -1,6 +1,14 @@
|
|||
- content_for :page_title do
|
||||
= t('admin.dashboard.title')
|
||||
|
||||
- unless @system_checks.empty?
|
||||
.flash-message-stack
|
||||
- @system_checks.each do |message|
|
||||
.flash-message.warning
|
||||
= t("admin.system_checks.#{message.key}.message_html", message.value ? { value: content_tag(:strong, message.value) } : {})
|
||||
- if message.action
|
||||
= link_to t("admin.system_checks.#{message.key}.action"), message.action
|
||||
|
||||
.dashboard__counters
|
||||
%div
|
||||
= link_to admin_accounts_url(local: 1, recent: 1) do
|
||||
|
|
|
@ -45,5 +45,5 @@
|
|||
= content_for?(:content) ? yield(:content) : yield
|
||||
|
||||
.logo-resources
|
||||
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
|
||||
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_full.svg')
|
||||
= raw render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
|
||||
= raw render file: Rails.root.join('app', 'javascript', 'images', 'logo_full.svg')
|
||||
|
|
|
@ -25,4 +25,4 @@
|
|||
= yield
|
||||
|
||||
.logo-resources
|
||||
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
|
||||
= raw render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
HealthCheck.setup do |config|
|
||||
config.uri = 'health'
|
||||
|
||||
config.standard_checks = %w(database migrations cache)
|
||||
config.full_checks = %w(database migrations cache)
|
||||
|
||||
config.include_error_in_response_body = false
|
||||
end
|
|
@ -367,7 +367,6 @@ en:
|
|||
feature_timeline_preview: Timeline preview
|
||||
features: Features
|
||||
hidden_service: Federation with hidden services
|
||||
misconfigured_sidekiq_alert: 'No Sidekiq process seems to be handling the following queues: %{queues}. Please review your Sidekiq configuration.'
|
||||
open_reports: open reports
|
||||
pending_tags: hashtags waiting for review
|
||||
pending_users: users waiting for review
|
||||
|
@ -661,6 +660,14 @@ en:
|
|||
no_status_selected: No statuses were changed as none were selected
|
||||
title: Account statuses
|
||||
with_media: With media
|
||||
system_checks:
|
||||
database_schema_check:
|
||||
message_html: There are pending database migrations. Please run them to ensure the application behaves as expected
|
||||
rules_check:
|
||||
action: Manage server rules
|
||||
message_html: You haven't defined any server rules.
|
||||
sidekiq_process_check:
|
||||
message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration
|
||||
tags:
|
||||
accounts_today: Unique uses today
|
||||
accounts_week: Unique uses this week
|
||||
|
|
|
@ -10,7 +10,7 @@ Rails.application.routes.draw do
|
|||
|
||||
mount LetterOpenerWeb::Engine, at: 'letter_opener' if Rails.env.development?
|
||||
|
||||
health_check_routes
|
||||
get 'health', to: 'health#show'
|
||||
|
||||
authenticate :user, lambda { |u| u.admin? } do
|
||||
mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
|
||||
|
|
|
@ -25,7 +25,9 @@ module Mastodon
|
|||
exit(1)
|
||||
end
|
||||
|
||||
ActiveRecord::Base.configurations[Rails.env]['pool'] = options[:concurrency] + 1
|
||||
db_config = ActiveRecord::Base.configurations[Rails.env].dup
|
||||
db_config['pool'] = options[:concurrency] + 1
|
||||
ActiveRecord::Base.establish_connection(db_config)
|
||||
|
||||
progress = create_progress_bar(scope.count)
|
||||
pool = Concurrent::FixedThreadPool.new(options[:concurrency])
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
"twitter-text": "3.1.0",
|
||||
"uuid": "^8.3.1",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-assets-manifest": "^4.0.1",
|
||||
"webpack-assets-manifest": "^4.0.2",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-merge": "^5.7.3",
|
||||
|
@ -176,7 +176,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"eslint": "^7.23.0",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe HealthCheck::HealthCheckController do
|
||||
describe HealthController do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
subject(:response) { get :index, params: { format: :json } }
|
||||
subject(:response) { get :show, params: { format: :json } }
|
||||
|
||||
it 'returns the right response' do
|
||||
expect(response).to have_http_status 200
|
24
yarn.lock
24
yarn.lock
|
@ -1405,10 +1405,10 @@
|
|||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react@^11.2.5":
|
||||
version "11.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.5.tgz#ae1c36a66c7790ddb6662c416c27863d87818eb9"
|
||||
integrity sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==
|
||||
"@testing-library/react@^11.2.6":
|
||||
version "11.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b"
|
||||
integrity sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@testing-library/dom" "^7.28.1"
|
||||
|
@ -3696,7 +3696,7 @@ deep-is@^0.1.3, deep-is@~0.1.3:
|
|||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
deepmerge@^4.0, deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
@ -6894,7 +6894,7 @@ locate-path@^5.0.0:
|
|||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lockfile@^1.0.4:
|
||||
lockfile@^1.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
|
||||
integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==
|
||||
|
@ -11213,14 +11213,14 @@ webidl-conversions@^6.1.0:
|
|||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
|
||||
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||
|
||||
webpack-assets-manifest@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-4.0.1.tgz#918989c51a7800be6683aaa27b9f36bcc7a9afdc"
|
||||
integrity sha512-NS7Bx2C3JsEj6a0MB/PPmPOD/BzDYjB3PaKcI7/r2fKXq0PuZ4YtcbZ5Og+q4gkmetGX9v21vejeAlbru/Fvhw==
|
||||
webpack-assets-manifest@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-4.0.2.tgz#ead6e6dbdcd1c2af45d11a382246fcc79a286372"
|
||||
integrity sha512-bBb9PvEGDOCFvW5/t6Yp9MEE0fymNJ0OvEud9nPvQegDbQEUZ/2WTeHnNoALwWMu1x3JHPyqHVYh8SwtYZ/dww==
|
||||
dependencies:
|
||||
chalk "^4.0"
|
||||
deepmerge "^4.2.2"
|
||||
lockfile "^1.0.4"
|
||||
deepmerge "^4.0"
|
||||
lockfile "^1.0"
|
||||
lodash.escaperegexp "^4.0"
|
||||
lodash.get "^4.0"
|
||||
lodash.has "^4.0"
|
||||
|
|
Reference in New Issue