about summary refs log tree commit diff
path: root/MatrixUtils.Web/Pages
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-08-22 17:39:57 +0200
committerRory& <root@rory.gay>2025-08-22 17:39:57 +0200
commit93db033377b5e457bd04daf0b16f3d8b8cd77b44 (patch)
tree3a98c0c2704b505b8fc8dca960f2aaa43ef14f70 /MatrixUtils.Web/Pages
parentFurther room cleanup work (diff)
downloadMatrixUtils-93db033377b5e457bd04daf0b16f3d8b8cd77b44.tar.xz
Room upgrade CLI changes, policy list work
Diffstat (limited to 'MatrixUtils.Web/Pages')
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor1
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyList.razor238
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor5
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor21
4 files changed, 200 insertions, 65 deletions
diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor

index 594ff35..e9d0cd2 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor
@@ -3,6 +3,7 @@ @using ArcaneLibs.Extensions @using LibMatrix @using LibMatrix.EventTypes.Spec +@using LibMatrix.StructuredData <h3>Homeserver Administration - Block media</h3> @if (Homeserver is not null) { diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
index cdb5894..412d8c9 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
@@ -6,6 +6,7 @@ @using LibMatrix.RoomTypes @using System.Collections.Frozen @using System.Reflection +@using System.Text.Json @using ArcaneLibs.Attributes @using LibMatrix.EventTypes @using LibMatrix.EventTypes.Interop.Draupnir @@ -136,6 +137,9 @@ else { [Parameter] public required string RoomId { get; set; } + [Parameter, SupplyParameterFromQuery] + public bool RenderEventInfo { get; set; } + private Dictionary<Type, List<StateEventResponse>> PolicyEventsByType { get; set; } = new(); public StateEventResponse? ServerPolicyToMakePermanent { @@ -172,12 +176,13 @@ else { } private async Task LoadStateAsync(bool firstLoad = false) { + // preload workers in task pool + // await Task.WhenAll(Enumerable.Range(0, WebWorkerService.MaxWorkerCount).Select(async _ => (await WebWorkerService.TaskPool.GetWorkerAsync()).WhenReady).ToList()); var sw = Stopwatch.StartNew(); // Loading = true; // var states = Room.GetFullStateAsync(); var states = await Room.GetFullStateAsListAsync(); // PolicyEventsByType.Clear(); - logger.LogInformation($"LoadStatesAsync: Loaded state in {sw.Elapsed}"); foreach (var type in KnownPolicyTypes) { @@ -215,7 +220,8 @@ else { var key = (evt.Type, evt.StateKey!); var policyInfo = new PolicyCollection.PolicyInfo { Policy = evt, - MadeRedundantBy = [] + MadeRedundantBy = [], + DuplicatedBy = [] }; if (evt.RawContent is null or { Count: 0 } || string.IsNullOrWhiteSpace(evt.RawContent?["recommendation"]?.GetValue<string>())) { collection.ActivePolicies.Remove(key); @@ -238,76 +244,187 @@ else { logger.LogInformation($"Policy collection {collection.Key.FullName} has {collection.Value.ActivePolicies.Count} active and {collection.Value.RemovedPolicies.Count} removed policies."); } + Loading = false; + StateHasChanged(); + await Task.Delay(100); + logger.LogInformation("LoadStatesAsync: Scanning for redundant policies..."); - Loading = false; - var allPolicies = PolicyCollections.Values + var scanSw = Stopwatch.StartNew(); + var allPolicyInfos = PolicyCollections.Values .SelectMany(x => x.ActivePolicies.Values) - .Select(x => (x, x.Policy.TypedContent as PolicyRuleEventContent)) .ToList(); - var wildcardPolicies = allPolicies - .Where(x => x.Item2!.IsGlobRule() || x.Item2 is ServerPolicyRuleEventContent) - .ToList(); - Console.WriteLine($"Got {allPolicies.Count} total policies, {wildcardPolicies.Count} wildcard policies."); - int i = 0; - int hits = 0; - int redundant = 0; - int duplicates = 0; - foreach (var policy in allPolicies) { - if (policy.Item2 is null) continue; - var matchingPolicies = wildcardPolicies - .Where(x => - !StateEvent.TypeKeyPairMatches(policy.x.Policy, x.x.Policy) - && x.Item2!.EntityMatches(policy.Item2.Entity!) - ) - .ToList(); - - if (matchingPolicies.Count > 0) { - logger.LogInformation($"{i} Got {matchingPolicies.Count} hits for {policy.x.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.x.Policy.RawContent).ToJson()}"); - foreach (var match in matchingPolicies) { - policy.x.MadeRedundantBy.Add(match.x.Policy); - } - - hits++; - redundant += matchingPolicies.Count; + // var allPolicies = allPolicyInfos + // .Select<PolicyCollection.PolicyInfo, (PolicyCollection.PolicyInfo PolicyInfo, PolicyRuleEventContent TypedContent)>(x => (x, (x.Policy.TypedContent as PolicyRuleEventContent)!)) + // .ToList(); + // var hashPolicies = allPolicies + // .Where(x => x.TypedContent.IsHashedRule()) + // .ToList(); + // var wildcardPolicies = allPolicies + // .Except(hashPolicies) // hashed policies cannot be wildcards + // .Where(x => x.TypedContent.IsGlobRule() || x.TypedContent is ServerPolicyRuleEventContent) + // .ToList(); + // var nonWildcardPolicies = allPolicies + // // .Except(wildcardPolicies) + // .Where(x => !x.TypedContent!.IsGlobRule() || x.TypedContent is ServerPolicyRuleEventContent) + // .ToList(); + // Console.WriteLine($"Got {allPolicies.Count} total policies, {wildcardPolicies.Count} wildcard policies. Time spent: {scanSw.Elapsed}"); + // int i = 0; + // int hits = 0; + // int redundant = 0; + // int duplicates = 0; + + // foreach (var (policyInfo, policyContent) in allPolicies) { + // foreach (var (otherPolicyInfo, otherPolicyContent) in allPolicies) { + // if (policyInfo.Policy == otherPolicyInfo.Policy) continue; // same event + // if (StateEvent.TypeKeyPairMatches(policyInfo.Policy, otherPolicyInfo.Policy)) { + // logger.LogWarning("Sanity check failed: Found same type and state key for two different policies: {Policy1} and {Policy2}", policyInfo.Policy.RawContent.ToJson(), otherPolicyInfo.Policy.RawContent.ToJson()); + // continue; // same type and state key + // } + // // if(!policyContent.IsHashedRule()) + // } + // + // if (++i % 100 == 0) { + // Console.WriteLine($"Processed {i} policies in {scanSw.Elapsed}"); + // await Task.Delay(1); + // } + // } - if (hits % 5 == 0) - StateHasChanged(); + Console.WriteLine($"Scanning for redundant policies in {allPolicyInfos.Count} total policies... ({scanSw.Elapsed})"); + List<Task<List<PolicyCollection.PolicyInfo>>> tasks = []; + // try to save some load... + var policiesJson = JsonSerializer.Serialize(allPolicyInfos.Select(x => x.Policy)); + var ranges = Enumerable.Range(0, allPolicyInfos.Count).DistributeSequentially(WebWorkerService.MaxWorkerCount); + foreach (var range in ranges) + tasks.Add(WebWorkerService.TaskPool.Invoke(CheckDuplicatePoliciesAsync, policiesJson, range.First(), range.Last())); + + // tasks.Add(CheckDuplicatePoliciesAsync(allPolicyInfos, range.First() .. range.Last())); + + await foreach (var modifiedPolicyInfos in tasks.ToAsyncEnumerable()) { + Console.WriteLine($"Main: got {modifiedPolicyInfos.Count} modified policies from worker, time: {scanSw.Elapsed}"); + foreach (var modifiedPolicyInfo in modifiedPolicyInfos) { + var original = allPolicyInfos.First(p => p.Policy.EventId == modifiedPolicyInfo.Policy.EventId); + original.DuplicatedBy = modifiedPolicyInfo.DuplicatedBy; + original.MadeRedundantBy = modifiedPolicyInfo.MadeRedundantBy; } - else logger.LogInformation("Sleeping..."); - await Task.Delay(1); - i++; + + Console.WriteLine($"Processed {modifiedPolicyInfos.Count} modified policies in {scanSw.Elapsed}"); } - i = 0; - foreach (var policy in allPolicies) { - if (policy.Item2 is null) continue; - var matchingPolicies = allPolicies - .Where(x => - !StateEvent.TypeKeyPairMatches(policy.x.Policy, x.x.Policy) - && x.Item2!.Entity == policy.Item2.Entity! - ) - .ToList(); - - if (matchingPolicies.Count > 0) { - logger.LogInformation($"{i} Got {matchingPolicies.Count} duplicates for {policy.x.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.x.Policy.RawContent).ToJson()}"); - foreach (var match in matchingPolicies) { - policy.x.MadeRedundantBy.Add(match.x.Policy); - } + Console.WriteLine($"Processed {allPolicyInfos.Count} policies in {scanSw.Elapsed}"); + + // // scan for wildcard matches + // foreach (var policy in allPolicies) { + // var matchingPolicies = wildcardPolicies + // .Where(x => + // !StateEvent.TypeKeyPairMatches(policy.PolicyInfo.Policy, x.PolicyInfo.Policy) + // && x.Item2.EntityMatches(policy.TypedContent.Entity!) + // ) + // .ToList(); + // + // if (matchingPolicies.Count > 0) { + // logger.LogInformation($"{i} Got {matchingPolicies.Count} hits for {policy.PolicyInfo.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.PolicyInfo.Policy.RawContent).ToJson()}"); + // foreach (var match in matchingPolicies) { + // policy.PolicyInfo.MadeRedundantBy.Add(match.PolicyInfo.Policy); + // } + // + // hits++; + // redundant += matchingPolicies.Count; + // + // if (hits % 5 == 0) + // StateHasChanged(); + // } + // else { + // //logger.LogInformation("Sleeping..."); + // await Task.Delay(1); + // } + // + // i++; + // } + // + // i = 0; + // // scan for exact duplicates + // foreach (var policy in allPolicies) { + // var matchingPolicies = allPolicies + // .Where(x => + // !StateEvent.TypeKeyPairMatches(policy.PolicyInfo.Policy, x.PolicyInfo.Policy) + // && ( + // x.Item2.IsHashedRule() + // ? x.Item2.EntityMatches(policy.Item2.Entity) + // : x.Item2!.Entity == policy.Item2.Entity! + // ) + // ) + // .ToList(); + // + // if (matchingPolicies.Count > 0) { + // logger.LogInformation($"{i} Got {matchingPolicies.Count} duplicates for {policy.PolicyInfo.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.PolicyInfo.Policy.RawContent).ToJson()}"); + // foreach (var match in matchingPolicies) { + // policy.PolicyInfo.MadeRedundantBy.Add(match.PolicyInfo.Policy); + // } + // + // hits++; + // duplicates += matchingPolicies.Count; + // + // if (hits % 5 == 0) + // StateHasChanged(); + // } + // else { + // //logger.LogInformation("Sleeping..."); + // await Task.Delay(1); + // } + // + // i++; + // } + // + // logger.LogInformation($"LoadStatesAsync: Found {hits} ({redundant} redundant, {duplicates} duplicates) redundant policies in {sw.Elapsed}"); + // StateHasChanged(); + } + + [return: WorkerTransfer] + private static async Task<List<PolicyCollection.PolicyInfo>> CheckDuplicatePoliciesAsync(string policiesJson, int start, int end) { + Console.WriteLine($"Got request to check duplicate policies in range {start} to {end} (length: {end - start}), {policiesJson.Length} bytes of JSON"); + return await CheckDuplicatePoliciesAsync(JsonSerializer.Deserialize<List<StateEventResponse>>(policiesJson), start .. end); + } + + [return: WorkerTransfer] + private static Task<List<PolicyCollection.PolicyInfo>> CheckDuplicatePoliciesAsync(List<StateEventResponse> policies, int start, int end) + => CheckDuplicatePoliciesAsync(policies, start .. end); - hits++; - redundant += matchingPolicies.Count; + [return: WorkerTransfer] + private static async Task<List<PolicyCollection.PolicyInfo>> CheckDuplicatePoliciesAsync(List<StateEventResponse> policies, Range range) { + Console.WriteLine($"Processing policies in range {range} ({range.GetOffsetAndLength(policies.Count).Length}) with {policies.Count} total policies"); + var allPolicies = policies + .Select(x => (Event: x, TypedContent: (x.TypedContent as PolicyRuleEventContent)!)) + .ToList(); + var toCheck = allPolicies[range]; + var modifiedPolicies = new List<PolicyCollection.PolicyInfo>(); + + foreach (var (policyEvent, policyContent) in toCheck) { + List<StateEventResponse> duplicatedBy = []; + List<StateEventResponse> madeRedundantBy = []; + + foreach (var (otherPolicyEvent, otherPolicyContent) in allPolicies) { + if (policyEvent == otherPolicyEvent) continue; // same event + if (StateEvent.TypeKeyPairMatches(policyEvent, otherPolicyEvent)) { + // logger.LogWarning("Sanity check failed: Found same type and state key for two different policies: {Policy1} and {Policy2}", policyInfo.Policy.RawContent.ToJson(), otherPolicyInfo.Policy.RawContent.ToJson()); + Console.WriteLine($"Sanity check failed: Found same type and state key for two different policies: {policyEvent.RawContent.ToJson()} and {otherPolicyEvent.RawContent.ToJson()}"); + continue; // same type and state key + } + // if(!policyContent.IsHashedRule()) + } - if (hits % 5 == 0) - StateHasChanged(); + if (duplicatedBy.Count > 0 || madeRedundantBy.Count > 0) { + modifiedPolicies.Add(new() { + Policy = policyEvent, + DuplicatedBy = duplicatedBy, + MadeRedundantBy = madeRedundantBy + }); } - else logger.LogInformation("Sleeping..."); - await Task.Delay(1); - i++; + + // await Task.Delay(1); } - logger.LogInformation($"LoadStatesAsync: Found {hits} ({redundant} redundant, {duplicates} duplicates) redundant policies in {sw.Elapsed}"); - StateHasChanged(); + return modifiedPolicies; } // the old one: @@ -415,9 +532,10 @@ else { public required Dictionary<(string Type, string StateKey), PolicyInfo> RemovedPolicies { get; set; } public required FrozenDictionary<string, PropertyInfo> PropertiesToDisplay { get; set; } - public struct PolicyInfo { + public class PolicyInfo { public required StateEventResponse Policy { get; init; } public required List<StateEventResponse> MadeRedundantBy { get; set; } + public required List<StateEventResponse> DuplicatedBy { get; set; } } } diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor
index f818b62..b52e03f 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor
@@ -18,7 +18,7 @@ </thead> <tbody> @foreach (var policy in PolicyCollection.ActivePolicies.Values.OrderBy(x => x.Policy.RawContent?["entity"]?.GetValue<string>())) { - <PolicyListRowComponent PolicyInfo="@policy" PolicyCollection="@PolicyCollection" Room="@Room"></PolicyListRowComponent> + <PolicyListRowComponent RenderEventInfo="RenderEventInfo" PolicyInfo="@policy" PolicyCollection="@PolicyCollection" Room="@Room"></PolicyListRowComponent> } </tbody> </table> @@ -56,6 +56,9 @@ [Parameter] public required GenericRoom Room { get; set; } + + [Parameter] + public bool RenderEventInfo { get; set; } protected override bool ShouldRender() { // if (PolicyCollection is null) return false; diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor
index 11de82c..9ac5077 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor
@@ -10,9 +10,19 @@ if (prop.Name == "Entity") { <td> <span>@TruncateMxid(TypedContent.Entity)</span> + @foreach (var dup in PolicyInfo.DuplicatedBy) { + <br/> + <span>Duplicated by @dup.FriendlyTypeName.ToLower() <a href="@Anchor(dup.EventId!)">@TruncateMxid(dup.RawContent["entity"]?.GetValue<string>())</a></span> + } @foreach (var dup in PolicyInfo.MadeRedundantBy) { <br/> - <span>Also matched by @dup.FriendlyTypeName.ToLower() <a href="@Anchor(dup.EventId)">@TruncateMxid(dup.RawContent["entity"].GetValue<string>())</a></span> + <span>Also matched by @dup.FriendlyTypeName.ToLower() <a href="@Anchor(dup.EventId!)">@TruncateMxid(dup.RawContent["entity"]?.GetValue<string>())</a></span> + } + @if (RenderEventInfo) { + <br/> + <pre> + @PolicyInfo.Policy.Type/@PolicyInfo.Policy.StateKey by @PolicyInfo.Policy.Sender at @PolicyInfo.Policy.OriginServerTs + </pre> } </td> } @@ -25,9 +35,9 @@ @* @if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, Policy.Type)) { *@ @if (true) { <LinkButton OnClickAsync="@(() => { - IsEditing = true; - return Task.CompletedTask; - })">Edit + IsEditing = true; + return Task.CompletedTask; + })">Edit </LinkButton> <LinkButton OnClickAsync="@RemovePolicyAsync">Remove</LinkButton> @if (Policy.IsLegacyType) { @@ -82,6 +92,9 @@ [Parameter] public required PolicyList.PolicyCollection PolicyCollection { get; set; } + [Parameter] + public bool RenderEventInfo { get; set; } + private StateEventResponse Policy => PolicyInfo.Policy; private bool IsEditing {