From f5b03846fee280cc40c5dc4a2e2b856d9c1b7c0c Mon Sep 17 00:00:00 2001 From: CakeAndBanana Date: Wed, 17 Sep 2025 02:07:46 +0200 Subject: [PATCH] Added changes for the profiles to be returned and be able to be changed. --- .../Hubs/MareHub.Groups.cs | 121 ++++++++++++------ .../LightlessSyncServer/Utils/Extensions.cs | 2 +- .../LightlessSyncShared/Models/Group.cs | 1 + .../LightlessSyncShared/Models/GroupPair.cs | 1 + .../Models/GroupProfile.cs | 8 +- 5 files changed, 86 insertions(+), 47 deletions(-) diff --git a/LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.Groups.cs b/LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.Groups.cs index f9895ff..f0468dd 100644 --- a/LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.Groups.cs +++ b/LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.Groups.cs @@ -57,7 +57,7 @@ public partial class LightlessHub group.PreferDisableAnimations = dto.Permissions.HasFlag(GroupPermissions.PreferDisableAnimations); group.PreferDisableVFX = dto.Permissions.HasFlag(GroupPermissions.PreferDisableVFX); - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToList(); await Clients.Users(groupPairs).Client_GroupChangePermissions(new GroupPermissionDto(dto.Group, dto.Permissions)).ConfigureAwait(false); @@ -151,15 +151,15 @@ public partial class LightlessHub public async Task GroupCreate() { _logger.LogCallInfo(); - var existingGroupsByUser = await DbContext.Groups.CountAsync(u => u.OwnerUID == UserUID).ConfigureAwait(false); - var existingJoinedGroups = await DbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID).ConfigureAwait(false); + var existingGroupsByUser = await DbContext.Groups.CountAsync(u => u.OwnerUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + var existingJoinedGroups = await DbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser) { throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}."); } var gid = StringUtils.GenerateRandomString(12); - while (await DbContext.Groups.AnyAsync(g => g.GID == "MSS-" + gid).ConfigureAwait(false)) + while (await DbContext.Groups.AnyAsync(g => g.GID == "MSS-" + gid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false)) { gid = StringUtils.GenerateRandomString(12); } @@ -168,8 +168,9 @@ public partial class LightlessHub var passwd = StringUtils.GenerateRandomString(16); using var sha = SHA256.Create(); var hashedPw = StringUtils.Sha256String(passwd); + var currentTime = DateTime.UtcNow; - UserDefaultPreferredPermission defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleAsync(u => u.UserUID == UserUID).ConfigureAwait(false); + UserDefaultPreferredPermission defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleAsync(u => u.UserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); Group newGroup = new() { @@ -179,7 +180,8 @@ public partial class LightlessHub OwnerUID = UserUID, PreferDisableAnimations = defaultPermissions.DisableGroupAnimations, PreferDisableSounds = defaultPermissions.DisableGroupSounds, - PreferDisableVFX = defaultPermissions.DisableGroupVFX + PreferDisableVFX = defaultPermissions.DisableGroupVFX, + CreatedDate = currentTime, }; GroupPair initialPair = new() @@ -187,6 +189,7 @@ public partial class LightlessHub GroupGID = newGroup.GID, GroupUserUID = UserUID, IsPinned = true, + JoinedGroupOn = currentTime, }; GroupPairPreferredPermission initialPrefPermissions = new() @@ -195,18 +198,18 @@ public partial class LightlessHub GroupGID = newGroup.GID, DisableSounds = defaultPermissions.DisableGroupSounds, DisableAnimations = defaultPermissions.DisableGroupAnimations, - DisableVFX = defaultPermissions.DisableGroupAnimations + DisableVFX = defaultPermissions.DisableGroupAnimations, }; - await DbContext.Groups.AddAsync(newGroup).ConfigureAwait(false); - await DbContext.GroupPairs.AddAsync(initialPair).ConfigureAwait(false); - await DbContext.GroupPairPreferredPermissions.AddAsync(initialPrefPermissions).ConfigureAwait(false); - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.Groups.AddAsync(newGroup, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + await DbContext.GroupPairs.AddAsync(initialPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + await DbContext.GroupPairPreferredPermissions.AddAsync(initialPrefPermissions, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); - var self = await DbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); + var self = await DbContext.Users.SingleAsync(u => u.UID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(newGroup.ToGroupData(), self.ToUserData(), - newGroup.ToEnum(), initialPrefPermissions.ToEnum(), initialPair.ToEnum(), new(StringComparer.Ordinal))) + newGroup.ToEnum(), initialPrefPermissions.ToEnum(), initialPair.ToEnum(), initialPair.JoinedGroupOn, new(StringComparer.Ordinal))) .ConfigureAwait(false); _logger.LogCallInfo(LightlessHubLogger.Args(gid)); @@ -262,10 +265,10 @@ public partial class LightlessHub _logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success")); - var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false); + var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); DbContext.RemoveRange(groupPairs); DbContext.Remove(group); - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false); @@ -278,9 +281,9 @@ public partial class LightlessHub _logger.LogCallInfo(LightlessHubLogger.Args(dto)); var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.GID).ConfigureAwait(false); - if (!userHasRights) return new List(); + if (!userHasRights) return []; - var banEntries = await DbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync().ConfigureAwait(false); + var banEntries = await DbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); List bannedGroupUsers = banEntries.Select(b => new BannedGroupUserDto(group.ToGroupData(), b.BannedUser.ToUserData(), b.BannedReason, b.BannedOn, @@ -298,14 +301,14 @@ public partial class LightlessHub _logger.LogCallInfo(LightlessHubLogger.Args(dto)); - var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid).ConfigureAwait(false); + var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); var groupGid = group?.GID ?? string.Empty; - var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID).ConfigureAwait(false); + var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); var hashedPw = StringUtils.Sha256String(dto.Password); - var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid).ConfigureAwait(false); - var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false); - var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID).ConfigureAwait(false); - var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw).ConfigureAwait(false); + var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); if (group == null || (!string.Equals(group.HashedPassword, hashedPw, StringComparison.Ordinal) && oneTimeInvite == null) @@ -357,6 +360,7 @@ public partial class LightlessHub { GroupGID = group.GID, GroupUserUID = UserUID, + JoinedGroupOn = DateTime.UtcNow, }; var preferredPermissions = await DbContext.GroupPairPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID && u.GroupGID == group.GID).ConfigureAwait(false); @@ -369,7 +373,7 @@ public partial class LightlessHub DisableSounds = dto.GroupUserPreferredPermissions.IsDisableSounds(), DisableVFX = dto.GroupUserPreferredPermissions.IsDisableVFX(), DisableAnimations = dto.GroupUserPreferredPermissions.IsDisableAnimations(), - IsPaused = false + IsPaused = false, }; DbContext.Add(newPerms); @@ -384,15 +388,15 @@ public partial class LightlessHub DbContext.Update(preferredPermissions); } - await DbContext.GroupPairs.AddAsync(newPair).ConfigureAwait(false); + await DbContext.GroupPairs.AddAsync(newPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); _logger.LogCallInfo(LightlessHubLogger.Args(aliasOrGid, "Success")); - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); - var groupInfos = await DbContext.GroupPairs.Where(u => u.GroupGID == group.GID && (u.IsPinned || u.IsModerator)).ToListAsync().ConfigureAwait(false); + var groupInfos = await DbContext.GroupPairs.Where(u => u.GroupGID == group.GID && (u.IsPinned || u.IsModerator)).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(group.ToGroupData(), group.Owner.ToUserData(), - group.ToEnum(), preferredPermissions.ToEnum(), newPair.ToEnum(), + group.ToEnum(), preferredPermissions.ToEnum(), newPair.ToEnum(), newPair.JoinedGroupOn, groupInfos.ToDictionary(u => u.GroupUserUID, u => u.ToEnum(), StringComparer.Ordinal))).ConfigureAwait(false); var self = DbContext.Users.Single(u => u.UID == UserUID); @@ -518,7 +522,7 @@ public partial class LightlessHub } } - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); return true; } @@ -541,8 +545,8 @@ public partial class LightlessHub .Where(g => g.GroupGID == dto.Group.GID) .ToListAsync().ConfigureAwait(false); var usersToPrune = allGroupUsers.Where(p => !p.IsPinned && !p.IsModerator - && p.GroupUserUID != UserUID - && p.Group.OwnerUID != p.GroupUserUID + && !string.Equals(p.GroupUserUID, UserUID, StringComparison.Ordinal) + && !string.Equals(p.Group.OwnerUID, p.GroupUserUID, StringComparison.Ordinal) && p.GroupUser.LastLoggedIn.AddDays(days) < DateTime.UtcNow); if (!execute) return usersToPrune.Count(); @@ -600,6 +604,45 @@ public partial class LightlessHub } } + [Authorize(Policy = "Identified")] + public async Task GroupSetProfile(GroupProfileDto dto) + { + _logger.LogCallInfo(LightlessHubLogger.Args(dto)); + + if (dto.Group == null) return; + + var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); + if (!hasRights) return; + + var groupProfileDb = await DbContext.GroupProfiles + .FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID, + _contextAccessor.HttpContext.RequestAborted) + .ConfigureAwait(false); + + if (groupProfileDb != null) + { + groupProfileDb.Description = dto.Description; + groupProfileDb.Tags = dto.Tags; + groupProfileDb.Base64GroupProfileImage = dto.PictureBase64; + } + else + { + var groupProfile = new GroupProfile + { + GroupGID = dto.Group.GID, + Description = dto.Description, + Tags = dto.Tags, + Base64GroupProfileImage = dto.PictureBase64, + }; + + await DbContext.GroupProfiles.AddAsync(groupProfile, + _contextAccessor.HttpContext.RequestAborted) + .ConfigureAwait(false); + } + + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + } + [Authorize(Policy = "Identified")] public async Task GroupSetUserInfo(GroupPairUserInfoDto dto) { @@ -629,9 +672,9 @@ public partial class LightlessHub userPair.IsModerator = false; } - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); - var groupPairs = await DbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync().ConfigureAwait(false); + var groupPairs = await DbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); await Clients.Users(groupPairs).Client_GroupPairChangeUserInfo(new GroupPairUserInfoDto(dto.Group, dto.User, userPair.ToEnum())).ConfigureAwait(false); } @@ -640,15 +683,15 @@ public partial class LightlessHub { _logger.LogCallInfo(); - var groups = await DbContext.GroupPairs.Include(g => g.Group).Include(g => g.Group.Owner).Where(g => g.GroupUserUID == UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); - var preferredPermissions = (await DbContext.GroupPairPreferredPermissions.Where(u => u.UserUID == UserUID).ToListAsync().ConfigureAwait(false)) + var groups = await DbContext.GroupPairs.Include(g => g.Group).Include(g => g.Group.Owner).Where(g => g.GroupUserUID == UserUID).AsNoTracking().ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); + var preferredPermissions = (await DbContext.GroupPairPreferredPermissions.Where(u => u.UserUID == UserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false)) .Where(u => groups.Exists(k => string.Equals(k.GroupGID, u.GroupGID, StringComparison.Ordinal))) .ToDictionary(u => groups.First(f => string.Equals(f.GroupGID, u.GroupGID, StringComparison.Ordinal)), u => u); var groupInfos = await DbContext.GroupPairs.Where(u => groups.Select(g => g.GroupGID).Contains(u.GroupGID) && (u.IsPinned || u.IsModerator)) - .ToListAsync().ConfigureAwait(false); + .ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); return preferredPermissions.Select(g => new GroupFullInfoDto(g.Key.Group.ToGroupData(), g.Key.Group.Owner.ToUserData(), - g.Key.Group.ToEnum(), g.Value.ToEnum(), g.Key.ToEnum(), + g.Key.Group.ToEnum(), g.Value.ToEnum(), g.Key.ToEnum(), g.Key.JoinedGroupOn, groupInfos.Where(i => string.Equals(i.GroupGID, g.Key.GroupGID, StringComparison.Ordinal)) .ToDictionary(i => i.GroupUserUID, i => i.ToEnum(), StringComparer.Ordinal))).ToList(); } @@ -661,11 +704,11 @@ public partial class LightlessHub var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!userHasRights) return; - var banEntry = await DbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID).ConfigureAwait(false); + var banEntry = await DbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); if (banEntry == null) return; DbContext.Remove(banEntry); - await DbContext.SaveChangesAsync().ConfigureAwait(false); + await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false); _logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success")); } diff --git a/LightlessSyncServer/LightlessSyncServer/Utils/Extensions.cs b/LightlessSyncServer/LightlessSyncServer/Utils/Extensions.cs index 070ff9e..59d30ba 100644 --- a/LightlessSyncServer/LightlessSyncServer/Utils/Extensions.cs +++ b/LightlessSyncServer/LightlessSyncServer/Utils/Extensions.cs @@ -10,7 +10,7 @@ public static class Extensions { public static GroupData ToGroupData(this Group group) { - return new GroupData(group.GID, group.Alias); + return new GroupData(group.GID, group.Alias, group.CreatedDate, group.Profile.Description, group.Profile.Tags, group.Profile.Base64GroupProfileImage); } public static UserData ToUserData(this GroupPair pair) diff --git a/LightlessSyncServer/LightlessSyncShared/Models/Group.cs b/LightlessSyncServer/LightlessSyncShared/Models/Group.cs index 2188a5b..4d6e26e 100644 --- a/LightlessSyncServer/LightlessSyncShared/Models/Group.cs +++ b/LightlessSyncServer/LightlessSyncShared/Models/Group.cs @@ -9,6 +9,7 @@ public class Group public string GID { get; set; } public string OwnerUID { get; set; } public User Owner { get; set; } + public GroupProfile? Profile { get; set; } [MaxLength(50)] public string Alias { get; set; } public bool InvitesEnabled { get; set; } diff --git a/LightlessSyncServer/LightlessSyncShared/Models/GroupPair.cs b/LightlessSyncServer/LightlessSyncShared/Models/GroupPair.cs index f04db3f..a0b7de8 100644 --- a/LightlessSyncServer/LightlessSyncShared/Models/GroupPair.cs +++ b/LightlessSyncServer/LightlessSyncShared/Models/GroupPair.cs @@ -4,6 +4,7 @@ public class GroupPair { public string GroupGID { get; set; } public Group Group { get; set; } + public GroupProfile Profile { get; set; } public string GroupUserUID { get; set; } public User GroupUser { get; set; } public bool IsPinned { get; set; } diff --git a/LightlessSyncServer/LightlessSyncShared/Models/GroupProfile.cs b/LightlessSyncServer/LightlessSyncShared/Models/GroupProfile.cs index ea750d8..347f79d 100644 --- a/LightlessSyncServer/LightlessSyncShared/Models/GroupProfile.cs +++ b/LightlessSyncServer/LightlessSyncShared/Models/GroupProfile.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LightlessSyncShared.Models; +namespace LightlessSyncShared.Models; public class GroupProfile { public string GroupGID { get; set; }