From f82c62fe9d75c3cdc5ed5fe6f5e5ce517e6eafbc Mon Sep 17 00:00:00 2001 From: Rory& Date: Thu, 17 Apr 2025 11:26:12 +0200 Subject: Room query cleanup, add start of state resync --- LibMatrix | 2 +- MatrixUtils.LibDMSpace/DMSpaceRoom.cs | 6 +- MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor | 3 +- .../RoomQuery/SynapseRoomQueryFilter.razor | 50 +++++ .../RoomQuery/SynapseRoomQueryFilter.razor.css | 35 ++++ .../RoomQuery/SynapseRoomQueryResult.razor | 5 + .../SynapseRoomShutdownWindowContent.razor | 69 ++++--- .../Pages/HSAdmin/Synapse/RoomQuery.razor | 218 ++++++++++----------- .../Pages/HSAdmin/Synapse/RoomQuery.razor.css | 7 + .../Synapse/SubTools/SynapseRoomStateResync.razor | 79 ++++++++ MatrixUtils.Web/Pages/Rooms/Space.razor | 4 +- .../Pages/Tools/Moderation/RoomIntersections.razor | 2 +- .../Pages/Tools/Room/SpaceRestrictedJoins.razor | 2 +- MatrixUtils.Web/Program.cs | 3 + .../Shared/RoomListComponents/RoomListSpace.razor | 2 +- 15 files changed, 322 insertions(+), 165 deletions(-) create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor.css create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryResult.razor create mode 100644 MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor diff --git a/LibMatrix b/LibMatrix index cacabe2..5121c35 160000 --- a/LibMatrix +++ b/LibMatrix @@ -1 +1 @@ -Subproject commit cacabe2b1a15bb7492e23d477ec653513e84d260 +Subproject commit 5121c355fa62a394b1090be67d48f972bcd5d4e4 diff --git a/MatrixUtils.LibDMSpace/DMSpaceRoom.cs b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs index 646a3f3..89ab91b 100644 --- a/MatrixUtils.LibDMSpace/DMSpaceRoom.cs +++ b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs @@ -61,7 +61,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI }).ToAsyncEnumerable(); await foreach (var ((userId, dmRooms), layer) in layerTasks) { - var space = Homeserver.GetRoom(layer.SpaceId).AsSpace; + var space = Homeserver.GetRoom(layer.SpaceId).AsSpace(); foreach (var roomid in dmRooms) { var dri = new DMRoomInfo() { AttributedUser = userId @@ -122,7 +122,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI await foreach (var (layer, profile) in getProfileTasks) { if (profile is null) continue; var layerContent = layer.TypedContent as DMSpaceChildLayer; - var space = Homeserver.GetRoom(layerContent!.SpaceId).AsSpace; + var space = Homeserver.GetRoom(layerContent!.SpaceId).AsSpace(); try { await space.SendStateEventAsync(RoomAvatarEventContent.EventId, "", new RoomAvatarEventContent() { @@ -140,7 +140,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI private async Task UpdateLayer(DMSpaceChildLayer layer, string mxid) { UserProfileResponse? profile = null; - var space = Homeserver.GetRoom(layer.SpaceId).AsSpace; + var space = Homeserver.GetRoom(layer.SpaceId).AsSpace(); if (string.IsNullOrWhiteSpace(layer.OverrideAvatar) || string.IsNullOrWhiteSpace(layer.OverrideName)) { try { diff --git a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor index e1b46e2..03d3ad1 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor @@ -11,7 +11,8 @@ else {

Synapse tools


Query rooms
- Block media + Block media
+ View running background jobs
} else if (Homeserver is AuthenticatedHomeserverHSE) {

Rory&::LibMatrix.HomeserverEmulator tools

diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor new file mode 100644 index 0000000..eb168f4 --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor @@ -0,0 +1,50 @@ +@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters +
+ String contains + Room ID: + Room name: + Canonical alias: + Creator: + Room version: + Encryption algorithm: + Join rules: + Guest access: + History visibility: + + Optional checks + + Is federated: + @if (Filter.CheckFederation) { + + } + + + Is public: + @if (Filter.CheckPublic) { + + } + + + Ranges + + + state events + + + + members + + + local members + +
+ +@code { + + [Parameter] + public required SynapseAdminLocalRoomQueryFilter Filter { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor.css b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor.css new file mode 100644 index 0000000..83ce426 --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor.css @@ -0,0 +1,35 @@ +.int-input { + width: 128px; +} + +.tile { + display: inline-block; + padding: 4px; + border: 1px solid #ffffff22; +} + +.tile280 { + min-width: 280px; +} + +.tile150 { + min-width: 150px; +} + +.range-sep { + display: inline-block; + padding: 4px; + width: 150px; +} + +.range-sep::before { + content: "< "; +} + +.range-sep::after { + content: " <"; +} + +.center-children { + text-align: center; +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryResult.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryResult.razor new file mode 100644 index 0000000..5591072 --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryResult.razor @@ -0,0 +1,5 @@ +

SynapseRoomQueryResult

+ +@code { + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor index d5daf75..124d073 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor @@ -2,35 +2,6 @@ @using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests @if (string.IsNullOrWhiteSpace(Context.DeleteId)) { - Media options -
-
- Quarantine local media: - -
- Quarantine remote media: - -
- Delete remote media: - -
- - User options -
-
- Suspend local users: - -
- Quarantine ALL local user media: - -
- Delete ALL local user media: - -
- - Room deletion options -
-
Block room:
@@ -40,17 +11,43 @@ Force purge room (unsafe):
- Warning room User ID (optional): - -
- @if (!string.IsNullOrWhiteSpace(Context.DeleteRequest.NewRoomUserId)) { +
+ Media + Quarantine local media: + +
+ Quarantine remote media: + +
+ Delete remote media: + +
+ +
+ Local users + Suspend local users: + +
+ Quarantine ALL local user media: + +
+ Delete ALL local user media: + +
+ +
+ Issue warning to local members (optional) + All fields are required if used!
+ Warning room User ID: + +
Warning room name:
Warning room message (plaintext):
- } +
Execute } @@ -107,7 +104,7 @@ public async Task DeleteRoom() { await TaskMap.SetValueAsync(Context.RoomId, Context); } - + private static readonly SemaphoreSlim OnCompleteLock = new(1, 1); - + } \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor index 79e7357..5c29909 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor @@ -2,9 +2,10 @@ @using Microsoft.AspNetCore.WebUtilities @using ArcaneLibs.Extensions @using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters -@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests @using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Responses @using MatrixUtils.Web.Pages.HSAdmin.Synapse.Components +@using MatrixUtils.Web.Pages.HSAdmin.Synapse.Components.RoomQuery +@inject ILogger Logger

Homeserver Administration - Room Query

@@ -21,93 +22,52 @@
Local filtering (slow) - -
- String contains - Room ID: - Room name: - Canonical alias: - Creator: - Room version: - Encryption algorithm: - Join rules: - Guest access: - History visibility: - - Optional checks - - Is federated: - @if (Filter.CheckFederation) { - - } - - - Is public: - @if (Filter.CheckPublic) { - - } - - - Ranges - - state events - - - members - - - local members - -
+

@if (Results.Count > 0) {

Found @Results.Count rooms

-
- TSV data (copy/paste) -
-            
-                @foreach (var res in Results) {
-                    
-                        
-                        
-                        
-                        
-                    
-                }
-            
@res.RoomId@("\t")@res.CanonicalAlias@("\t")@res.Creator@("\t")@res.Name
-
-
+ @*
*@ + @* TSV data (copy/paste) *@ + @*
 *@
+    @*              *@
+    @*                 @foreach (var res in Results) { *@
+    @*                      *@
+    @*                          *@
+    @*                          *@
+    @*                          *@
+    @*                          *@
+    @*                      *@
+    @*                 } *@
+    @*             
@res.RoomId@("\t")@res.CanonicalAlias@("\t")@res.Creator@("\t")@res.Name
*@ + @*
*@ + @*
*@ } @foreach (var res in Results) { -
+
@* *@

@if (!string.IsNullOrWhiteSpace(res.CanonicalAlias)) { - @res.CanonicalAlias - @res.RoomId (@res.Name) -
+ @res.CanonicalAlias - } - else { - @res.RoomId (@res.Name) -
+ @res.RoomId + @if (!string.IsNullOrWhiteSpace(res.Name)) { + (@res.Name) } +
+ @if (!string.IsNullOrWhiteSpace(res.Creator)) { - @* Created by *@ Created by @res.Creator
}

Delete room + Resync state

- @res.StateEvents state events
- @if (res.LocalMembers is null) { - @res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server + + @{ + List flags = []; + if (res.JoinedLocalMembers > 0) { + flags.Add(res.JoinRules switch { + "public" => "Public", + "invite" => "Invite only", + "knock" => "Knock", + "restricted" => "Restricted", + "knock_restricted" => "Knock + restricted", + // TODO: default? + null => null, + "" => null, + _ => "unknown join rule: " + res.JoinRules + }); + if (!string.IsNullOrWhiteSpace(res.Encryption)) flags.Add("encrypted"); + if (!res.Federatable) flags.Add("unfederated"); + + flags.Add(res.HistoryVisibility switch { + "world_readable" => "world readable history", + "shared" => "shared history", + "invited" => "history since invite", + "joined" => "history since join", + // TODO: default? + null => null, + "" => null, + _ => "unknown history setting: " + res.HistoryVisibility + }); + + flags.Add(res.GuestAccess switch { + "can_join" => "guests allowed", + "forbidden" => null, + // TODO: default? + null => null, + "" => null, + _ => "unknown guest access: " + res.GuestAccess, + }); + + flags = flags.Where(x => x != null).ToList(); + } } - else { - @res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server: @(string.Join(", ", res.LocalMembers)) + @string.Join(", ", flags)
+ + @res.StateEvents state events, room version @(res.Version ?? "1")
+ + @{ + var memberSummary = $"{res.JoinedMembers} members, of which {res.JoinedLocalMembers} are on this server"; + if (res.LocalMembers is not null) { + memberSummary += $": {string.Join(", ", res.LocalMembers)}"; + } } + @memberSummary
Full result data
@res.ToJson(ignoreNull: true)
@@ -146,46 +153,14 @@ @* *@ @* } *@ -@foreach(var (roomId, deleteRequest) in DeleteRequests) { - +@foreach (var (roomId, deleteRequest) in DeleteRequests) { + + + } @code { @@ -200,7 +175,7 @@ [Parameter] [SupplyParameterFromQuery(Name = "ascending")] - public bool Ascending { get; set; } + public bool Ascending { get; set; } = true; private List Results { get; set; } = new(); @@ -215,8 +190,6 @@ // private Dictionary DeleteStatuses { get; set; } = new(); protected override Task OnParametersSetAsync() { - if (Ascending == null) - Ascending = true; OrderBy ??= "name"; var execute = false; @@ -300,9 +273,6 @@ while (await searchRooms.MoveNextAsync()) { var room = searchRooms.Current; - if (Results.Count < 100) - Console.WriteLine("Hit: " + room.ToJson(false)); - var roomInfo = new RoomInfo { RoomId = room.RoomId, Name = room.Name, @@ -322,14 +292,24 @@ Results.Add(roomInfo); - if (room.JoinedLocalMembers is > 0 and < 100) - roomInfo.LocalMembers = (await synapse.Admin.GetRoomMembersAsync(room.RoomId)).Members.Where(x => x.EndsWith(":" + synapse.ServerName)).ToList(); - - if (Results.Count < 200 || Results.Count % 1000 == 0) { + if ((Results.Count <= 200 && Results.Count % 10 == 0) || Results.Count % 1000 == 0) { StateHasChanged(); await Task.Yield(); + await Task.Delay(1); } } + + StateHasChanged(); + + var tasks = Results + .Where(x => x.JoinedLocalMembers is > 0 and < 100) + .Select(async r => { + var members = (await synapse.Admin.GetRoomMembersAsync(r.RoomId)).Members.Where(x => x.EndsWith(":" + synapse.ServerName)).ToList(); + r.LocalMembers = members; + } + ); + await Task.WhenAll(tasks); + //Logger.LogWarning($"Found {tasks.Count} rooms with >0, <100 local members"); } StateHasChanged(); @@ -416,5 +396,5 @@ private class RoomInfo : SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom { public List? LocalMembers { get; set; } } - + } diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css index e69de29..62941e5 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor.css @@ -0,0 +1,7 @@ +.room-list-item { + background-color: #ffffff11; + border-radius: 0.5em; + display: block; + margin-top: 4px; + padding: 4px; +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor new file mode 100644 index 0000000..05a4bbc --- /dev/null +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor @@ -0,0 +1,79 @@ +@page "/HSAdmin/Synapse/ResyncState" +@using ArcaneLibs.Extensions +@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests + +

Resync room state with other server

+
+Room ID: +
+Via: +
+Execute + +@if (Executing) { +

Execution in progress. DO NOT CLOSE THIS PAGE!

+ @* stage 1 *@ + @if (Members is null) { +

Loading members...

+ } + else { +

Got @Members.Count local members

+ } + + @* stage 2 *@ + @if (DeleteStatus is not null) { +

Purging room, please wait...

+
@DeleteStatus.ToJson(ignoreNull: true)
+ } + else { +

Purging room...

+
@DeleteStatus!.ToJson(ignoreNull: true)
+ } + + @* stage 3 *@ +} +@if (Done) { +

Execution finished. You may now close the page :)

+} + + +@code { + + [Parameter] + [SupplyParameterFromQuery] + public string? RoomId { get; set; } + + [Parameter] + [SupplyParameterFromQuery(Name = "via")] + public string? Via { get; set; } + + private bool Executing { get; set; } + private bool Done { get; set; } + + private List? Members { get; set; } + + private AuthenticatedHomeserverSynapse? Homeserver { get; set; } + private SynapseAdminRoomDeleteStatus? DeleteStatus { get; set; } + + protected override async Task OnInitializedAsync() { + if (await sessionStore.GetCurrentHomeserver(navigateOnFailure: true) is not AuthenticatedHomeserverSynapse hs) return; + Homeserver = hs; + + StateHasChanged(); + } + + private async Task Execute() { + if (string.IsNullOrWhiteSpace(RoomId)) return; + if (string.IsNullOrWhiteSpace(Via)) return; + Executing = true; + StateHasChanged(); + + Members = (await Homeserver.Admin.GetRoomMembersAsync(RoomId)) + .Members.Where(m => m.EndsWith(Homeserver.ServerName)) + .ToList(); + StateHasChanged(); + + + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/Space.razor b/MatrixUtils.Web/Pages/Rooms/Space.razor index 46e39ed..86a4c13 100644 --- a/MatrixUtils.Web/Pages/Rooms/Space.razor +++ b/MatrixUtils.Web/Pages/Rooms/Space.razor @@ -126,7 +126,7 @@ var ce = await room.GetCreateEventAsync(); if(ce is null) continue; if (ce.Type == "m.space") { - var children = room.AsSpace.GetChildrenAsync(false); + var children = room.AsSpace().GetChildrenAsync(false); await foreach (var child in children) { JoinRecursive(child.RoomId); } @@ -142,7 +142,7 @@ private async Task AddNewRoom() { if (string.IsNullOrWhiteSpace(NewRoomId)) return; - await Room.AsSpace.AddChildByIdAsync(NewRoomId); + await Room.AsSpace().AddChildByIdAsync(NewRoomId); } } diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor b/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor index 736e59a..e407de4 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor @@ -158,7 +158,7 @@ } public async Task AppendSet(string spaceId, List rooms) { - var space = hs.GetRoom(spaceId).AsSpace; + var space = hs.GetRoom(spaceId).AsSpace(); Log.Add($"Found space {spaceId}"); var roomIdsEnum = space.GetChildrenAsync(true); List tasks = new(); diff --git a/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor b/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor index ac3c651..5d5ca20 100644 --- a/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor +++ b/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor @@ -40,7 +40,7 @@ } private async Task Execute() { - var space = hs.GetRoom(RoomId).AsSpace; + var space = hs.GetRoom(RoomId).AsSpace(); await foreach (var room in space.GetChildrenAsync()) { log.Add($"Got room {room.RoomId}"); if (ChangeGuestAccess) { diff --git a/MatrixUtils.Web/Program.cs b/MatrixUtils.Web/Program.cs index 14cf2fc..57fe03d 100644 --- a/MatrixUtils.Web/Program.cs +++ b/MatrixUtils.Web/Program.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Blazored.LocalStorage; using Blazored.SessionStorage; +using LibMatrix.Extensions; using LibMatrix.Services; using MatrixUtils.Web; using MatrixUtils.Web.Classes; @@ -77,6 +78,8 @@ builder.Services.AddScoped(x => ) ); +MatrixHttpClient.LogRequests = false; + builder.Services.AddRoryLibMatrixServices(); builder.Services.AddScoped(); // await builder.Build().RunAsync(); diff --git a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor index 27f0499..471f586 100644 --- a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor +++ b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor @@ -45,7 +45,7 @@ if (Breadcrumbs == null) throw new ArgumentNullException(nameof(Breadcrumbs)); if (Homeserver is null) throw new ArgumentNullException(nameof(Homeserver)); await Task.Delay(Random.Shared.Next(1000, 10000)); - var rooms = Space.Room.AsSpace.GetChildrenAsync(); + var rooms = Space.Room.AsSpace().GetChildrenAsync(); var joinedRooms = await Homeserver.GetJoinedRooms(); await foreach (var room in rooms) { if (Breadcrumbs.Contains(room.RoomId)) continue; -- cgit 1.5.1