@inherits LocalizableComponentBase @* PrivaPub's federation statistics (/clientapi/admin/statistics): the last days at a glance, the servers exchanged with (by traffic, failures, latency or name, 50 a page) and how far the crawler got. Counted per server, never per account. *@
@if (IsLoading) {

@Localizer["loading"]

} else if (overview is null) {

@Localizer["The statistics could not be read."]

} else { @Localizer["Deliveries that arrived"] @(overview.DeliverySuccess is { } success ? $"{success * 100:0.#}%" : "—") @Localizer["p50 {0} ms, p95 {1} ms", Ms(overview.DeliveryLatencyMs.P50), Ms(overview.DeliveryLatencyMs.P95)] @Localizer["servers"] @overview.Hosts.Active @Localizer["{0} known", overview.Hosts.Known] @Localizer["Accounts seen"] @overview.Accounts @Localizer["ledger {0} written, {1} dropped", overview.Ledger.Written, overview.Ledger.Dropped]
@foreach (var (title, counts) in Outcomes) {
@title @foreach (var (outcome, count) in counts.OrderByDescending(c => c.Value)) { @outcome@count }
}
@if (hosts is not null) { @Localizer["server"] @Localizer["software"] @Localizer["In"] @Localizer["Out"] @Localizer["Refused"] @Localizer["Failures"] p50 @Localizer["Delivery"] @foreach (var host in hosts.Hosts) { @host.Host @host.Software @host.Version @host.In @host.Out @host.Refused @host.Failures @Ms(host.LatencyP50) @if (host.Delivery?.UnavailableUntil is { } until && until > DateTime.UtcNow) { @Localizer["paused"] } else if (host.Delivery?.ConsecutiveFailures > 0) { @Localizer["{0} failing", host.Delivery.ConsecutiveFailures] } } @if (hosts.Total > PageSize) { }

@hosts.Attribution

} @if (crawler is not null) {
@Localizer["Crawler"] @(crawler.Enabled ? Localizer["on"] : Localizer["off"]) · @Localizer["{0} servers touched, {1} only crawled, {2} due, {3} refuse robots", crawler.Touched, crawler.Crawled, crawler.Due, crawler.RobotsDisallowed] @foreach (var host in crawler.Recent) { @host.Host @host.Software @if (host.LastCrawledAt is { } crawled) { @crawled.GetPassedTime(Localizer) } @if (host.CrawlError is { Length: > 0 } crawlError) { @crawlError } }
} }
@code { [Inject] AdminService AdminService { get; set; } const int PageSize = 50; static readonly int[] Periods = [1, 7, 30, 90]; ViewStatisticsOverview overview; ViewHostsPage hosts; ViewCrawler crawler; int days = 30; string sort = "traffic"; int page = 1; (string Title, Dictionary Counts)[] Outcomes => [ (Localizer["Received"], overview.Inbound), (Localizer["Delivered"], overview.Outbound), (Localizer["Channels"], overview.Channels) ]; protected override async Task OnInitializedAsync() { if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); IsLoading = false; } async Task LoadData() { overview = await AdminService.Overview(days); hosts = await AdminService.Hosts(days, sort, page); crawler = await AdminService.Crawler(); } static string Ms(int? value) => value?.ToString() ?? "—"; async Task ChangeDays(int value) { days = value; page = 1; await LoadData(); } async Task ChangeSort(string value) { sort = value; page = 1; hosts = await AdminService.Hosts(days, sort, page); } async Task ChangePage(int value) { page = value; hosts = await AdminService.Hosts(days, sort, page); } }