diff --git a/LibMatrix b/LibMatrix
-Subproject cacabe2b1a15bb7492e23d477ec653513e84d26
+Subproject 5121c355fa62a394b1090be67d48f972bcd5d4e
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 {
<h4>Synapse tools</h4>
<hr/>
<a href="/HSAdmin/Synapse/RoomQuery">Query rooms</a><br/>
- <a href="/HSAdmin/Synapse/BlockMedia">Block media</a>
+ <a href="/HSAdmin/Synapse/BlockMedia">Block media</a><br/>
+ <a href="/HSAdmin/Synapse/BackgroundJobs">View running background jobs</a><br/>
}
else if (Homeserver is AuthenticatedHomeserverHSE) {
<h4>Rory&::LibMatrix.HomeserverEmulator tools</h4>
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
+<div style="margin-left: 8px; margin-bottom: 8px;">
+ <u style="display: block;">String contains</u>
+ <span class="tile tile280">Room ID: <FancyTextBox @bind-Value="@Filter.RoomIdContains"></FancyTextBox></span>
+ <span class="tile tile280">Room name: <FancyTextBox @bind-Value="@Filter.NameContains"></FancyTextBox></span>
+ <span class="tile tile280">Canonical alias: <FancyTextBox @bind-Value="@Filter.CanonicalAliasContains"></FancyTextBox></span>
+ <span class="tile tile280">Creator: <FancyTextBox @bind-Value="@Filter.CreatorContains"></FancyTextBox></span>
+ <span class="tile tile280">Room version: <FancyTextBox @bind-Value="@Filter.VersionContains"></FancyTextBox></span>
+ <span class="tile tile280">Encryption algorithm: <FancyTextBox @bind-Value="@Filter.EncryptionContains"></FancyTextBox></span>
+ <span class="tile tile280">Join rules: <FancyTextBox @bind-Value="@Filter.JoinRulesContains"></FancyTextBox></span>
+ <span class="tile tile280">Guest access: <FancyTextBox @bind-Value="@Filter.GuestAccessContains"></FancyTextBox></span>
+ <span class="tile tile280">History visibility: <FancyTextBox @bind-Value="@Filter.HistoryVisibilityContains"></FancyTextBox></span>
+
+ <u style="display: block;">Optional checks</u>
+ <span class="tile tile150">
+ <InputCheckbox @bind-Value="@Filter.CheckFederation"></InputCheckbox> Is federated:
+ @if (Filter.CheckFederation) {
+ <InputCheckbox @bind-Value="@Filter.Federatable"></InputCheckbox>
+ }
+ </span>
+ <span class="tile tile150">
+ <InputCheckbox @bind-Value="@Filter.CheckPublic"></InputCheckbox> Is public:
+ @if (Filter.CheckPublic) {
+ <InputCheckbox @bind-Value="@Filter.Public"></InputCheckbox>
+ }
+ </span>
+
+ <u style="display: block;">Ranges</u>
+ <span class="tile center-children">
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsGreaterThan"/>
+ <span class="range-sep">state events</span>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsLessThan"/>
+ </span>
+ <span class="tile center-children">
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersGreaterThan"></InputNumber><span class="range-sep">members</span><InputNumber
+ max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersLessThan"></InputNumber>
+ </span>
+ <span class="tile center-children">
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembersGreaterThan"></InputNumber><span
+ class="range-sep">local members</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int"
+ @bind-Value="@Filter.JoinedLocalMembersLessThan"></InputNumber>
+ </span>
+</div>
+
+@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 @@
+<h3>SynapseRoomQueryResult</h3>
+
+@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)) {
- <b>Media options</b>
- <br/>
- <hr/>
- <span>Quarantine local media: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineLocalMedia"/>
- <br/>
- <span>Quarantine remote media: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineRemoteMedia"/>
- <br/>
- <span>Delete remote media: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.DeleteRemoteMedia"/>
- <br/>
-
- <b>User options</b>
- <br/>
- <hr/>
- <span>Suspend local users: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.SuspendLocalUsers"></InputCheckbox>
- <br/>
- <span>Quarantine <b>ALL</b> local user media: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineLocalUserMedia"></InputCheckbox>
- <br/>
- <span>Delete <b>ALL</b> local user media: </span>
- <InputCheckbox @bind-Value="@Context.ExtraOptions.DeleteLocalUserMedia"></InputCheckbox>
- <br/>
-
- <b>Room deletion options</b>
- <br/>
- <hr/>
<span>Block room: </span>
<InputCheckbox @bind-Value="@Context.DeleteRequest.Block"/>
<br/>
@@ -40,17 +11,43 @@
<span>Force purge room (unsafe): </span>
<InputCheckbox @bind-Value="@Context.DeleteRequest.ForcePurge"></InputCheckbox>
<br/>
- <span>Warning room User ID (optional): </span>
- <FancyTextBox @bind-Value="@Context.DeleteRequest.NewRoomUserId"/>
- <br/>
- @if (!string.IsNullOrWhiteSpace(Context.DeleteRequest.NewRoomUserId)) {
+ <details>
+ <summary>Media</summary>
+ <span>Quarantine local media: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineLocalMedia"/>
+ <br/>
+ <span>Quarantine remote media: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineRemoteMedia"/>
+ <br/>
+ <span>Delete remote media: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.DeleteRemoteMedia"/>
+ </details>
+
+ <details>
+ <summary>Local users</summary>
+ <span>Suspend local users: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.SuspendLocalUsers"></InputCheckbox>
+ <br/>
+ <span>Quarantine <b>ALL</b> local user media: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.QuarantineLocalUserMedia"></InputCheckbox>
+ <br/>
+ <span>Delete <b>ALL</b> local user media: </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.DeleteLocalUserMedia"></InputCheckbox>
+ </details>
+
+ <details>
+ <summary>Issue warning to local members (optional)</summary>
+ <b>All fields are required if used!</b><br/>
+ <span>Warning room User ID: </span>
+ <FancyTextBox @bind-Value="@Context.DeleteRequest.NewRoomUserId"/>
+ <br/>
<span>Warning room name: </span>
<FancyTextBox @bind-Value="@Context.DeleteRequest.RoomName"/>
<br/>
<span>Warning room message (plaintext): </span>
<FancyTextBox Multiline="true" @bind-Value="@Context.DeleteRequest.Message"/>
<br/>
- }
+ </details>
<LinkButton OnClick="@DeleteRoom">Execute</LinkButton>
}
@@ -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<RoomQuery> Logger
<h3>Homeserver Administration - Room Query</h3>
@@ -21,93 +22,52 @@
<details>
<summary>
<span>Local filtering (slow)</span>
-
</summary>
- <div style="margin-left: 8px; margin-bottom: 8px;">
- <u style="display: block;">String contains</u>
- <span class="tile tile280">Room ID: <FancyTextBox @bind-Value="@Filter.RoomIdContains"></FancyTextBox></span>
- <span class="tile tile280">Room name: <FancyTextBox @bind-Value="@Filter.NameContains"></FancyTextBox></span>
- <span class="tile tile280">Canonical alias: <FancyTextBox @bind-Value="@Filter.CanonicalAliasContains"></FancyTextBox></span>
- <span class="tile tile280">Creator: <FancyTextBox @bind-Value="@Filter.CreatorContains"></FancyTextBox></span>
- <span class="tile tile280">Room version: <FancyTextBox @bind-Value="@Filter.VersionContains"></FancyTextBox></span>
- <span class="tile tile280">Encryption algorithm: <FancyTextBox @bind-Value="@Filter.EncryptionContains"></FancyTextBox></span>
- <span class="tile tile280">Join rules: <FancyTextBox @bind-Value="@Filter.JoinRulesContains"></FancyTextBox></span>
- <span class="tile tile280">Guest access: <FancyTextBox @bind-Value="@Filter.GuestAccessContains"></FancyTextBox></span>
- <span class="tile tile280">History visibility: <FancyTextBox @bind-Value="@Filter.HistoryVisibilityContains"></FancyTextBox></span>
-
- <u style="display: block;">Optional checks</u>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckFederation"></InputCheckbox> Is federated:
- @if (Filter.CheckFederation) {
- <InputCheckbox @bind-Value="@Filter.Federatable"></InputCheckbox>
- }
- </span>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckPublic"></InputCheckbox> Is public:
- @if (Filter.CheckPublic) {
- <InputCheckbox @bind-Value="@Filter.Public"></InputCheckbox>
- }
- </span>
-
- <u style="display: block;">Ranges</u>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsGreaterThan"></InputNumber><span class="range-sep">state events</span><InputNumber
- max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsLessThan"></InputNumber>
- </span>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersGreaterThan"></InputNumber><span class="range-sep">members</span><InputNumber
- max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersLessThan"></InputNumber>
- </span>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembersGreaterThan"></InputNumber><span
- class="range-sep">local members</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int"
- @bind-Value="@Filter.JoinedLocalMembersLessThan"></InputNumber>
- </span>
- </div>
+ <SynapseRoomQueryFilter Filter="@Filter"/>
</details>
<button class="btn btn-primary" @onclick="Search">Search</button>
<br/>
@if (Results.Count > 0) {
<p>Found @Results.Count rooms</p>
- <details>
- <summary>TSV data (copy/paste)</summary>
- <pre style="font-size: 0.6em;">
- <table>
- @foreach (var res in Results) {
- <tr>
- <td style="padding: 8px;">@res.RoomId@("\t")</td>
- <td style="padding: 8px;">@res.CanonicalAlias@("\t")</td>
- <td style="padding: 8px;">@res.Creator@("\t")</td>
- <td style="padding: 8px;">@res.Name</td>
- </tr>
- }
- </table>
- </pre>
- </details>
+ @* <details> *@
+ @* <summary>TSV data (copy/paste)</summary> *@
+ @* <pre style="font-size: 0.6em;"> *@
+ @* <table> *@
+ @* @foreach (var res in Results) { *@
+ @* <tr> *@
+ @* <td style="padding: 8px;">@res.RoomId@("\t")</td> *@
+ @* <td style="padding: 8px;">@res.CanonicalAlias@("\t")</td> *@
+ @* <td style="padding: 8px;">@res.Creator@("\t")</td> *@
+ @* <td style="padding: 8px;">@res.Name</td> *@
+ @* </tr> *@
+ @* } *@
+ @* </table> *@
+ @* </pre> *@
+ @* </details> *@
}
@foreach (var res in Results) {
- <div style="background-color: #ffffff11; border-radius: 0.5em; display: block; margin-top: 4px; padding: 4px;">
+ <div class="room-list-item">
@* <RoomListItem RoomName="@res.Name" RoomId="@res.RoomId"></RoomListItem> *@
<p>
@if (!string.IsNullOrWhiteSpace(res.CanonicalAlias)) {
- <span>@res.CanonicalAlias - @res.RoomId (@res.Name)</span>
- <br/>
+ <span>@res.CanonicalAlias - </span>
}
- else {
- <span>@res.RoomId (@res.Name)</span>
- <br/>
+ <span>@res.RoomId</span>
+ @if (!string.IsNullOrWhiteSpace(res.Name)) {
+ <span> (@res.Name)</span>
}
+ <br/>
+
@if (!string.IsNullOrWhiteSpace(res.Creator)) {
- @* <span>Created by <InlineUserItem UserId="@res.Creator"></InlineUserItem></span> *@
<span>Created by @res.Creator</span>
<br/>
}
</p>
<p>
<LinkButton OnClick="@(() => {
- DeleteRequests.Add(res.RoomId, new() {
+ DeleteRequests.TryAdd(res.RoomId, new() {
RoomId = res.RoomId,
DeleteRequest = new() {
Block = true,
@@ -115,18 +75,65 @@
ForcePurge = false
}
});
-
+ StateHasChanged();
+
return Task.CompletedTask;
})">Delete room
</LinkButton>
+ <LinkButton target="_blank" href="@($"/HSAdmin/Synapse/ResyncState?roomId={res.RoomId}&via={res.RoomId.Split(':', 2)[1]}")">Resync state</LinkButton>
</p>
- <span>@res.StateEvents state events</span><br/>
- @if (res.LocalMembers is null) {
- <span>@res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server</span>
+
+ @{
+ List<string?> 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 {
- <span>@res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server: @(string.Join(", ", res.LocalMembers))</span>
+ <span>@string.Join(", ", flags)</span><br/>
+
+ <span>@res.StateEvents state events, room version @(res.Version ?? "1")</span><br/>
+
+ @{
+ var memberSummary = $"{res.JoinedMembers} members, of which {res.JoinedLocalMembers} are on this server";
+ if (res.LocalMembers is not null) {
+ memberSummary += $": {string.Join(", ", res.LocalMembers)}";
+ }
}
+ <span>@memberSummary</span>
<details>
<summary>Full result data</summary>
<pre>@res.ToJson(ignoreNull: true)</pre>
@@ -146,46 +153,14 @@
@* </ModalWindow> *@
@* } *@
-@foreach(var (roomId, deleteRequest) in DeleteRequests) {
- <SynapseRoomShutdownWindowContent Context="deleteRequest" Homeserver="Homeserver"/>
+@foreach (var (roomId, deleteRequest) in DeleteRequests) {
+ <ModalWindow Title="@($"Delete room {roomId}")" OnCloseClicked="@(() => { DeleteRequests.Remove(roomId); StateHasChanged(); })">
+ <SynapseRoomShutdownWindowContent Context="deleteRequest" Homeserver="Homeserver"/>
+ </ModalWindow>
}
<style>
- .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;
- }
</style>
@code {
@@ -200,7 +175,7 @@
[Parameter]
[SupplyParameterFromQuery(Name = "ascending")]
- public bool Ascending { get; set; }
+ public bool Ascending { get; set; } = true;
private List<RoomInfo> Results { get; set; } = new();
@@ -215,8 +190,6 @@
// private Dictionary<string, SynapseAdminRoomDeleteStatus> 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<string>? 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
+
+<h3>Resync room state with other server</h3>
+<hr/>
+<span>Room ID: </span>
+<InputText @bind-Value="@RoomId"></InputText><br/>
+<span>Via: </span>
+<InputText @bind-Value="@Via"></InputText><br/>
+<LinkButton OnClick="@Execute">Execute</LinkButton>
+
+@if (Executing) {
+ <p>Execution in progress. DO NOT CLOSE THIS PAGE!</p>
+ @* stage 1 *@
+ @if (Members is null) {
+ <p>Loading members...</p>
+ }
+ else {
+ <p>Got @Members.Count local members</p>
+ }
+
+ @* stage 2 *@
+ @if (DeleteStatus is not null) {
+ <p>Purging room, please wait...</p>
+ <pre>@DeleteStatus.ToJson(ignoreNull: true)</pre>
+ }
+ else {
+ <p>Purging room...</p>
+ <pre>@DeleteStatus!.ToJson(ignoreNull: true)</pre>
+ }
+
+ @* stage 3 *@
+}
+@if (Done) {
+ <p>Execution finished. You may now close the page :)</p>
+}
+
+
+@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<string?>? 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<GenericRoom> rooms) {
- var space = hs.GetRoom(spaceId).AsSpace;
+ var space = hs.GetRoom(spaceId).AsSpace();
Log.Add($"Found space {spaceId}");
var roomIdsEnum = space.GetChildrenAsync(true);
List<Task> 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<TieredStorageService>(x =>
)
);
+MatrixHttpClient.LogRequests = false;
+
builder.Services.AddRoryLibMatrixServices();
builder.Services.AddScoped<RmuSessionStore>();
// 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;
|