Iceshrimp.NET/Iceshrimp.Backend/Pages/Admin/Federation.razor

212 lines
No EOL
6.6 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@page "/admin/federation"
@using Iceshrimp.Backend.Components.Admin
@using Iceshrimp.Backend.Core.Configuration
@using Iceshrimp.Backend.Core.Database.Tables
@using Iceshrimp.Backend.Core.Extensions
@using Iceshrimp.Backend.Core.Middleware
@using Microsoft.EntityFrameworkCore
@using Microsoft.Extensions.Options
@using static Iceshrimp.Backend.Core.Configuration.Enums;
@using Microsoft.AspNetCore.Components.Forms
@inherits AdminComponentBase
<AdminPageHeader Title="@($"{ModeString} instances")"/>
<table>
<thead>
<th>Host</th>
<th>Imported</th>
@if (IsBlocklist)
{
<th>Reason</th>
}
<th>Actions</th>
</thead>
<tbody>
@if (IsBlocklist)
{
@foreach (var instance in _blockedInstances)
{
<tr>
@{
var host = instance.Host;
if (host.StartsWith("xn--"))
host = $"{host.FromPunycode()} ({host})";
}
<td>@host</td>
<td>
<i>@(instance.IsImported ? "Yes" : "No")</i>
</td>
<td>
@if (instance.Reason is not null)
{
@instance.Reason
}
else
{
<i>No reason set</i>
}
</td>
<td>
<a class="fake-link" onclick="unblockInstance('@instance.Host', event.target)">Unblock</a>
</td>
</tr>
}
}
else
{
@foreach (var instance in _allowedInstances)
{
<tr>
@{
var host = instance.Host;
if (host.StartsWith("xn--"))
host = host.FromPunycode() + $"({instance.Host})";
}
<td>@host</td>
<td>
<i>@(instance.IsImported ? "Yes" : "No")</i>
</td>
<td>
<a class="fake-link" onclick="disallowInstance('@instance.Host', event.target)">Remove</a>
</td>
</tr>
}
}
</tbody>
</table>
@if (_blockedInstances is [] && _allowedInstances is [])
{
<p>
<i>No instances listed.</i>
</p>
}
<div class="flex">
@if (Offset is > 0)
{
<button role="link" data-target="/admin/federation?offset=@(Math.Max(0, Offset.Value - 50))"
onclick="navigate(event)"> Previous page
</button>
}
else
{
<button disabled> Previous page</button>
}
@if (_blockedInstances.Length == 50 || _allowedInstances.Length == 50)
{
<button role="link" data-target="/admin/federation?offset=@((Offset ?? 0) + 50)" onclick="navigate(event)">
Next page
</button>
}
else
{
<button disabled>Next page </button>
}
<form method="get" class="inline-flex flex-grow">
<input type="text" id="search" name="q" placeholder="Search..." class="flex-grow" value="@(Query ?? "")"/>
</form>
</div>
<h3>Add @ModeString.ToLowerInvariant() instance</h3>
@if (IsBlocklist)
{
<EditForm FormName="add-host" Model="Model" OnSubmit="@OnSubmit">
<label for="host">Host</label>
<InputText @bind-Value="@Model.Host" id="host" placeholder="example.org" required/>
<label for="reason">Reason</label>
<InputText @bind-Value="@Model.Reason" id="reason" placeholder="Bad vibes"/>
<button type="submit">Submit</button>
</EditForm>
}
else
{
<EditForm FormName="add-host" Model="Model" OnSubmit="@OnSubmit">
<label for="host">Host</label>
<InputText @bind-Value="@Model.Host" id="host" placeholder="example.org" required/>
<button type="submit">Submit</button>
</EditForm>
}
@code {
[Inject] public required IOptionsSnapshot<Config.SecuritySection> Options { get; set; }
[SupplyParameterFromQuery(Name = "q")] public string? Query { get; set; }
[SupplyParameterFromQuery] public int? Offset { get; set; }
[SupplyParameterFromForm] private FederationModel Model { get; set; } = null!;
private bool IsBlocklist => Options.Value.FederationMode == FederationMode.BlockList;
private string ModeString => IsBlocklist ? "Blocked" : "Allowed";
private BlockedInstance[] _blockedInstances = [];
private AllowedInstance[] _allowedInstances = [];
private class FederationModel
{
public string? Host { get; set; }
public string? Reason { get; set; }
public void Canonicalize()
{
Host = Host?.Trim().ToPunycodeLower();
Reason = Reason?.Trim();
if (Host?.Length == 0)
Host = null;
if (Reason?.Length == 0)
Reason = null;
}
}
protected override void OnParametersSet()
{
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
Model ??= new FederationModel();
}
protected override async Task OnGet()
{
if (IsBlocklist)
_blockedInstances = await Database.BlockedInstances.Where(p => p.Host.ToLower().Contains((Query ?? "").ToLower())).OrderBy(p => p.Host).Skip(Offset ?? 0).Take(50).ToArrayAsync();
else
_allowedInstances = await Database.AllowedInstances.Where(p => p.Host.ToLower().Contains((Query ?? "").ToLower())).OrderBy(p => p.Host).Skip(Offset ?? 0).Take(50).ToArrayAsync();
}
private async Task OnSubmit()
{
Model.Canonicalize();
if (Model.Host == null)
throw GracefulException.BadRequest("Missing host field");
if (IsBlocklist)
{
if (await Database.BlockedInstances.FirstOrDefaultAsync(p => p.Host == Model.Host) is { } instance)
{
instance.Reason = Model.Reason;
instance.IsImported = false;
}
else
{
var newInstance = new BlockedInstance
{
Host = Model.Host,
Reason = Model.Reason,
IsImported = false
};
Database.Add(newInstance);
}
}
else
{
if (!await Database.AllowedInstances.AnyAsync(p => p.Host == Model.Host))
{
var newInstance = new AllowedInstance { Host = Model.Host, IsImported = false };
Database.Add(newInstance);
}
}
await Database.SaveChangesAsync();
ReloadPage();
}
}