@@ -0,0 +1,42 @@ | |||
# frozen_string_literal: true | |||
require 'sidekiq/api' | |||
module Admin | |||
class DashboardController < BaseController | |||
def index | |||
@users_count = User.count | |||
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0 | |||
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}") | |||
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0 | |||
@relay_enabled = Relay.enabled.exists? | |||
@single_user_mode = Rails.configuration.x.single_user_mode | |||
@registrations_enabled = Setting.open_registrations | |||
@deletions_enabled = Setting.open_deletions | |||
@invites_enabled = Setting.min_invite_role == 'user' | |||
@search_enabled = Chewy.enabled? | |||
@version = Mastodon::Version.to_s | |||
@database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] | |||
@redis_version = redis_info['redis_version'] | |||
@reports_count = Report.unresolved.count | |||
@queue_backlog = Sidekiq::Stats.new.enqueued | |||
@recent_users = User.confirmed.recent.includes(:account).limit(4) | |||
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size'] | |||
@redis_size = redis_info['used_memory'] | |||
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true' | |||
@cas_enabled = ENV['CAS_ENABLED'] == 'true' | |||
@saml_enabled = ENV['SAML_ENABLED'] == 'true' | |||
@pam_enabled = ENV['PAM_ENABLED'] == 'true' | |||
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' | |||
end | |||
private | |||
def current_week | |||
@current_week ||= Time.now.utc.to_date.cweek | |||
end | |||
def redis_info | |||
@redis_info ||= Redis.current.info | |||
end | |||
end | |||
end |
@@ -21,5 +21,6 @@ | |||
@import 'mastodon/about'; | |||
@import 'mastodon/tables'; | |||
@import 'mastodon/admin'; | |||
@import 'mastodon/dashboard'; | |||
@import 'mastodon/rtl'; | |||
@import 'mastodon/accessibility'; |
@@ -0,0 +1,69 @@ | |||
.dashboard__counters { | |||
display: flex; | |||
flex-wrap: wrap; | |||
margin: 0 -5px; | |||
margin-bottom: 20px; | |||
& > div { | |||
box-sizing: border-box; | |||
flex: 0 0 33.333%; | |||
padding: 0 5px; | |||
margin-bottom: 10px; | |||
& > div, | |||
& > a { | |||
padding: 20px; | |||
background: lighten($ui-base-color, 4%); | |||
border-radius: 4px; | |||
} | |||
& > a { | |||
text-decoration: none; | |||
color: inherit; | |||
display: block; | |||
&:hover, | |||
&:focus, | |||
&:active { | |||
background: lighten($ui-base-color, 8%); | |||
} | |||
} | |||
} | |||
&__num { | |||
text-align: center; | |||
font-weight: 500; | |||
font-size: 24px; | |||
color: $primary-text-color; | |||
font-family: 'mastodon-font-display', sans-serif; | |||
margin-bottom: 20px; | |||
} | |||
&__label { | |||
font-size: 14px; | |||
color: $darker-text-color; | |||
text-align: center; | |||
font-weight: 500; | |||
} | |||
} | |||
.dashboard__widgets { | |||
display: flex; | |||
flex-wrap: wrap; | |||
margin: 0 -5px; | |||
& > div { | |||
flex: 0 0 33.333%; | |||
margin-bottom: 20px; | |||
& > div { | |||
padding: 0 5px; | |||
} | |||
} | |||
a:not(.name-tag) { | |||
color: $ui-secondary-color; | |||
font-weight: 500; | |||
text-decoration: none; | |||
} | |||
} |
@@ -20,6 +20,8 @@ class PotentialFriendshipTracker | |||
redis.zincrby(key, weight, target_account_id) | |||
redis.zremrangebyrank(key, 0, -MAX_ITEMS) | |||
redis.expire(key, EXPIRE_AFTER) | |||
ActivityTracker.increment('activity:interactions') | |||
end | |||
def remove(account_id, target_account_id) | |||
@@ -0,0 +1,140 @@ | |||
- content_for :page_title do | |||
= t('admin.dashboard.title') | |||
.dashboard__counters | |||
%div | |||
= link_to admin_accounts_url(local: 1, recent: 1) do | |||
.dashboard__counters__num= number_with_delimiter @users_count | |||
.dashboard__counters__label= t 'admin.dashboard.total_users' | |||
%div | |||
%div | |||
.dashboard__counters__num= number_with_delimiter @registrations_week | |||
.dashboard__counters__label= t 'admin.dashboard.week_users_new' | |||
%div | |||
%div | |||
.dashboard__counters__num= number_with_delimiter @logins_week | |||
.dashboard__counters__label= t 'admin.dashboard.week_users_active' | |||
%div | |||
%div | |||
.dashboard__counters__num= number_with_delimiter @interactions_week | |||
.dashboard__counters__label= t 'admin.dashboard.week_interactions' | |||
%div | |||
= link_to admin_reports_url do | |||
.dashboard__counters__num= number_with_delimiter @reports_count | |||
.dashboard__counters__label= t 'admin.dashboard.open_reports' | |||
%div | |||
= link_to sidekiq_url do | |||
.dashboard__counters__num= number_with_delimiter @queue_backlog | |||
.dashboard__counters__label= t 'admin.dashboard.backlog' | |||
.dashboard__widgets | |||
.dashboard__widgets__users | |||
%div | |||
%h4= t 'admin.dashboard.recent_users' | |||
%ul | |||
- @recent_users.each do |user| | |||
%li= admin_account_link_to(user.account) | |||
.dashboard__widgets__features | |||
%div | |||
%h4= t 'admin.dashboard.features' | |||
%ul | |||
%li | |||
= link_to t('admin.dashboard.feature_registrations'), edit_admin_settings_path | |||
- if @registrations_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
= link_to t('admin.dashboard.feature_invites'), edit_admin_settings_path | |||
- if @invites_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
= link_to t('admin.dashboard.feature_deletions'), edit_admin_settings_path | |||
- if @deletions_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
= link_to t('admin.dashboard.feature_relay'), admin_relays_path | |||
- if @relay_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
.dashboard__widgets__versions | |||
%div | |||
%h4= t 'admin.dashboard.software' | |||
%ul | |||
%li | |||
Mastodon | |||
%span.pull-right= @version | |||
%li | |||
Ruby | |||
%span.pull-right= "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}" | |||
%li | |||
PostgreSQL | |||
%span.pull-right= @database_version | |||
%li | |||
Redis | |||
%span.pull-right= @redis_version | |||
.dashboard__widgets__space | |||
%div | |||
%h4= t 'admin.dashboard.space' | |||
%ul | |||
%li | |||
PostgreSQL | |||
%span.pull-right= number_to_human_size @database_size | |||
%li | |||
Redis | |||
%span.pull-right= number_to_human_size @redis_size | |||
.dashboard__widgets__config | |||
%div | |||
%h4= t 'admin.dashboard.config' | |||
%ul | |||
%li | |||
= t('admin.dashboard.search') | |||
- if @search_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
= t('admin.dashboard.single_user_mode') | |||
- if @single_user_mode | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
LDAP | |||
- if @ldap_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
CAS | |||
- if @cas_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
SAML | |||
- if @saml_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
PAM | |||
- if @pam_enabled | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' | |||
%li | |||
= t 'admin.dashboard.hidden_service' | |||
- if @hidden_service | |||
%span.pull-right.positive-hint= fa_icon 'check fw' | |||
- else | |||
%span.pull-right.negative-hint= fa_icon 'times fw' |
@@ -206,6 +206,26 @@ en: | |||
update_failed_msg: Could not update that emoji | |||
updated_msg: Emoji successfully updated! | |||
upload: Upload | |||
dashboard: | |||
backlog: backlogged jobs | |||
config: Configuration | |||
feature_deletions: Account deletions | |||
feature_invites: Invite links | |||
feature_registrations: Registrations | |||
feature_relay: Federation relay | |||
features: Features | |||
hidden_service: Federation with hidden services | |||
open_reports: open reports | |||
recent_users: Recent users | |||
search: Full-text search | |||
single_user_mode: Single user mode | |||
software: Software | |||
space: Space usage | |||
title: Dashboard | |||
total_users: users in total | |||
week_interactions: interactions this week | |||
week_users_active: active this week | |||
week_users_new: users this week | |||
domain_blocks: | |||
add_new: Add new | |||
created_msg: Domain block is now being processed | |||
@@ -33,7 +33,8 @@ SimpleNavigation::Configuration.run do |navigation| | |||
admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? } | |||
end | |||
primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), proc { current_user.admin? ? edit_admin_settings_url : admin_custom_emojis_url }, if: proc { current_user.staff? } do |admin| | |||
primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_dashboard_url, if: proc { current_user.staff? } do |admin| | |||
admin.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url | |||
admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? } | |||
admin.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis} | |||
admin.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/relays} | |||
@@ -124,6 +124,8 @@ Rails.application.routes.draw do | |||
resource :share, only: [:show, :create] | |||
namespace :admin do | |||
get '/dashboard', to: 'dashboard#index' | |||
resources :subscriptions, only: [:index] | |||
resources :domain_blocks, only: [:index, :new, :create, :show, :destroy] | |||
resources :email_domain_blocks, only: [:index, :new, :create, :destroy] | |||
@@ -196,13 +198,7 @@ Rails.application.routes.draw do | |||
resources :account_moderation_notes, only: [:create, :destroy] | |||
end | |||
authenticate :user, lambda { |u| u.admin? } do | |||
get '/admin', to: redirect('/admin/settings/edit', status: 302) | |||
end | |||
authenticate :user, lambda { |u| u.moderator? } do | |||
get '/admin', to: redirect('/admin/reports', status: 302) | |||
end | |||
get '/admin', to: redirect('/admin/dashboard', status: 302) | |||
namespace :api do | |||
# PubSubHubbub outgoing subscriptions | |||