Local changes

This commit is contained in:
Emma [it/its]@Rory& committed 2025-10-05 21:34:40 +02:00
1 parent a632666203
commit abb1b570a4
73 files changed
+2067 -64

No files matched your search

@@ -0,0 +1,107 @@
@page "/"
@using System.Net.Http.Headers
@using Spacebar.AdminAPI.TestClient.Services
@inject Config Config
@inject ILocalStorageService LocalStorage
<PageTitle>Home</PageTitle>
<span style="@($"color: {(IsApiUrlValid ? "green" : "red")};")">Spacebar API URL: </span>
<InputText @bind-Value:get="Config.ApiUrl" @bind-Value:set="@(async (v) => {
Config.ApiUrl = v!;
await ValidateAndSaveConfig();
})"/>
<br/>
<!-- <span style="@($"color: {(IsGatewayUrlValid ? "green" : "red")};")">Spacebar Gateway URL: </span> -->
<!-- <InputText @bind-Value="GatewayUrl" /> -->
<!-- <br /> -->
<span style="@($"color: {(IsCdnUrlValid ? "green" : "red")};")">Spacebar CDN URL: </span>
<InputText @bind-Value:get="Config.CdnUrl" @bind-Value:set="@(async (v) => {
Config.CdnUrl = v!;
await ValidateAndSaveConfig();
})"/>
<br/>
<span style="@($"color: {(IsAdminApiUrlValid ? "green" : "red")};")">Spacebar Admin API URL: </span>
<InputText @bind-Value:get="Config.AdminUrl" @bind-Value:set="@(async (v) => {
Config.AdminUrl = v!;
await ValidateAndSaveConfig();
})"/>
<br/>
<span style="@($"color: {(IsAccessTokenValid ? "green" : "red")};")">Access Token: </span>
<InputText @bind-Value:get="Config.AccessToken" @bind-Value:set="@(async (v) => {
Config.AccessToken = v!;
await ValidateAndSaveConfig();
})"/>
<a href="/login">New access token</a>
<br/>
@code {
private bool IsApiUrlValid { get; set; }
// private bool IsGatewayUrlValid { get; set; }
private bool IsCdnUrlValid { get; set; }
private bool IsAdminApiUrlValid { get; set; }
private bool IsAccessTokenValid { get; set; }
protected override async Task OnInitializedAsync() {
await ValidateAndSaveConfig();
}
private async Task ValidateAndSaveConfig() {
await LocalStorage.SetItemAsync("sb_admin_tc_config", Config);
using var hc = new HttpClient();
HttpResponseMessage response;
try {
response = await hc.GetAsync(Config.ApiUrl + "/api/v9/policies/instance/domains");
IsApiUrlValid = response.IsSuccessStatusCode;
}
catch {
IsApiUrlValid = false;
}
StateHasChanged();
// response = await hc.GetAsync(Config.GatewayUrl + "/api/v9/policies/instance");
// IsGatewayUrlValid = response.IsSuccessStatusCode;
// StateHasChanged();
try {
response = await hc.GetAsync(Config.CdnUrl + "/ping");
IsCdnUrlValid = response.IsSuccessStatusCode;
}
catch {
IsCdnUrlValid = false;
}
StateHasChanged();
try {
response = await hc.GetAsync(Config.AdminUrl + "/_spacebar/admin/ping");
IsAdminApiUrlValid = response.IsSuccessStatusCode;
}
catch {
IsAdminApiUrlValid = false;
}
StateHasChanged();
try {
var request = new HttpRequestMessage(HttpMethod.Get, Config.AdminUrl + "/_spacebar/admin/whoami");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken);
response = await hc.SendAsync(request);
IsAccessTokenValid = response.IsSuccessStatusCode;
}
catch {
IsAccessTokenValid = false;
}
StateHasChanged();
}
}
@@ -0,0 +1,57 @@
@page "/Login"
@using System.Text.Json.Nodes
@using Spacebar.AdminAPI.TestClient.Services
@inject ILocalStorageService LocalStorage
@inject Config Config
@inject NavigationManager Navigation
<h3>Login</h3>
<span>Email: </span>
<InputText @bind-Value="Email"/>
<br/>
<span>Password: </span>
<InputText type="password" @bind-Value="Password"/>
<br/>
<button @onclick="DoLogin">Login</button>
<br/>
<pre style="color: red; font-family: 'JetBrains Mono',monospace">@Error</pre>
@code {
private string Email { get; set; }
private string Password { get; set; }
private string Error { get; set; }
private async Task DoLogin() {
HttpResponseMessage response;
using var hc = new HttpClient();
try {
response = await hc.PostAsJsonAsync(Config.ApiUrl + "/api/v9/auth/login", new {
login = Email,
password = Password,
login_source = "Spacebar Admin API Test Client",
undelete = false
});
}
catch (Exception e) {
Error = e.ToString();
return;
}
if (!response.IsSuccessStatusCode) {
Error = await response.Content.ReadAsStringAsync();
return;
}
var content = await response.Content.ReadFromJsonAsync<JsonObject>();
var accessToken = content!["token"].ToString();
Config.AccessToken = accessToken;
await LocalStorage.SetItemAsync("sb_admin_tc_config", Config);
Navigation.NavigateTo("/", true, true);
}
}
@@ -0,0 +1,72 @@
@page "/Users"
@using System.Net.Http.Headers
@using System.Reflection
@using Spacebar.AdminApi.Models
@using Spacebar.AdminAPI.TestClient.Services
@using ArcaneLibs.Blazor.Components
@inject Config Config
@inject ILocalStorageService LocalStorage
<PageTitle>Users</PageTitle>
<details>
<summary>Displayed columns</summary>
@foreach (var column in DisplayedColumns) {
var value = column.Value;
<span>
<InputCheckbox @bind-Value:get="@(value)" @bind-Value:set="@(b => {
DisplayedColumns[column.Key] = b;
StateHasChanged();
})"/>
@column.Key.Name
</span>
<br/>
}
</details>
<table class="table table-bordered">
@{
var columns = DisplayedColumns.Where(kvp => kvp.Value).Select(kvp => kvp.Key).ToList();
}
<thead>
<tr>
@foreach (var column in columns) {
<th>@column.Name</th>
}
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in UserList) {
<tr>
@foreach (var column in columns) {
<td>@column.GetValue(user)</td>
}
<td>
<LinkButton href="@($"/Users/Delete/{user.Id}")" Color="#ff0000">Delete</LinkButton>
</td>
</tr>
}
</tbody>
</table>
@code {
private Dictionary<PropertyInfo, bool> DisplayedColumns { get; set; } = typeof(UserModel).GetProperties()
.ToDictionary(p => p, p => p.Name == "Username" || p.Name == "Id" || p.Name == "MessageCount");
private List<UserModel> UserList { get; set; } = new();
protected override async Task OnInitializedAsync() {
using var hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken);
var response = await hc.GetAsync(Config.AdminUrl + "/_spacebar/admin/users/");
if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync());
var content = response.Content.ReadFromJsonAsAsyncEnumerable<UserModel>();
await foreach (var user in content) {
UserList.Add(user);
StateHasChanged();
}
}
}
@@ -0,0 +1,61 @@
@page "/Users/Delete/{Id}"
@using System.Net.Http.Headers
@using System.Text.Json
@using System.Text.Json.Nodes
@using ArcaneLibs.Extensions
@using Spacebar.AdminApi.Models
@using Spacebar.AdminAPI.TestClient.Services
@inject Config Config
<h3>UsersDelete - @Id</h3>
Deleted @ChannelDeleteProgress.Sum(x=>x.Value.Deleted) messages so far!
@foreach (var (channel, progress) in ChannelDeleteProgress.Where(x=>x.Value.Deleted != x.Value.Total).OrderByDescending(x=>x.Value.Progress)) {
<div>@channel: @progress.Total total, @progress.Deleted deleted</div>
<progress max="@progress.Total" value="@progress.Deleted"></progress>
}
@code {
[Parameter]
public required string Id { get; set; }
private Dictionary<string, DeleteProgress> ChannelDeleteProgress { get; set; } = new();
protected override async Task OnInitializedAsync() {
using var hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken);
var response = await hc.GetAsync(Config.AdminUrl + $"/_spacebar/admin/Users/{Id}/delete?messageDeleteChunkSize=100");
if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync());
var content = response.Content.ReadFromJsonAsAsyncEnumerable<AsyncActionResult>();
await foreach (var actionResult in content) {
Console.WriteLine(actionResult.ToJson(indent: false));
switch (actionResult.MessageType) {
case "STATS": {
var data = JsonSerializer.Deserialize<JsonObject>(actionResult.Data.ToJson());
ChannelDeleteProgress = data!["messages_per_channel"]!
.Deserialize<Dictionary<string, int>>()!
.ToDictionary(x=>x.Key, x=>new DeleteProgress { Total = x.Value });
break;
}
case "BULK_DELETED": {
var data = JsonSerializer.Deserialize<JsonObject>(actionResult.Data.ToJson());
ChannelDeleteProgress[data!["channel_id"]!.ToString()].Deleted += data!["deleted"]!.GetValue<int>();
break;
}
default: {
Console.WriteLine($"Unknown message type: {actionResult.MessageType}");
break;
}
}
StateHasChanged();
await Task.Delay(1);
}
}
private class DeleteProgress {
public int Total { get; set; }
public int Deleted { get; set; } = 0;
public float Progress => (float)Deleted / Total;
}
}