@inherits LocalizableComponentBase @* One server of the federation statistics (/clientapi/admin/statistics/hosts/{host}), opened from its row: what it says of itself and what PrivaPub knows of its deliveries (as nerd rows, by their field names), its days with the main counters (newest first; a live day is today's, or one not rolled up yet), and its latest events. "Describe again" asks PrivaPub to read its NodeInfo and instance description anew. Counted per server, never per account. *@ @HostName
@Localizer["Describe again"]
@if (IsLoading) {

@Localizer["loading"]

} else if (detail is null) {

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

} else { @if (detail.Instance is { } instance) { } @Localizer["Day"] @Localizer["Received"] @Localizer["In"] @Localizer["Out"] @Localizer["Failures"] @Localizer["Accounts"] @foreach (var day in detail.Days.OrderByDescending(d => d.Day)) { @day.Day.ToString(SUtility.dateTimeFormat) @if (day.Live) { @Localizer["live"] } @Channel(day.Counters, "recv") @Channel(day.Counters, "in") @Channel(day.Counters, "out") @Failures(day.Counters) @day.Accounts } @if (detail.Events.Count > 0) { @Localizer["Last events"] }

@detail.Attribution

}
@code { [Inject] AdminService AdminService { get; set; } [Parameter, EditorRequired] public string HostName { get; set; } /// How many days back its days go. [Parameter] public int Days { get; set; } = 30; [Parameter] public EventCallback OnClosed { get; set; } ViewHostDetail detail; bool isDescribing; protected override async Task OnInitializedAsync() { if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); IsLoading = false; } async Task LoadData() => detail = await AdminService.Host(HostName, Days); async Task Describe() { isDescribing = true; var result = await AdminService.Describe(HostName); isDescribing = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } Toast.AddResponseSuccess(Localizer["PrivaPub will read what {0} says of itself again.", HostName]); } /// A channel's counters of a day (recv: deliveries to PrivaPub's inboxes; in: activities taken in; out: sent). static long Channel(Dictionary counters, string channel) => counters.Where(c => c.Key.StartsWith(channel + ":", StringComparison.Ordinal)).Sum(c => c.Value); // the deliveries out that failed, are retried or were given up, as the servers table counts them static long Failures(Dictionary counters) => counters.Where(c => c.Key.Split(':') is { Length: >= 4 } parts && parts[0] == "out" && parts[3] is "retry" or "dead" or "failed").Sum(c => c.Value); static IEnumerable InstanceRows(ViewHostInstance instance) { yield return NerdRow.Of("software", $"{instance.Software} {instance.SoftwareVersion}".Trim()); yield return NerdRow.Of("nodeName", instance.NodeName); yield return NerdRow.Of("title", instance.Title); yield return NerdRow.Of("nodeDescription", instance.NodeDescription); yield return NerdRow.Of("openRegistrations", instance.OpenRegistrations); yield return NerdRow.Of("registrationMode", instance.RegistrationMode); yield return NerdRow.Of("usersTotal", instance.UsersTotal); yield return NerdRow.Of("usersActiveMonth", instance.UsersActiveMonth); yield return NerdRow.Of("localPosts", instance.LocalPosts); yield return NerdRow.Of("languages", instance.Languages.Count > 0 ? string.Join(", ", instance.Languages) : null); yield return NerdRow.Of("seen", instance.Seen); yield return NerdRow.Of("firstSeenAt", instance.FirstSeenAt); yield return NerdRow.Of("lastSeenAt", instance.LastSeenAt); yield return NerdRow.Of("describedAt", instance.DescribedAt); yield return NerdRow.Of("descriptionError", instance.DescriptionError); yield return NerdRow.Of("lastSuccessAt", instance.LastSuccessAt); yield return NerdRow.Of("lastFailureAt", instance.LastFailureAt); yield return NerdRow.Of("lastError", instance.LastError); yield return NerdRow.Of("consecutiveFailures", instance.ConsecutiveFailures > 0 ? instance.ConsecutiveFailures : null); yield return NerdRow.Of("unavailableUntil", instance.UnavailableUntil); yield return NerdRow.Of("place", string.Join(", ", new[] { instance.City, instance.Country }.Where(p => !string.IsNullOrWhiteSpace(p))) is { Length: > 0 } place ? place : null); yield return NerdRow.Of("asn", instance.Asn is { } asn ? $"AS{asn} {instance.AsnOrg}".Trim() : null); yield return NerdRow.Of("cdn", instance.Cdn); yield return NerdRow.Of("instanceApi", instance.InstanceApi); yield return NerdRow.Of("sourceUrl", instance.SourceUrl); } Task Closed(bool open) => open ? Task.CompletedTask : OnClosed.InvokeAsync(); }