161 lines
5.1 KiB
Plaintext
161 lines
5.1 KiB
Plaintext
@inherits LocalizableComponentBase
|
|||
|
|
|
||
|
|
@* The staff's log (/clientapi/moderator/log), newest first, 50 at a time: who acted (to administrators every login's
|
||
|
|
name; to a moderator only its own; "the command line" for PrivaPub's CLI; nobody when an act on a persona of the
|
||
|
|
acting login would tie the two), what was done, to what, and its note. Never an IP address. A moderator who isn't an
|
||
|
|
administrator reads what isn't about a login. *@
|
||
|
|
<div class="flex flex-col gap-2 w-full" data-log>
|
||
|
|
<div class="flex flex-wrap items-center gap-2">
|
||
|
|
<DButton Size="DSize.Sm" Disabled="IsDefaultDisabled || isLoadingMore" OnClick="Reload" data-log-reload>
|
||
|
|
<i class="ion-md-refresh" aria-hidden="true"></i><span>@Localizer["Read again"]</span>
|
||
|
|
</DButton>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
@if (IsLoading)
|
||
|
|
{
|
||
|
|
<p class="flex w-full items-center justify-center gap-2">
|
||
|
|
<DLoading Variant="DLoadingVariant.Dots" LabelText="@Localizer["loading"]" />
|
||
|
|
@Localizer["loading"]
|
||
|
|
</p>
|
||
|
|
}
|
||
|
|
else if (failed)
|
||
|
|
{
|
||
|
|
<p class="text-sm text-error">@Localizer["The log could not be read."]</p>
|
||
|
|
}
|
||
|
|
else if (entries.Count == 0)
|
||
|
|
{
|
||
|
|
<p class="text-sm opacity-70" data-log-empty>@Localizer["Nothing was done yet."]</p>
|
||
|
|
}
|
||
|
|
|
||
|
|
<ol class="flex flex-col gap-2">
|
||
|
|
@foreach (var entry in entries)
|
||
|
|
{
|
||
|
|
<li class="flex flex-col gap-1 p-2 rounded-lg neo-raised neo-2xs" data-log-entry="@entry.Id" data-log-action="@entry.Action">
|
||
|
|
<div class="flex flex-wrap items-center gap-2">
|
||
|
|
<b class="text-sm">@ActionName(entry.Action)</b>
|
||
|
|
@if (entry.TargetLabel is { Length: > 0 } label)
|
||
|
|
{
|
||
|
|
<span class="text-sm break-all min-w-0" title="@($"{entry.TargetKind} {entry.TargetId}")" data-log-target>@label</span>
|
||
|
|
}
|
||
|
|
<span class="flex-1"></span>
|
||
|
|
<span class="text-xs opacity-70" title="@entry.At.ToLocalTime().ToString(SUtility.dateTimeFormatExt)">@entry.At.GetPassedTime(Localizer)</span>
|
||
|
|
</div>
|
||
|
|
<div class="flex flex-wrap items-center gap-2 text-xs">
|
||
|
|
<span data-log-by="@(entry.ByYou ? "you" : entry.By ?? "none")">@By(entry)</span>
|
||
|
|
<code class="opacity-60">@entry.Action</code>
|
||
|
|
@if (entry.ReportId is { Length: > 0 } reportId)
|
||
|
|
{
|
||
|
|
<span class="opacity-70">@Localizer["Report: {0}", reportId]</span>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
@if (entry.Note is { Length: > 0 } note)
|
||
|
|
{
|
||
|
|
<p class="text-xs opacity-80 break-words" data-log-note>@note</p>
|
||
|
|
}
|
||
|
|
</li>
|
||
|
|
}
|
||
|
|
</ol>
|
||
|
|
|
||
|
|
@if (next is not null)
|
||
|
|
{
|
||
|
|
<DButton Size="DSize.Sm" class="self-center" Disabled="IsDefaultDisabled || isLoadingMore" OnClick="More" data-log-more>
|
||
|
|
@if (isLoadingMore)
|
||
|
|
{
|
||
|
|
<DLoading Size="DSize.Sm" LabelText="@Localizer["loading"]" />
|
||
|
|
}
|
||
|
|
<span>@Localizer["Load more"]</span>
|
||
|
|
</DButton>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
@code {
|
||
|
|
const int PageSize = 50;
|
||
|
|
|
||
|
|
[Inject] AdminService AdminService { get; set; }
|
||
|
|
|
||
|
|
List<ViewAdminAction> entries = [];
|
||
|
|
// the next page's `before`; null on the last
|
||
|
|
string next;
|
||
|
|
bool failed;
|
||
|
|
bool isLoadingMore;
|
||
|
|
|
||
|
|
protected override async Task OnInitializedAsync()
|
||
|
|
{
|
||
|
|
if (CascadingState.IsOffline)
|
||
|
|
{
|
||
|
|
WhenOnline(LoadData);
|
||
|
|
StartOnlinePolling();
|
||
|
|
IsLoading = false;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
await LoadData();
|
||
|
|
IsLoading = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task LoadData()
|
||
|
|
{
|
||
|
|
var page = await AdminService.Log(null, PageSize);
|
||
|
|
failed = page is null;
|
||
|
|
entries = page?.Items ?? [];
|
||
|
|
next = page?.Next;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task Reload()
|
||
|
|
{
|
||
|
|
IsLoading = true;
|
||
|
|
await LoadData();
|
||
|
|
IsLoading = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task More()
|
||
|
|
{
|
||
|
|
isLoadingMore = true;
|
||
|
|
var page = await AdminService.Log(next, PageSize);
|
||
|
|
isLoadingMore = false;
|
||
|
|
if (page is null)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(Localizer["The log could not be read."]);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var known = entries.Select(e => e.Id).ToHashSet();
|
||
|
|
entries.AddRange(page.Items.Where(e => known.Add(e.Id)));
|
||
|
|
next = page.Next;
|
||
|
|
}
|
||
|
|
|
||
|
|
string By(ViewAdminAction entry) => entry switch
|
||
|
|
{
|
||
|
|
{ ByYou: true } => Localizer["by you"],
|
||
|
|
{ By: "cli" } => Localizer["by the command line"],
|
||
|
|
{ By: { Length: > 0 } name } => Localizer["by {0}", name],
|
||
|
|
_ => Localizer["by someone not named"]
|
||
|
|
};
|
||
|
|
|
||
|
|
string ActionName(string action) => action switch
|
||
|
|
{
|
||
|
|
"domain_block.insert" => Localizer["Server blocked"],
|
||
|
|
"domain_block.delete" => Localizer["Server unblocked"],
|
||
|
|
"backup.create" => Localizer["Backup made"],
|
||
|
|
"backup.delete" => Localizer["Backup deleted"],
|
||
|
|
"backup.restore" => Localizer["Backup restored"],
|
||
|
|
"settings.update" => Localizer["Settings saved"],
|
||
|
|
"settings.thumbnail" => Localizer["Thumbnail changed"],
|
||
|
|
"rule.insert" => Localizer["Rule added"],
|
||
|
|
"rule.update" => Localizer["Rule changed"],
|
||
|
|
"rule.delete" => Localizer["Rule removed"],
|
||
|
|
"announcement.insert" => Localizer["Announcement written"],
|
||
|
|
"announcement.update" => Localizer["Announcement changed"],
|
||
|
|
"announcement.delete" => Localizer["Announcement deleted"],
|
||
|
|
"relay.insert" => Localizer["Relay added"],
|
||
|
|
"relay.delete" => Localizer["Relay removed"],
|
||
|
|
"report.resolve" => Localizer["Report resolved"],
|
||
|
|
"root.promote" => Localizer["Login made administrator"],
|
||
|
|
"root.demote" => Localizer["Login no longer administrator"],
|
||
|
|
"root.create" => Localizer["Login made"],
|
||
|
|
"root.delete" => Localizer["Login deleted"],
|
||
|
|
"root.reset_totp" => Localizer["Two-factor sign-in reset"],
|
||
|
|
"persona.move" => Localizer["Persona moved to another login"],
|
||
|
|
_ => action
|
||
|
|
};
|
||
|
|
}
|