From c5b72e6f002a637d542068be88d70936150c8818 Mon Sep 17 00:00:00 2001 From: Rory& Date: Sun, 31 Dec 2023 12:00:40 +0100 Subject: Add auth, start of commit script --- .../Controllers/Admin/RoomQueryController.cs | 135 +++++++++++++++++++++ .../Controllers/AppService/PingController.cs | 73 +++++++++++ .../AppService/TransactionsController.cs | 23 ++-- ModAS.Server/Controllers/Debug/DebugController.cs | 120 ++++++++++++++++++ ModAS.Server/Controllers/DebugController.cs | 104 ---------------- ModAS.Server/Controllers/HomeController.cs | 11 +- ModAS.Server/Controllers/RoomQueryController.cs | 135 --------------------- 7 files changed, 345 insertions(+), 256 deletions(-) create mode 100644 ModAS.Server/Controllers/Admin/RoomQueryController.cs create mode 100644 ModAS.Server/Controllers/AppService/PingController.cs create mode 100644 ModAS.Server/Controllers/Debug/DebugController.cs delete mode 100644 ModAS.Server/Controllers/DebugController.cs delete mode 100644 ModAS.Server/Controllers/RoomQueryController.cs (limited to 'ModAS.Server/Controllers') diff --git a/ModAS.Server/Controllers/Admin/RoomQueryController.cs b/ModAS.Server/Controllers/Admin/RoomQueryController.cs new file mode 100644 index 0000000..a49e5c0 --- /dev/null +++ b/ModAS.Server/Controllers/Admin/RoomQueryController.cs @@ -0,0 +1,135 @@ +using System.Collections.Frozen; +using ArcaneLibs.Extensions; +using Elastic.Apm; +using Elastic.Apm.Api; +using LibMatrix; +using LibMatrix.EventTypes.Spec.State; +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.Responses.ModAS; +using LibMatrix.RoomTypes; +using Microsoft.AspNetCore.Mvc; +using ModAS.Classes; +using ModAS.Server.Services; +using MxApiExtensions.Services; + +namespace ModAS.Server.Controllers; + +[ApiController] +public class RoomQueryController(UserProviderService ahsProvider, ModASConfiguration config, RoomStateCacheService stateCacheService, UserProviderService userProviderService) : ControllerBase { + [HttpGet("/_matrix/_modas/room_query")] + public async IAsyncEnumerable RoomQuery() { + var fetchRoomQueryDataSpan = currentTransaction.StartSpan("fetchRoomQueryData", ApiConstants.TypeApp); + List processedRooms = new(); + var getUsersSpan = currentTransaction.StartSpan("getUsers", ApiConstants.TypeApp); + var validUsers = await ahsProvider.GetValidUsers(); + getUsersSpan.End(); + + var collectRoomsSpan = currentTransaction.StartSpan("collectRooms", ApiConstants.TypeApp); + // var userRoomLists = validUsers.Values.Select(ahs => ahs.GetJoinedRooms()).ToList(); + // await Task.WhenAll(userRoomLists); + // var rooms = userRoomLists.SelectMany(r => r.Result).DistinctBy(x => x.RoomId).ToFrozenSet(); + var roomsTasks = validUsers.Values.Select(u => userProviderService.GetUserRoomsCached(u.WhoAmI.UserId)).ToList(); + await Task.WhenAll(roomsTasks); + var rooms = roomsTasks.SelectMany(r => r.Result).DistinctBy(x => x.RoomId).ToFrozenSet(); + collectRoomsSpan.End(); + + var collectRoomStateSpan = currentTransaction.StartSpan("collectRoomState", ApiConstants.TypeApp); + //make sure we lock!!!! + await stateCacheService.EnsureCachedFromRoomList(rooms); + var roomStateTasks = rooms.Select(GetRoomState).ToAsyncEnumerable(); + var awaitRoomStateSpan = currentTransaction.StartSpan("fetchRoomState", ApiConstants.TypeApp); + await foreach (var (room, roomState) in roomStateTasks) { + awaitRoomStateSpan.Name = $"awaitRoomState {room.RoomId}"; + awaitRoomStateSpan.End(); + + var filterStateEventsSpan = currentTransaction.StartSpan($"filterStateEvents {room.RoomId}", ApiConstants.TypeApp); + var roomMembers = roomState.Where(r => r.Type == RoomMemberEventContent.EventId).ToFrozenSet(); + var localRoomMembers = roomMembers.Where(x => x.StateKey.EndsWith(':' + config.ServerName)).ToFrozenSet(); + var nonMemberState = roomState.Where(x => !roomMembers.Contains(x)).ToFrozenSet(); + filterStateEventsSpan.End(); + var buildResultSpan = currentTransaction.StartSpan($"buildResult {room.RoomId}", ApiConstants.TypeApp); + + // forgive me for raw json access... attempt at optimisation -emma + yield return new ModASRoomQueryResult { + RoomId = room.RoomId, + StateEvents = roomState.Count, + //members + TotalMembers = roomMembers.Count, + TotalLocalMembers = localRoomMembers.Count, + JoinedMembers = roomMembers.Count(x => (x.TypedContent as RoomMemberEventContent)?.Membership == "join"), + JoinedLocalMembers = localRoomMembers.Count(x => (x.TypedContent as RoomMemberEventContent)?.Membership == "join"), + //-members + //creation event + Creator = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.Sender, + Version = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["room_version"]?.GetValue(), + Type = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["type"]?.GetValue(), + Federatable = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["m.federate"]?.GetValue() ?? true, + //-creation event + Name = nonMemberState.FirstOrDefault(r => r.Type == RoomNameEventContent.EventId)?.RawContent?["name"]?.GetValue(), + CanonicalAlias = nonMemberState.FirstOrDefault(r => r.Type == RoomCanonicalAliasEventContent.EventId)?.RawContent?["alias"]?.GetValue(), + JoinRules = nonMemberState.FirstOrDefault(r => r.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.GetValue(), + GuestAccess = nonMemberState.FirstOrDefault(r => r.Type == RoomGuestAccessEventContent.EventId)?.RawContent?["guest_access"]?.GetValue(), + HistoryVisibility = + nonMemberState.FirstOrDefault(r => r.Type == RoomHistoryVisibilityEventContent.EventId)?.RawContent?["history_visibility"]?.GetValue(), + Public = nonMemberState.FirstOrDefault(r => r.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.GetValue() == "public", + Encryption = nonMemberState.FirstOrDefault(r => r.Type == RoomEncryptionEventContent.EventId)?.RawContent?["algorithm"]?.GetValue(), + AvatarUrl = nonMemberState.FirstOrDefault(r => r.Type == RoomAvatarEventContent.EventId)?.RawContent?["url"]?.GetValue(), + RoomTopic = nonMemberState.FirstOrDefault(r => r.Type == RoomTopicEventContent.EventId)?.RawContent?["topic"]?.GetValue() + }; + buildResultSpan.End(); + awaitRoomStateSpan = currentTransaction.StartSpan("fetchRoomState", ApiConstants.TypeApp); + } + + collectRoomStateSpan.End(); + fetchRoomQueryDataSpan.End(); + } + + [HttpPost("/_matrix/_modas/room_query")] + public async IAsyncEnumerable RoomQuery([FromBody] RoomQueryFilter request) { + await foreach (var room in RoomQuery()) { + if (!string.IsNullOrWhiteSpace(request.RoomIdContains) && !room.RoomId.Contains(request.RoomIdContains)) continue; + + if (!string.IsNullOrWhiteSpace(request.NameContains) && room.Name?.Contains(request.NameContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.CanonicalAliasContains) && room.CanonicalAlias?.Contains(request.CanonicalAliasContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.VersionContains) && room.Version?.Contains(request.VersionContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.CreatorContains) && room.Creator?.Contains(request.CreatorContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.EncryptionContains) && room.Encryption?.Contains(request.EncryptionContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.JoinRulesContains) && room.JoinRules?.Contains(request.JoinRulesContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.GuestAccessContains) && room.GuestAccess?.Contains(request.GuestAccessContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.HistoryVisibilityContains) && room.HistoryVisibility?.Contains(request.HistoryVisibilityContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.AvatarUrlContains) && room.AvatarUrl?.Contains(request.AvatarUrlContains) != true) continue; + + if (!string.IsNullOrWhiteSpace(request.RoomTopicContains) && room.RoomTopic?.Contains(request.RoomTopicContains) != true) continue; + + if (request.JoinedMembersMin.HasValue && room.JoinedMembers < request.JoinedMembersMin.Value) continue; + if (request.JoinedMembersMax.HasValue && room.JoinedMembers > request.JoinedMembersMax.Value) continue; + + if (request.JoinedLocalMembersMin.HasValue && room.JoinedLocalMembers < request.JoinedLocalMembersMin.Value) continue; + if (request.JoinedLocalMembersMax.HasValue && room.JoinedLocalMembers > request.JoinedLocalMembersMax.Value) continue; + + if (request.StateEventsMin.HasValue && room.StateEvents < request.StateEventsMin.Value) continue; + if (request.StateEventsMax.HasValue && room.StateEvents > request.StateEventsMax.Value) continue; + + if (request.IsFederatable.HasValue && room.Federatable != request.IsFederatable.Value) continue; + + if (request.IsPublic.HasValue && room.Public != request.IsPublic.Value) continue; + + yield return room; + } + } + + private async Task>> GetRoomState(GenericRoom room) { + var roomState = await stateCacheService.GetRoomState(room.RoomId, room); + return new(room, roomState); + } + + private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction; +} \ No newline at end of file diff --git a/ModAS.Server/Controllers/AppService/PingController.cs b/ModAS.Server/Controllers/AppService/PingController.cs new file mode 100644 index 0000000..7b073c1 --- /dev/null +++ b/ModAS.Server/Controllers/AppService/PingController.cs @@ -0,0 +1,73 @@ +using System.IO.Pipelines; +using System.Net; +using System.Net.Http.Headers; +using System.Text.Json; +using ArcaneLibs; +using LibMatrix; +using LibMatrix.EventTypes.Spec; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration.Json; +using ModAS.Server; +using ModAS.Server.Attributes; +using ModAS.Server.Services; +using MxApiExtensions.Services; + +namespace ModAS.Server.Controllers.AppService; + +[ApiController] +public class PingController( + AppServiceRegistration asr, + ModASConfiguration config, + UserProviderService userProvider, + RoomContextService roomContextService, + RoomStateCacheService stateCacheService) : ControllerBase { + private static List _ignoredInvalidationEvents { get; set; } = [ + RoomMessageEventContent.EventId, + RoomMessageReactionEventContent.EventId + ]; + + [HttpPut("/_matrix/app/v1/transactions/{txnId}")] + [UserAuth(AuthType = AuthType.Server)] + public async Task PutTransactions(string txnId) { + var data = await JsonSerializer.DeserializeAsync(Request.Body); + Console.WriteLine( + $"PutTransaction: {txnId}: {data.Events.Count} events, {Util.BytesToString(Request.Headers.ContentLength ?? Request.ContentLength ?? Request.Body.Length)}"); + + if (!Directory.Exists("data")) + Directory.CreateDirectory("data"); + Directory.CreateDirectory($"data/{txnId}"); + // var pipe = PipeReader.Create(Request.Body); + // await using var file = System.IO.File.OpenWrite($"data/{txnId}"); + // await pipe.CopyToAsync(file); + // await pipe.CompleteAsync(); + // + // Console.WriteLine($"PutTransaction: {txnId}: {Util.BytesToString(file.Length)}"); + for (var i = 0; i < data.Events.Count; i++) { + var evt = data.Events[i]; + Console.WriteLine($"PutTransaction: {txnId}/{i}: {evt.Type} {evt.StateKey} {evt.Sender}"); + await System.IO.File.WriteAllTextAsync($"data/{txnId}/{i}-{evt.Type.Replace("/", "")}-{evt.StateKey.Replace("/", "")}-{evt.Sender?.Replace("/", "")}.json", + JsonSerializer.Serialize(evt)); + + if (evt.Sender.EndsWith(':' + config.ServerName)) { + Console.WriteLine("PutTransaction: sender is local user, updating data..."); + try { + var user = await userProvider.GetImpersonatedHomeserver(evt.Sender); + var rooms = await user.GetJoinedRooms(); + foreach (var room in rooms) { + await roomContextService.GetRoomContext(room); + } + } + catch (Exception e) { + Console.WriteLine($"PutTransaction: failed to update data: {e}"); + } + } + else + Console.WriteLine("PutTransaction: sender is remote user"); + + if (!string.IsNullOrWhiteSpace(evt.RoomId) && !_ignoredInvalidationEvents.Contains(evt.Type)) + await stateCacheService.InvalidateRoomState(evt.RoomId); + } + + return Ok(new { }); + } +} \ No newline at end of file diff --git a/ModAS.Server/Controllers/AppService/TransactionsController.cs b/ModAS.Server/Controllers/AppService/TransactionsController.cs index b74e1e1..53bfaf5 100644 --- a/ModAS.Server/Controllers/AppService/TransactionsController.cs +++ b/ModAS.Server/Controllers/AppService/TransactionsController.cs @@ -8,6 +8,7 @@ using LibMatrix.EventTypes.Spec; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration.Json; using ModAS.Server; +using ModAS.Server.Attributes; using ModAS.Server.Services; using MxApiExtensions.Services; @@ -26,16 +27,17 @@ public class TransactionsController( ]; [HttpPut("/_matrix/app/v1/transactions/{txnId}")] + [UserAuth(AuthType = AuthType.Server)] public async Task PutTransactions(string txnId) { - if (!Request.Headers.ContainsKey("Authorization")) { - Console.WriteLine("PutTransaction: missing authorization header"); - return Unauthorized(); - } - - if (Request.GetTypedHeaders().Get("Authorization")?.Parameter != asr.HomeserverToken) { - Console.WriteLine($"PutTransaction: invalid authorization header: {Request.Headers["Authorization"]}"); - return Unauthorized(); - } + // if (!Request.Headers.ContainsKey("Authorization")) { + // Console.WriteLine("PutTransaction: missing authorization header"); + // return Unauthorized(); + // } + // + // if (Request.GetTypedHeaders().Get("Authorization")?.Parameter != asr.HomeserverToken) { + // Console.WriteLine($"PutTransaction: invalid authorization header: {Request.Headers["Authorization"]}"); + // return Unauthorized(); + // } var data = await JsonSerializer.DeserializeAsync(Request.Body); Console.WriteLine( @@ -53,7 +55,8 @@ public class TransactionsController( for (var i = 0; i < data.Events.Count; i++) { var evt = data.Events[i]; Console.WriteLine($"PutTransaction: {txnId}/{i}: {evt.Type} {evt.StateKey} {evt.Sender}"); - await System.IO.File.WriteAllTextAsync($"data/{txnId}/{i}-{evt.Type}-{evt.StateKey}-{evt.Sender}.json", JsonSerializer.Serialize(evt)); + await System.IO.File.WriteAllTextAsync($"data/{txnId}/{i}-{evt.Type.Replace("/", "")}-{evt.StateKey.Replace("/", "")}-{evt.Sender?.Replace("/", "")}.json", + JsonSerializer.Serialize(evt)); if (evt.Sender.EndsWith(':' + config.ServerName)) { Console.WriteLine("PutTransaction: sender is local user, updating data..."); diff --git a/ModAS.Server/Controllers/Debug/DebugController.cs b/ModAS.Server/Controllers/Debug/DebugController.cs new file mode 100644 index 0000000..7bec3e5 --- /dev/null +++ b/ModAS.Server/Controllers/Debug/DebugController.cs @@ -0,0 +1,120 @@ +using System.Collections.Frozen; +using ArcaneLibs.Extensions; +using Elastic.Apm; +using Elastic.Apm.Api; +using LibMatrix.Homeservers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using ModAS.Server.Attributes; +using ModAS.Server.Services; +using MxApiExtensions.Services; + +namespace ModAS.Server.Controllers.Debug; + +/// +/// Provides debugging endpoints. +/// +/// +/// +/// +[ApiController] +[UserAuth(AnyRoles = AuthRoles.Developer | AuthRoles.Administrator)] +public class DebugController(ModASConfiguration config, UserProviderService authHsProvider, RoomContextService roomContextService) : ControllerBase { + /// + /// Returns a JSON object containing the request and response headers. + /// + /// JSON object with request and partial response headers. + [HttpGet("/_matrix/_modas/debug")] + public IActionResult Index() { + return Ok(new { + Request = Request.Headers, + Response = Response.Headers + }); + } + + /// + /// Returns a JSON object containing the configuration. + /// + /// + [HttpGet("/_matrix/_modas/debug/config")] + public IActionResult Config() { + return Ok(config); + } + + [HttpGet("/_matrix/_modas/debug/known_users")] + public IActionResult KnownUsers() { + return Ok(authHsProvider.KnownUsers.Keys); + } + + [HttpGet("/_matrix/_modas/debug/test_locate_users")] + public async IAsyncEnumerable TestLocateUsers([FromQuery] string startUser) { + List foundUsers = (await authHsProvider.GetValidUsers()).Select(x => x.Value).ToList(); + if (!foundUsers.Any(x => x.WhoAmI.UserId == startUser)) { + foundUsers.Add(await authHsProvider.GetImpersonatedHomeserver(startUser)); + } + + List processedRooms = [], processedUsers = []; + var foundNew = true; + while (foundNew) { + var span1 = currentTransaction.StartSpan("iterateUsers", ApiConstants.TypeApp); + foundNew = false; + var usersToProcess = foundUsers.Where(x => !processedUsers.Any(y => x.WhoAmI.UserId == y)).ToFrozenSet(); + Console.WriteLine($"Got {usersToProcess.Count} users: {string.Join(", ", usersToProcess)}"); + + var rooms = usersToProcess.Select(async x => await x.GetJoinedRooms()); + var roomLists = rooms.ToAsyncEnumerable(); + await foreach (var roomList in roomLists) { + if (roomList is null) continue; + foreach (var room in roomList) { + if (processedRooms.Contains(room.RoomId)) continue; + processedRooms.Add(room.RoomId); + var roomMembers = await room.GetMembersListAsync(false); + foreach (var roomMember in roomMembers) { + if (roomMember.StateKey.EndsWith(':' + config.ServerName) && !foundUsers.Any(x => x.WhoAmI.UserId == roomMember.StateKey)) { + foundUsers.Add(await authHsProvider.GetImpersonatedHomeserver(roomMember.StateKey)); + foundNew = true; + yield return roomMember.StateKey; + } + } + } + } + + // await foreach (var task in tasks) { + // if (task is null) continue; + // foreach (var user in task) { + // if (foundUsers.Contains(user)) continue; + // foundUsers.Add(user); + // foundNew = true; + // yield return user; + // } + // } + + span1.End(); + } + } + + [HttpGet("/_matrix/_modas/debug/room_contexts")] + public IActionResult RoomContexts() { + return Ok(roomContextService.RoomContexts.Values); + } + + [HttpGet("/_matrix/_modas/debug/room_contexts/{roomId}")] + public async Task RoomContext(string roomId) { + var roomContext = await roomContextService.GetRoomContext(roomId); + if (roomContext is null) return NotFound("Room not found"); + return Ok(roomContext); + } + + [HttpGet("/_matrix/_modas/debug/room_contexts/by_user/{userId}")] + public async IAsyncEnumerable RoomContextByUser(string userId) { + var user = await authHsProvider.GetImpersonatedHomeserver(userId); + var rooms = await user.GetJoinedRooms(); + var contexts = rooms.Select(x => roomContextService.GetRoomContext(x.RoomId)).ToAsyncEnumerable(); + await foreach (var context in contexts) { + if (context is null) continue; + yield return context; + } + } + + private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction; +} \ No newline at end of file diff --git a/ModAS.Server/Controllers/DebugController.cs b/ModAS.Server/Controllers/DebugController.cs deleted file mode 100644 index f0fe91e..0000000 --- a/ModAS.Server/Controllers/DebugController.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Frozen; -using ArcaneLibs.Extensions; -using Elastic.Apm; -using Elastic.Apm.Api; -using LibMatrix; -using LibMatrix.Homeservers; -using Microsoft.AspNetCore.Mvc; -using ModAS.Server.Services; -using MxApiExtensions.Services; - -namespace ModAS.Server.Controllers; - -[ApiController] -public class DebugController(ModASConfiguration config, UserProviderService authHsProvider, RoomContextService roomContextService) : ControllerBase { - [HttpGet("/_matrix/_modas/debug")] - public IActionResult Index() { - return Ok(new { - Request = Request.Headers, - Response = Response.Headers - }); - } - - [HttpGet("/_matrix/_modas/debug/config")] - public IActionResult Config() { - return Ok(config); - } - - [HttpGet("/_matrix/_modas/debug/known_users")] - public IActionResult KnownUsers() { - return Ok(authHsProvider.KnownUsers.Keys); - } - - [HttpGet("/_matrix/_modas/debug/test_locate_users")] - public async IAsyncEnumerable TestLocateUsers([FromQuery] string startUser) { - List foundUsers = (await authHsProvider.GetValidUsers()).Select(x=>x.Value).ToList(); - if(!foundUsers.Any(x=>x.WhoAmI.UserId == startUser)) { - foundUsers.Add(await authHsProvider.GetImpersonatedHomeserver(startUser)); - } - - List processedRooms = [], processedUsers = []; - var foundNew = true; - while (foundNew) { - var span1 = currentTransaction.StartSpan("iterateUsers", ApiConstants.TypeApp); - foundNew = false; - var usersToProcess = foundUsers.Where(x => !processedUsers.Any(y=>x.WhoAmI.UserId == y)).ToFrozenSet(); - Console.WriteLine($"Got {usersToProcess.Count} users: {string.Join(", ", usersToProcess)}"); - - var rooms = usersToProcess.Select(async x => await x.GetJoinedRooms()); - var roomLists = rooms.ToAsyncEnumerable(); - await foreach (var roomList in roomLists) { - if (roomList is null) continue; - foreach (var room in roomList) { - if (processedRooms.Contains(room.RoomId)) continue; - processedRooms.Add(room.RoomId); - var roomMembers = await room.GetMembersListAsync(false); - foreach (var roomMember in roomMembers) { - if (roomMember.StateKey.EndsWith(':' + config.ServerName) && !foundUsers.Any(x=>x.WhoAmI.UserId == roomMember.StateKey)) { - foundUsers.Add(await authHsProvider.GetImpersonatedHomeserver(roomMember.StateKey)); - foundNew = true; - yield return roomMember.StateKey; - } - } - } - } - - // await foreach (var task in tasks) { - // if (task is null) continue; - // foreach (var user in task) { - // if (foundUsers.Contains(user)) continue; - // foundUsers.Add(user); - // foundNew = true; - // yield return user; - // } - // } - - span1.End(); - } - } - - [HttpGet("/_matrix/_modas/debug/room_contexts")] - public IActionResult RoomContexts() { - return Ok(roomContextService.RoomContexts.Values); - } - - [HttpGet("/_matrix/_modas/debug/room_contexts/{roomId}")] - public async Task RoomContext(string roomId) { - var roomContext = await roomContextService.GetRoomContext(roomId); - if (roomContext is null) return NotFound("Room not found"); - return Ok(roomContext); - } - - [HttpGet("/_matrix/_modas/debug/room_contexts/by_user/{userId}")] - public async IAsyncEnumerable RoomContextByUser(string userId) { - var user = await authHsProvider.GetImpersonatedHomeserver(userId); - var rooms = await user.GetJoinedRooms(); - var contexts = rooms.Select(x => roomContextService.GetRoomContext(x.RoomId)).ToAsyncEnumerable(); - await foreach (var context in contexts) { - if (context is null) continue; - yield return context; - } - } - - private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction; -} \ No newline at end of file diff --git a/ModAS.Server/Controllers/HomeController.cs b/ModAS.Server/Controllers/HomeController.cs index 5fd309f..eb17966 100644 --- a/ModAS.Server/Controllers/HomeController.cs +++ b/ModAS.Server/Controllers/HomeController.cs @@ -3,17 +3,14 @@ using Microsoft.AspNetCore.Mvc; namespace ModAS.Server.Controllers; +/// +/// Manages the visual homepage. +/// [ApiController] public class HomeController : Controller { - private readonly ILogger _logger; - - public HomeController(ILogger logger) { - _logger = logger; - } - + /// [HttpGet("/_matrix/_modas")] public IActionResult Index() { - //return wwwroot/index.html return LocalRedirect("/index.html"); } } \ No newline at end of file diff --git a/ModAS.Server/Controllers/RoomQueryController.cs b/ModAS.Server/Controllers/RoomQueryController.cs deleted file mode 100644 index a49e5c0..0000000 --- a/ModAS.Server/Controllers/RoomQueryController.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System.Collections.Frozen; -using ArcaneLibs.Extensions; -using Elastic.Apm; -using Elastic.Apm.Api; -using LibMatrix; -using LibMatrix.EventTypes.Spec.State; -using LibMatrix.EventTypes.Spec.State.RoomInfo; -using LibMatrix.Responses.ModAS; -using LibMatrix.RoomTypes; -using Microsoft.AspNetCore.Mvc; -using ModAS.Classes; -using ModAS.Server.Services; -using MxApiExtensions.Services; - -namespace ModAS.Server.Controllers; - -[ApiController] -public class RoomQueryController(UserProviderService ahsProvider, ModASConfiguration config, RoomStateCacheService stateCacheService, UserProviderService userProviderService) : ControllerBase { - [HttpGet("/_matrix/_modas/room_query")] - public async IAsyncEnumerable RoomQuery() { - var fetchRoomQueryDataSpan = currentTransaction.StartSpan("fetchRoomQueryData", ApiConstants.TypeApp); - List processedRooms = new(); - var getUsersSpan = currentTransaction.StartSpan("getUsers", ApiConstants.TypeApp); - var validUsers = await ahsProvider.GetValidUsers(); - getUsersSpan.End(); - - var collectRoomsSpan = currentTransaction.StartSpan("collectRooms", ApiConstants.TypeApp); - // var userRoomLists = validUsers.Values.Select(ahs => ahs.GetJoinedRooms()).ToList(); - // await Task.WhenAll(userRoomLists); - // var rooms = userRoomLists.SelectMany(r => r.Result).DistinctBy(x => x.RoomId).ToFrozenSet(); - var roomsTasks = validUsers.Values.Select(u => userProviderService.GetUserRoomsCached(u.WhoAmI.UserId)).ToList(); - await Task.WhenAll(roomsTasks); - var rooms = roomsTasks.SelectMany(r => r.Result).DistinctBy(x => x.RoomId).ToFrozenSet(); - collectRoomsSpan.End(); - - var collectRoomStateSpan = currentTransaction.StartSpan("collectRoomState", ApiConstants.TypeApp); - //make sure we lock!!!! - await stateCacheService.EnsureCachedFromRoomList(rooms); - var roomStateTasks = rooms.Select(GetRoomState).ToAsyncEnumerable(); - var awaitRoomStateSpan = currentTransaction.StartSpan("fetchRoomState", ApiConstants.TypeApp); - await foreach (var (room, roomState) in roomStateTasks) { - awaitRoomStateSpan.Name = $"awaitRoomState {room.RoomId}"; - awaitRoomStateSpan.End(); - - var filterStateEventsSpan = currentTransaction.StartSpan($"filterStateEvents {room.RoomId}", ApiConstants.TypeApp); - var roomMembers = roomState.Where(r => r.Type == RoomMemberEventContent.EventId).ToFrozenSet(); - var localRoomMembers = roomMembers.Where(x => x.StateKey.EndsWith(':' + config.ServerName)).ToFrozenSet(); - var nonMemberState = roomState.Where(x => !roomMembers.Contains(x)).ToFrozenSet(); - filterStateEventsSpan.End(); - var buildResultSpan = currentTransaction.StartSpan($"buildResult {room.RoomId}", ApiConstants.TypeApp); - - // forgive me for raw json access... attempt at optimisation -emma - yield return new ModASRoomQueryResult { - RoomId = room.RoomId, - StateEvents = roomState.Count, - //members - TotalMembers = roomMembers.Count, - TotalLocalMembers = localRoomMembers.Count, - JoinedMembers = roomMembers.Count(x => (x.TypedContent as RoomMemberEventContent)?.Membership == "join"), - JoinedLocalMembers = localRoomMembers.Count(x => (x.TypedContent as RoomMemberEventContent)?.Membership == "join"), - //-members - //creation event - Creator = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.Sender, - Version = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["room_version"]?.GetValue(), - Type = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["type"]?.GetValue(), - Federatable = nonMemberState.FirstOrDefault(r => r.Type == RoomCreateEventContent.EventId)?.RawContent?["m.federate"]?.GetValue() ?? true, - //-creation event - Name = nonMemberState.FirstOrDefault(r => r.Type == RoomNameEventContent.EventId)?.RawContent?["name"]?.GetValue(), - CanonicalAlias = nonMemberState.FirstOrDefault(r => r.Type == RoomCanonicalAliasEventContent.EventId)?.RawContent?["alias"]?.GetValue(), - JoinRules = nonMemberState.FirstOrDefault(r => r.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.GetValue(), - GuestAccess = nonMemberState.FirstOrDefault(r => r.Type == RoomGuestAccessEventContent.EventId)?.RawContent?["guest_access"]?.GetValue(), - HistoryVisibility = - nonMemberState.FirstOrDefault(r => r.Type == RoomHistoryVisibilityEventContent.EventId)?.RawContent?["history_visibility"]?.GetValue(), - Public = nonMemberState.FirstOrDefault(r => r.Type == RoomJoinRulesEventContent.EventId)?.RawContent?["join_rule"]?.GetValue() == "public", - Encryption = nonMemberState.FirstOrDefault(r => r.Type == RoomEncryptionEventContent.EventId)?.RawContent?["algorithm"]?.GetValue(), - AvatarUrl = nonMemberState.FirstOrDefault(r => r.Type == RoomAvatarEventContent.EventId)?.RawContent?["url"]?.GetValue(), - RoomTopic = nonMemberState.FirstOrDefault(r => r.Type == RoomTopicEventContent.EventId)?.RawContent?["topic"]?.GetValue() - }; - buildResultSpan.End(); - awaitRoomStateSpan = currentTransaction.StartSpan("fetchRoomState", ApiConstants.TypeApp); - } - - collectRoomStateSpan.End(); - fetchRoomQueryDataSpan.End(); - } - - [HttpPost("/_matrix/_modas/room_query")] - public async IAsyncEnumerable RoomQuery([FromBody] RoomQueryFilter request) { - await foreach (var room in RoomQuery()) { - if (!string.IsNullOrWhiteSpace(request.RoomIdContains) && !room.RoomId.Contains(request.RoomIdContains)) continue; - - if (!string.IsNullOrWhiteSpace(request.NameContains) && room.Name?.Contains(request.NameContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.CanonicalAliasContains) && room.CanonicalAlias?.Contains(request.CanonicalAliasContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.VersionContains) && room.Version?.Contains(request.VersionContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.CreatorContains) && room.Creator?.Contains(request.CreatorContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.EncryptionContains) && room.Encryption?.Contains(request.EncryptionContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.JoinRulesContains) && room.JoinRules?.Contains(request.JoinRulesContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.GuestAccessContains) && room.GuestAccess?.Contains(request.GuestAccessContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.HistoryVisibilityContains) && room.HistoryVisibility?.Contains(request.HistoryVisibilityContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.AvatarUrlContains) && room.AvatarUrl?.Contains(request.AvatarUrlContains) != true) continue; - - if (!string.IsNullOrWhiteSpace(request.RoomTopicContains) && room.RoomTopic?.Contains(request.RoomTopicContains) != true) continue; - - if (request.JoinedMembersMin.HasValue && room.JoinedMembers < request.JoinedMembersMin.Value) continue; - if (request.JoinedMembersMax.HasValue && room.JoinedMembers > request.JoinedMembersMax.Value) continue; - - if (request.JoinedLocalMembersMin.HasValue && room.JoinedLocalMembers < request.JoinedLocalMembersMin.Value) continue; - if (request.JoinedLocalMembersMax.HasValue && room.JoinedLocalMembers > request.JoinedLocalMembersMax.Value) continue; - - if (request.StateEventsMin.HasValue && room.StateEvents < request.StateEventsMin.Value) continue; - if (request.StateEventsMax.HasValue && room.StateEvents > request.StateEventsMax.Value) continue; - - if (request.IsFederatable.HasValue && room.Federatable != request.IsFederatable.Value) continue; - - if (request.IsPublic.HasValue && room.Public != request.IsPublic.Value) continue; - - yield return room; - } - } - - private async Task>> GetRoomState(GenericRoom room) { - var roomState = await stateCacheService.GetRoomState(room.RoomId, room); - return new(room, roomState); - } - - private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction; -} \ No newline at end of file -- cgit 1.5.1