mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-12 07:23:15 +00:00
Compare commits
9 Commits
d747651b99
...
56f8b67c19
Author | SHA1 | Date | |
---|---|---|---|
![]() |
56f8b67c19 | ||
![]() |
3b52dca405 | ||
![]() |
853a0c466e | ||
![]() |
5910d5096d | ||
![]() |
010688a67a | ||
![]() |
c98dfb827b | ||
![]() |
7535ad01d6 | ||
![]() |
53a4a3c8cf | ||
![]() |
39d2f132e8 |
|
@ -21,6 +21,15 @@ module Admin
|
|||
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(LOGS_LIMIT)
|
||||
end
|
||||
|
||||
def availability
|
||||
authorize :instance, :show?
|
||||
end
|
||||
|
||||
def statistics
|
||||
authorize :instance, :show?
|
||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize :instance, :destroy?
|
||||
Admin::DomainPurgeWorker.perform_async(@instance.domain)
|
||||
|
@ -31,7 +40,7 @@ module Admin
|
|||
def clear_delivery_errors
|
||||
authorize :delivery, :clear_delivery_errors?
|
||||
@instance.delivery_failure_tracker.clear_failures!
|
||||
redirect_to admin_instance_path(@instance.domain)
|
||||
redirect_to availability_admin_instance_path(@instance.domain)
|
||||
end
|
||||
|
||||
def restart_delivery
|
||||
|
@ -42,14 +51,14 @@ module Admin
|
|||
log_action :destroy, @instance.unavailable_domain
|
||||
end
|
||||
|
||||
redirect_to admin_instance_path(@instance.domain)
|
||||
redirect_to availability_admin_instance_path(@instance.domain)
|
||||
end
|
||||
|
||||
def stop_delivery
|
||||
authorize :delivery, :stop_delivery?
|
||||
unavailable_domain = UnavailableDomain.create!(domain: @instance.domain)
|
||||
log_action :create, unavailable_domain
|
||||
redirect_to admin_instance_path(@instance.domain)
|
||||
redirect_to availability_admin_instance_path(@instance.domain)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -26,6 +26,12 @@ module ContextHelper
|
|||
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
||||
attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } },
|
||||
quote_requests: { 'QuoteRequest' => 'https://w3id.org/fep/044f#QuoteRequest' },
|
||||
quotes: {
|
||||
'quote' => 'https://w3id.org/fep/044f#quote',
|
||||
'quoteUri' => 'http://fedibird.com/ns#quoteUri',
|
||||
'_misskey_quote' => 'https://misskey-hub.net/ns#_misskey_quote',
|
||||
'quoteAuthorization' => 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
},
|
||||
interaction_policies: {
|
||||
'gts' => 'https://gotosocial.org/ns#',
|
||||
'interactionPolicy' => { '@id' => 'gts:interactionPolicy', '@type' => '@id' },
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { useLinks } from 'mastodon/hooks/useLinks';
|
||||
|
||||
export const AccountBio: React.FC<{
|
||||
interface AccountBioProps {
|
||||
note: string;
|
||||
className: string;
|
||||
}> = ({ note, className }) => {
|
||||
const handleClick = useLinks();
|
||||
dropdownAccountId?: string;
|
||||
}
|
||||
|
||||
if (note.length === 0 || note === '<p></p>') {
|
||||
export const AccountBio: React.FC<AccountBioProps> = ({
|
||||
note,
|
||||
className,
|
||||
dropdownAccountId,
|
||||
}) => {
|
||||
const handleClick = useLinks(!!dropdownAccountId);
|
||||
const handleNodeChange = useCallback(
|
||||
(node: HTMLDivElement | null) => {
|
||||
if (!dropdownAccountId || !node || node.childNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
addDropdownToHashtags(node, dropdownAccountId);
|
||||
},
|
||||
[dropdownAccountId],
|
||||
);
|
||||
|
||||
if (note.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -15,6 +33,28 @@ export const AccountBio: React.FC<{
|
|||
className={`${className} translate`}
|
||||
dangerouslySetInnerHTML={{ __html: note }}
|
||||
onClickCapture={handleClick}
|
||||
ref={handleNodeChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function addDropdownToHashtags(node: HTMLElement | null, accountId: string) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
for (const childNode of node.childNodes) {
|
||||
if (!(childNode instanceof HTMLElement)) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
childNode instanceof HTMLAnchorElement &&
|
||||
(childNode.classList.contains('hashtag') ||
|
||||
childNode.innerText.startsWith('#')) &&
|
||||
!childNode.dataset.menuHashtag
|
||||
) {
|
||||
childNode.dataset.menuHashtag = accountId;
|
||||
} else if (childNode.childNodes.length > 0) {
|
||||
addDropdownToHashtags(childNode, accountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ export default class Dimension extends PureComponent {
|
|||
</tbody>
|
||||
</table>
|
||||
);
|
||||
} else {
|
||||
} else if (data[0].data.length > 0) {
|
||||
const sum = data[0].data.reduce((sum, cur) => sum + (cur.value * 1), 0);
|
||||
|
||||
content = (
|
||||
|
@ -81,6 +81,16 @@ export default class Dimension extends PureComponent {
|
|||
</tbody>
|
||||
</table>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<table>
|
||||
<tbody>
|
||||
<tr className='dimension__item'>
|
||||
<td>No data available</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,6 +6,7 @@ import classNames from 'classnames';
|
|||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { AccountBio } from '@/mastodon/components/account_bio';
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
|
@ -773,7 +774,6 @@ export const AccountHeader: React.FC<{
|
|||
);
|
||||
}
|
||||
|
||||
const content = { __html: account.note_emojified };
|
||||
const displayNameHtml = { __html: account.display_name_html };
|
||||
const fields = account.fields;
|
||||
const isLocal = !account.acct.includes('@');
|
||||
|
@ -897,12 +897,11 @@ export const AccountHeader: React.FC<{
|
|||
<AccountNote accountId={accountId} />
|
||||
)}
|
||||
|
||||
{account.note.length > 0 && account.note !== '<p></p>' && (
|
||||
<div
|
||||
className='account__header__content translate'
|
||||
dangerouslySetInnerHTML={content}
|
||||
<AccountBio
|
||||
note={account.note_emojified}
|
||||
dropdownAccountId={accountId}
|
||||
className='account__header__content'
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='account__header__fields'>
|
||||
<dl>
|
||||
|
|
|
@ -8,13 +8,14 @@ import { openURL } from 'mastodon/actions/search';
|
|||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
const isMentionClick = (element: HTMLAnchorElement) =>
|
||||
element.classList.contains('mention');
|
||||
element.classList.contains('mention') &&
|
||||
!element.classList.contains('hashtag');
|
||||
|
||||
const isHashtagClick = (element: HTMLAnchorElement) =>
|
||||
element.textContent?.[0] === '#' ||
|
||||
element.previousSibling?.textContent?.endsWith('#');
|
||||
|
||||
export const useLinks = () => {
|
||||
export const useLinks = (skipHashtags?: boolean) => {
|
||||
const history = useHistory();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
@ -61,12 +62,12 @@ export const useLinks = () => {
|
|||
if (isMentionClick(target)) {
|
||||
e.preventDefault();
|
||||
void handleMentionClick(target);
|
||||
} else if (isHashtagClick(target)) {
|
||||
} else if (isHashtagClick(target) && !skipHashtags) {
|
||||
e.preventDefault();
|
||||
handleHashtagClick(target);
|
||||
}
|
||||
},
|
||||
[handleMentionClick, handleHashtagClick],
|
||||
[skipHashtags, handleMentionClick, handleHashtagClick],
|
||||
);
|
||||
|
||||
return handleClick;
|
||||
|
|
|
@ -126,6 +126,9 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
|
|||
? accountJSON.username
|
||||
: accountJSON.display_name;
|
||||
|
||||
const accountNote =
|
||||
accountJSON.note && accountJSON.note !== '<p></p>' ? accountJSON.note : '';
|
||||
|
||||
return AccountFactory({
|
||||
...accountJSON,
|
||||
moved: moved?.id,
|
||||
|
@ -142,8 +145,8 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
|
|||
escapeTextContentForBrowser(displayName),
|
||||
emojiMap,
|
||||
),
|
||||
note_emojified: emojify(accountJSON.note, emojiMap),
|
||||
note_plain: unescapeHTML(accountJSON.note),
|
||||
note_emojified: emojify(accountNote, emojiMap),
|
||||
note_plain: unescapeHTML(accountNote),
|
||||
url:
|
||||
accountJSON.url.startsWith('http://') ||
|
||||
accountJSON.url.startsWith('https://')
|
||||
|
|
|
@ -309,6 +309,27 @@ $content-width: 840px;
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
color: $secondary-text-color;
|
||||
margin-bottom: 30px;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
p.with-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.fields-group h6 {
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
|
@ -609,6 +630,25 @@ body,
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.simple_form {
|
||||
.actions {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
&__button--end {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.batch-form-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -1835,7 +1875,7 @@ a.sparkline {
|
|||
.availability-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
|
||||
|
|
|
@ -62,11 +62,20 @@
|
|||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
|
||||
grid-gap: 10px;
|
||||
align-content: start;
|
||||
|
||||
@media screen and (width <= 1350px) {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
&__hint {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
grid-column: span 3;
|
||||
align-items: center;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&__item {
|
||||
&--span-double-column {
|
||||
grid-column: span 2;
|
||||
|
@ -118,4 +127,8 @@
|
|||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&--instance-overview {
|
||||
min-height: 400px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ class DeliveryFailureTracker
|
|||
include Redisable
|
||||
|
||||
FAILURE_DAYS_THRESHOLD = 7
|
||||
MEASURED_DAYS = 14
|
||||
|
||||
def initialize(url_or_host)
|
||||
@host = url_or_host.start_with?('https://', 'http://') ? Addressable::URI.parse(url_or_host).normalized_host : url_or_host
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
%br/
|
||||
|
||||
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.federation_policies.policies') }.join(' · ')
|
||||
- if f.object.public_comment.present?
|
||||
·
|
||||
= f.object.public_comment
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
-# locals: (instance_domain:, period_end_at:, period_start_at:)
|
||||
%p
|
||||
= material_symbol 'info'
|
||||
= t('admin.instances.totals_time_period_hint_html')
|
||||
|
||||
.dashboard
|
||||
.dashboard.dashboard--instance-overview
|
||||
.dashboard__item
|
||||
= react_admin_component :counter,
|
||||
end_at: period_end_at,
|
||||
|
@ -48,19 +45,6 @@
|
|||
measure: 'instance_reports',
|
||||
params: { domain: instance_domain },
|
||||
start_at: period_start_at
|
||||
.dashboard__item
|
||||
= react_admin_component :dimension,
|
||||
dimension: 'instance_accounts',
|
||||
end_at: period_end_at,
|
||||
label: t('admin.instances.dashboard.instance_accounts_dimension'),
|
||||
limit: 8,
|
||||
params: { domain: instance_domain },
|
||||
start_at: period_start_at
|
||||
.dashboard__item
|
||||
= react_admin_component :dimension,
|
||||
dimension: 'instance_languages',
|
||||
end_at: period_end_at,
|
||||
label: t('admin.instances.dashboard.instance_languages_dimension'),
|
||||
limit: 8,
|
||||
params: { domain: instance_domain },
|
||||
start_at: period_start_at
|
||||
%p.dashboard__hint
|
||||
= material_symbol 'info'
|
||||
= t('admin.instances.totals_time_period_hint_html')
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
%small
|
||||
- if instance.domain_block
|
||||
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.federation_policies.policies') }.join(' · ')
|
||||
- if instance.domain_block.public_comment.present?
|
||||
%span.comment.public-comment #{t('admin.domain_blocks.public_comment')}: #{instance.domain_block.public_comment}
|
||||
- if instance.domain_block.private_comment.present?
|
||||
|
|
47
app/views/admin/instances/availability.html.haml
Normal file
47
app/views/admin/instances/availability.html.haml
Normal file
|
@ -0,0 +1,47 @@
|
|||
- content_for :page_title do
|
||||
= @instance.domain
|
||||
|
||||
- content_for :heading do
|
||||
%h2= t('admin.instances.instance.title', domain: @instance.domain)
|
||||
= render partial: 'admin/instances/shared/links', locals: { instance: @instance }
|
||||
|
||||
%p.lead= t('admin.instances.availability.subtitle')
|
||||
|
||||
- if @instance.persisted?
|
||||
%p
|
||||
= t('admin.instances.availability.description_html', count: DeliveryFailureTracker::FAILURE_DAYS_THRESHOLD)
|
||||
= t('admin.instances.availability.period_description', count: DeliveryFailureTracker::MEASURED_DAYS)
|
||||
|
||||
.availability-indicator
|
||||
%ul.availability-indicator__graphic
|
||||
- @instance.availability_over_days(DeliveryFailureTracker::MEASURED_DAYS).each do |(date, failing)|
|
||||
%li.availability-indicator__graphic__item{ class: failing ? 'negative' : 'neutral', title: l(date) }
|
||||
.availability-indicator__hint
|
||||
- if @instance.unavailable?
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failure_threshold_reached', date: l(@instance.unavailable_domain.created_at.to_date))
|
||||
- elsif @instance.exhausted_deliveries_days.empty?
|
||||
%span.positive-hint
|
||||
= t('admin.instances.availability.no_failures_recorded')
|
||||
- else
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days)
|
||||
|
||||
.button-group
|
||||
- if @instance.unavailable?
|
||||
= link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button'
|
||||
- elsif @instance.exhausted_deliveries_days.empty?
|
||||
= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button button--destructive'
|
||||
- else
|
||||
= link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button button-secondary' unless @instance.exhausted_deliveries_days.empty?
|
||||
= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button button--destructive'
|
||||
|
||||
%hr.spacer
|
||||
|
||||
- if @instance.purgeable?
|
||||
%h3= t('admin.instances.purge_title')
|
||||
%p= t('admin.instances.purge_description_html')
|
||||
|
||||
= link_to t('admin.instances.purge'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button button--destructive'
|
||||
- else
|
||||
%p= t('admin.instances.availability.unknown_instance')
|
6
app/views/admin/instances/shared/_links.html.haml
Normal file
6
app/views/admin/instances/shared/_links.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
.content__heading__tabs
|
||||
= render_navigation renderer: :links do |primary|
|
||||
:ruby
|
||||
primary.item :overview, safe_join([material_symbol('cloud'), t('admin.instances.title')]), admin_instance_path(instance)
|
||||
primary.item :statistics, safe_join([material_symbol('trending_up'), t('admin.instances.statistics.title')]), statistics_admin_instance_path(instance)
|
||||
primary.item :availability, safe_join([material_symbol('warning'), t('admin.instances.availability.title')]), availability_admin_instance_path(instance)
|
|
@ -1,6 +1,10 @@
|
|||
- content_for :page_title do
|
||||
= @instance.domain
|
||||
|
||||
- content_for :heading do
|
||||
%h2= t('admin.instances.instance.title', domain: @instance.domain)
|
||||
= render partial: 'admin/instances/shared/links', locals: { instance: @instance }
|
||||
|
||||
- if current_user.can?(:view_dashboard)
|
||||
- content_for :heading_actions do
|
||||
= date_range(@time_period)
|
||||
|
@ -13,34 +17,49 @@
|
|||
|
||||
%hr.spacer/
|
||||
|
||||
%h3= t('admin.instances.content_policies.title')
|
||||
%h3= t('admin.instances.federation_policies.title')
|
||||
|
||||
- if limited_federation_mode?
|
||||
%p= t('admin.instances.content_policies.limited_federation_mode_description_html')
|
||||
%p= t('admin.instances.federation_policies.limited_federation_mode_description_html')
|
||||
|
||||
- if @instance.domain_allow
|
||||
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
- else
|
||||
= link_to t('admin.domain_allows.add_new'), admin_domain_allows_path(domain_allow: { domain: @instance.domain }), class: 'button', method: :post
|
||||
- else
|
||||
%p= t('admin.instances.content_policies.description_html')
|
||||
%p= t('admin.instances.federation_policies.description_html')
|
||||
|
||||
- if @instance.domain_block
|
||||
.table-wrapper
|
||||
%table.table.horizontal-table
|
||||
%thead
|
||||
%tr
|
||||
%th{ width: '28%' }
|
||||
%th
|
||||
%tbody
|
||||
%tr
|
||||
%th= t('admin.instances.content_policies.comment')
|
||||
%td= @instance.domain_block.private_comment
|
||||
%th= t('admin.instances.federation_policies.comment')
|
||||
%td
|
||||
- if @instance.domain_block.private_comment.present?
|
||||
= @instance.domain_block.private_comment
|
||||
- else
|
||||
%em= t('admin.instances.federation_policies.no_comment')
|
||||
%tr
|
||||
%th= t('admin.instances.content_policies.reason')
|
||||
%td= @instance.domain_block.public_comment
|
||||
%th= t('admin.instances.federation_policies.reason')
|
||||
%td
|
||||
- if @instance.domain_block.public_comment.present?
|
||||
= @instance.domain_block.public_comment
|
||||
- else
|
||||
%em= t('admin.instances.federation_policies.no_comment')
|
||||
%tr
|
||||
%th= t('admin.instances.content_policies.policy')
|
||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
%th= t('admin.instances.federation_policies.policy')
|
||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.federation_policies.policies') }.join(' · ')
|
||||
|
||||
.button-group
|
||||
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
|
||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button button-secondary', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
- if @instance.purgeable?
|
||||
= link_to t('admin.instances.federation_policies.purge_data'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button button-tertiary button--destructive button-group__button--end'
|
||||
- else
|
||||
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
|
||||
|
||||
|
@ -48,12 +67,13 @@
|
|||
%hr.spacer/
|
||||
|
||||
%h3= t('admin.instances.audit_log.title')
|
||||
%p= t('admin.instances.audit_log.description')
|
||||
- if @action_logs.empty?
|
||||
%p= t('accounts.nothing_here')
|
||||
- else
|
||||
.report-notes
|
||||
= render partial: 'admin/action_logs/action_log', collection: @action_logs
|
||||
= link_to t('admin.instances.audit_log.view_all'), admin_action_logs_path(target_domain: @instance.domain), class: 'button'
|
||||
= link_to t('admin.instances.audit_log.view_all'), admin_action_logs_path(target_domain: @instance.domain), class: 'button button-secondary'
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
|
@ -71,35 +91,3 @@
|
|||
|
||||
.actions
|
||||
= form.button :button, t('admin.instances.moderation_notes.create'), type: :submit
|
||||
|
||||
- if @instance.persisted?
|
||||
%hr.spacer/
|
||||
|
||||
%h3= t('admin.instances.availability.title')
|
||||
|
||||
%p
|
||||
= t('admin.instances.availability.description_html', count: DeliveryFailureTracker::FAILURE_DAYS_THRESHOLD)
|
||||
|
||||
.availability-indicator
|
||||
%ul.availability-indicator__graphic
|
||||
- @instance.availability_over_days(14).each do |(date, failing)|
|
||||
%li.availability-indicator__graphic__item{ class: failing ? 'negative' : 'neutral', title: l(date) }
|
||||
.availability-indicator__hint
|
||||
- if @instance.unavailable?
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failure_threshold_reached', date: l(@instance.unavailable_domain.created_at.to_date))
|
||||
= link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
- elsif @instance.exhausted_deliveries_days.empty?
|
||||
%span.positive-hint
|
||||
= t('admin.instances.availability.no_failures_recorded')
|
||||
= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
- else
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days)
|
||||
%span= link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } unless @instance.exhausted_deliveries_days.empty?
|
||||
%span= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
|
||||
- if @instance.purgeable?
|
||||
%p= t('admin.instances.purge_description_html')
|
||||
|
||||
= link_to t('admin.instances.purge'), admin_instance_path(@instance), data: { confirm: t('admin.instances.confirm_purge'), method: :delete }, class: 'button button--destructive'
|
||||
|
|
28
app/views/admin/instances/statistics.html.haml
Normal file
28
app/views/admin/instances/statistics.html.haml
Normal file
|
@ -0,0 +1,28 @@
|
|||
- content_for :page_title do
|
||||
= @instance.domain
|
||||
|
||||
- content_for :heading do
|
||||
%h2= t('admin.instances.instance.title', domain: @instance.domain)
|
||||
= render partial: 'admin/instances/shared/links', locals: { instance: @instance }
|
||||
|
||||
- if @instance.persisted?
|
||||
%p.lead= t('admin.instances.statistics.subtitle', domain: @instance.domain)
|
||||
.dashboard
|
||||
.dashboard__item
|
||||
= react_admin_component :dimension,
|
||||
dimension: 'instance_accounts',
|
||||
label: t('admin.instances.dashboard.instance_accounts_dimension'),
|
||||
params: { domain: @instance.domain },
|
||||
start_at: @time_period.first,
|
||||
end_at: @time_period.last,
|
||||
limit: 8
|
||||
.dashboard__item
|
||||
= react_admin_component :dimension,
|
||||
dimension: 'instance_languages',
|
||||
label: t('admin.instances.dashboard.instance_languages_dimension'),
|
||||
params: { domain: @instance.domain },
|
||||
start_at: @time_period.first,
|
||||
end_at: @time_period.last,
|
||||
limit: 8
|
||||
- else
|
||||
%p.lead= t('admin.instances.availability.unknown_instance')
|
|
@ -520,8 +520,9 @@ en:
|
|||
unsuppress: Restore follow recommendation
|
||||
instances:
|
||||
audit_log:
|
||||
title: Recent Audit Logs
|
||||
view_all: View full audit logs
|
||||
description: These are the 5 most recent audit log entries relating to this instance.
|
||||
title: Audit Logs
|
||||
view_all: View all audit logs
|
||||
availability:
|
||||
description_html:
|
||||
one: If delivering to the domain fails <strong>%{count} day</strong> without succeeding, no further delivery attempts will be made unless a delivery <em>from</em> the domain is received.
|
||||
|
@ -531,25 +532,16 @@ en:
|
|||
one: Failed attempt on %{count} day.
|
||||
other: Failed attempts on %{count} different days.
|
||||
no_failures_recorded: No failures on record.
|
||||
period_description: The bars shown are for delivery status over the last %{count} days.
|
||||
subtitle: Instance availability measures how successfully we've been able to federate
|
||||
title: Availability
|
||||
unknown_instance: There is no data available for this instance as this server doesn't currently federate with it.
|
||||
warning: The last attempt to connect to this server has been unsuccessful
|
||||
back_to_all: All
|
||||
back_to_limited: Limited
|
||||
back_to_warning: Warning
|
||||
by_domain: Domain
|
||||
confirm_purge: Are you sure you want to permanently delete data from this domain?
|
||||
content_policies:
|
||||
comment: Internal note
|
||||
description_html: You can define content policies that will be applied to all accounts from this domain and any of its subdomains.
|
||||
limited_federation_mode_description_html: You can chose whether to allow federation with this domain.
|
||||
policies:
|
||||
reject_media: Reject media
|
||||
reject_reports: Reject reports
|
||||
silence: Limit
|
||||
suspend: Suspend
|
||||
policy: Policy
|
||||
reason: Public reason
|
||||
title: Content policies
|
||||
dashboard:
|
||||
instance_accounts_dimension: Most followed accounts
|
||||
instance_accounts_measure: stored accounts
|
||||
|
@ -571,6 +563,22 @@ en:
|
|||
delivery_error_hint: If delivery is not possible for %{count} days, it will be automatically marked as undeliverable.
|
||||
destroyed_msg: Data from %{domain} is now queued for imminent deletion.
|
||||
empty: No domains found.
|
||||
federation_policies:
|
||||
comment: Private note
|
||||
description_html: You can set a federation policy that will be applied to all accounts from this domain and any of its subdomains.
|
||||
limited_federation_mode_description_html: You can chose whether to allow federation with this domain.
|
||||
no_comment: None
|
||||
policies:
|
||||
reject_media: Reject media
|
||||
reject_reports: Reject reports
|
||||
silence: Limit
|
||||
suspend: Suspend
|
||||
policy: Policy
|
||||
purge_data: Purge Data
|
||||
reason: Public reason
|
||||
title: Federation policy
|
||||
instance:
|
||||
title: Federation with %{domain}
|
||||
known_accounts:
|
||||
one: "%{count} known account"
|
||||
other: "%{count} known accounts"
|
||||
|
@ -587,15 +595,19 @@ en:
|
|||
title: Moderation Notes
|
||||
private_comment: Private comment
|
||||
public_comment: Public comment
|
||||
purge: Purge
|
||||
purge: Purge Data
|
||||
purge_description_html: If you believe this domain is offline for good, you can delete all account records and associated data from this domain from your storage. This may take a while.
|
||||
purge_title: Purge Data
|
||||
statistics:
|
||||
subtitle: Below are a few statistics about %{domain}, that you may find interesting.
|
||||
title: Statistics
|
||||
title: Federation
|
||||
total_blocked_by_us: Blocked by us
|
||||
total_followed_by_them: Followed by them
|
||||
total_followed_by_us: Followed by us
|
||||
total_reported: Reports about them
|
||||
total_storage: Media attachments
|
||||
totals_time_period_hint_html: The totals displayed below include data for all time.
|
||||
totals_time_period_hint_html: The totals displayed above include data for all time.
|
||||
unknown_instance: There is currently no record of this domain on this server.
|
||||
invites:
|
||||
deactivate_all: Deactivate all
|
||||
|
|
|
@ -87,6 +87,8 @@ namespace :admin do
|
|||
|
||||
resources :instances, only: [:index, :show, :destroy], constraints: { id: %r{[^/]+} }, format: 'html' do
|
||||
member do
|
||||
get :availability
|
||||
get :statistics
|
||||
post :clear_delivery_errors
|
||||
post :restart_delivery
|
||||
post :stop_delivery
|
||||
|
|
Loading…
Reference in New Issue
Block a user