@page "/HSAdmin/Synapse/RoomQuery" @using Microsoft.AspNetCore.WebUtilities @using ArcaneLibs.Extensions @using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters @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




Local filtering (slow)

@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
*@ @*
*@ @*
*@ } @foreach (var res in Results) {
@* *@

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

Delete room Resync state

@{ 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(); } } @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)
} @* *@ @* @if (DeleteRequest.HasValue) { *@ @* *@ @* *@ @* *@ @* } *@ @* @foreach (var (roomId, status) in DeleteStatuses) { *@ @* *@ @*
@status.ToJson()
*@ @*
*@ @* } *@ @foreach (var (roomId, deleteRequest) in DeleteRequests) { } @code { [Parameter] [SupplyParameterFromQuery(Name = "order_by")] public string? OrderBy { get; set; } [Parameter] [SupplyParameterFromQuery(Name = "name_search")] public string SearchTerm { get; set; } [Parameter] [SupplyParameterFromQuery(Name = "ascending")] public bool Ascending { get; set; } = true; private List Results { get; set; } = new(); private AuthenticatedHomeserverSynapse Homeserver { get; set; } = null!; private string Status { get; set; } public SynapseAdminLocalRoomQueryFilter Filter { get; set; } = new(); private Dictionary DeleteRequests { get; set; } = []; // private Dictionary DeleteStatuses { get; set; } = new(); protected override Task OnParametersSetAsync() { OrderBy ??= "name"; var execute = false; foreach (var (key, value) in QueryHelpers.ParseQuery(new Uri(NavigationManager.Uri).Query)) { switch (key) { case "RoomIdContains": Filter.RoomIdContains = value[0]!; break; case "NameContains": Filter.NameContains = value[0]!; break; case "CanonicalAliasContains": Filter.CanonicalAliasContains = value[0]!; break; case "VersionContains": Filter.VersionContains = value[0]!; break; case "CreatorContains": Filter.CreatorContains = value[0]!; break; case "EncryptionContains": Filter.EncryptionContains = value[0]!; break; case "JoinRulesContains": Filter.JoinRulesContains = value[0]!; break; case "GuestAccessContains": Filter.GuestAccessContains = value[0]!; break; case "HistoryVisibilityContains": Filter.HistoryVisibilityContains = value[0]!; break; case "Federatable": Filter.Federatable = bool.Parse(value[0]!); Filter.CheckFederation = true; break; case "Public": Filter.Public = value[0] == "true"; Filter.CheckPublic = true; break; case "JoinedMembersGreaterThan": Filter.JoinedMembersGreaterThan = int.Parse(value[0]!); break; case "JoinedMembersLessThan": Filter.JoinedMembersLessThan = int.Parse(value[0]!); break; case "JoinedLocalMembersGreaterThan": Filter.JoinedLocalMembersGreaterThan = int.Parse(value[0]!); break; case "JoinedLocalMembersLessThan": Filter.JoinedLocalMembersLessThan = int.Parse(value[0]!); break; case "StateEventsGreaterThan": Filter.StateEventsGreaterThan = int.Parse(value[0]!); break; case "StateEventsLessThan": Filter.StateEventsLessThan = int.Parse(value[0]!); break; case "Execute": execute = true; break; default: Console.WriteLine($"Unknown query parameter: {key}"); break; } } if (execute) _ = Search(); return Task.CompletedTask; } private async Task Search() { Results.Clear(); var hs = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); if (hs is AuthenticatedHomeserverSynapse synapse) { Homeserver = synapse; var searchRooms = synapse.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator(); while (await searchRooms.MoveNextAsync()) { var room = searchRooms.Current; var roomInfo = new RoomInfo { RoomId = room.RoomId, Name = room.Name, CanonicalAlias = room.CanonicalAlias, Creator = room.Creator, Version = room.Version, Encryption = room.Encryption, Federatable = room.Federatable, Public = room.Public, JoinRules = room.JoinRules, GuestAccess = room.GuestAccess, HistoryVisibility = room.HistoryVisibility, StateEvents = room.StateEvents, JoinedMembers = room.JoinedMembers, JoinedLocalMembers = room.JoinedLocalMembers }; Results.Add(roomInfo); 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(); } // // private async Task DeleteRoom() { // if (DeleteRequest is { } deleteRequest) { // var media = await Homeserver.Admin.GetRoomMediaAsync(deleteRequest.RoomId); // if (deleteRequest.DeleteRequest.QuarantineRemoteMedia) { // foreach (var remoteMedia in media.Remote) { // await Homeserver.Admin.QuarantineMediaById(remoteMedia); // } // } // // if (deleteRequest.DeleteRequest.DeleteRemoteMedia) { // foreach (var remoteMedia in media.Remote) { // await Homeserver.Admin.DeleteMediaById(remoteMedia); // } // } // else if (deleteRequest.DeleteRequest.QuarantineLocalMedia) { // foreach (var localMedia in media.Local) { // await Homeserver.Admin.QuarantineMediaById(localMedia); // } // } // // var deleteId = await Homeserver.Admin.DeleteRoom(deleteRequest.RoomId, deleteRequest.DeleteRequest, waitForCompletion: false); // DeleteRequest = null; // List alreadyCleanedUsers = []; // while (true) { // var status = await Homeserver.Admin.GetRoomDeleteStatus(deleteId.DeleteId); // DeleteStatuses[deleteRequest.RoomId] = status; // StateHasChanged(); // await Task.Delay(5000); // if (status.Status == "complete") { // DeleteStatuses.Remove(deleteRequest.RoomId); // StateHasChanged(); // break; // } // // if (status.Status == "failed") { // deleteId = await Homeserver.Admin.DeleteRoom(deleteRequest.RoomId, deleteRequest.DeleteRequest, waitForCompletion: false); // } // // var newCleanedUsers = status.ShutdownRoom?.KickedUsers?.Except(alreadyCleanedUsers).ToList(); // if (newCleanedUsers is not null) { // alreadyCleanedUsers.AddRange(newCleanedUsers); // foreach (var user in newCleanedUsers) { // if (deleteRequest.DeleteRequest.SuspendLocalUsers) { // // await Homeserver.Admin.(user); // } // // if (deleteRequest.DeleteRequest.QuarantineLocalUserMedia) { // await Homeserver.Admin.QuarantineMediaByUserId(user); // } // // if (deleteRequest.DeleteRequest.DeleteLocalUserMedia) { // var userMedia = Homeserver.Admin.GetUserMediaEnumerableAsync(user); // await foreach (var mediaEntry in userMedia) { // await Homeserver.Admin.DeleteMediaById(mediaEntry.MediaId); // } // } // } // } // } // } // } private readonly Dictionary validOrderBy = new() { { "name", "Room name" }, { "canonical_alias", "Main alias address" }, { "joined_members", "Number of members (reversed)" }, { "joined_local_members", "Number of local members (reversed)" }, { "version", "Room version" }, { "creator", "Creator of the room" }, { "encryption", "End-to-end encryption algorithm" }, { "federatable", "Is room federated" }, { "public", "Visibility in room list" }, { "join_rules", "Join rules" }, { "guest_access", "Guest access" }, { "history_visibility", "Visibility of history" }, { "state_events", "Number of state events" } }; private class RoomInfo : SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom { public List? LocalMembers { get; set; } } }