From ca16b08a2f60cde2eaeadd63925776a1972500f3 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 29 Jan 2023 15:15:02 +0100 Subject: [PATCH] Server rework for API change and paradigm shift (#23) * start with group permissions implementation * some minor refactoring * adjust hub for api changes * fixes and database migration * fixes sending online/offline message to clients stuff * remove admin stuff from server * fixes get server permissions * send group pair info to client on joining a group * send online only on sending connection dto --------- Co-authored-by: rootdarkarchon --- Docker/run/compose/mare-standalone.yml | 15 +- MareAPI | 2 +- .../Controllers/JwtController.cs | 2 +- .../Hubs/MareHub.Admin.cs | 150 ----- .../Hubs/MareHub.ClientStubs.cs | 87 +-- .../Hubs/MareHub.Files.cs | 3 +- .../Hubs/MareHub.Functions.cs | 33 +- .../Hubs/MareHub.Groups.cs | 527 +++++++---------- .../MareSynchronosServer/Hubs/MareHub.User.cs | 202 +++---- .../MareSynchronosServer/Hubs/MareHub.cs | 13 +- .../Services/ClientMessageService.cs | 3 +- .../Services/SystemInfoService.cs | 3 +- .../MareSynchronosServer/Startup.cs | 2 +- .../MareSynchronosServer/Utils/Extensions.cs | 51 ++ .../20230126163758_GroupPerms.Designer.cs | 530 ++++++++++++++++++ .../Migrations/20230126163758_GroupPerms.cs | 62 ++ .../Migrations/MareDbContextModelSnapshot.cs | 16 + .../MareSynchronosShared/Models/Group.cs | 2 + .../MareSynchronosShared/Models/GroupPair.cs | 2 + .../Controllers/CacheController.cs | 2 +- .../Controllers/RequestController.cs | 2 +- .../Controllers/ServerFilesController.cs | 2 +- .../Services/CachedFileProvider.cs | 2 +- .../Services/RequestQueueService.cs | 3 +- 24 files changed, 1027 insertions(+), 689 deletions(-) delete mode 100644 MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs create mode 100644 MareSynchronosServer/MareSynchronosServer/Utils/Extensions.cs create mode 100644 MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.Designer.cs create mode 100644 MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.cs diff --git a/Docker/run/compose/mare-standalone.yml b/Docker/run/compose/mare-standalone.yml index eaf02a6..ef86e6c 100644 --- a/Docker/run/compose/mare-standalone.yml +++ b/Docker/run/compose/mare-standalone.yml @@ -35,6 +35,11 @@ services: depends_on: postgres: condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl --fail http://localhost:6000/health || exit 1"] + retries: 60 + start_period: 10s + timeout: 1s mare-services: image: darkarchon/mare-synchronos-services:latest @@ -48,7 +53,8 @@ services: depends_on: postgres: condition: service_healthy - - "mare-server" + mare-server: + condition: service_healthy mare-files: image: darkarchon/mare-synchronos-staticfilesserver:latest @@ -63,7 +69,10 @@ services: depends_on: postgres: condition: service_healthy - - "mare-server" + mare-server: + condition: service_healthy volumes: - postgres_socket: \ No newline at end of file + postgres_socket: + cache: + driver: local \ No newline at end of file diff --git a/MareAPI b/MareAPI index 2015496..981f62a 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit 2015496ec0bbda566a0ac7e88ce56d7714fd8c49 +Subproject commit 981f62a07198c5f4dfb881381290ce5cad836d7b diff --git a/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs b/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs index a00b5a5..2810617 100644 --- a/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs +++ b/MareSynchronosServer/MareSynchronosServer/Controllers/JwtController.cs @@ -1,4 +1,4 @@ -using MareSynchronos.API; +using MareSynchronos.API.Routes; using MareSynchronosServer.Authentication; using MareSynchronosShared; using MareSynchronosShared.Data; diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs deleted file mode 100644 index 782dd85..0000000 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Admin.cs +++ /dev/null @@ -1,150 +0,0 @@ -using MareSynchronos.API; -using MareSynchronosShared.Models; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.SignalR; -using Microsoft.EntityFrameworkCore; - -namespace MareSynchronosServer.Hubs; - -public partial class MareHub -{ - // TODO: remove all of this and migrate it to the discord bot eventually - private List OnlineAdmins => _dbContext.Users.Where(u => (u.IsModerator || u.IsAdmin)).Select(u => u.UID).ToList(); - - [Authorize(Policy = "Admin")] - public async Task AdminChangeModeratorStatus(string uid, bool isModerator) - { - var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid).ConfigureAwait(false); - - if (user == null) return; - - user.IsModerator = isModerator; - _dbContext.Update(user); - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(user.UID).Client_AdminForcedReconnect().ConfigureAwait(false); - } - - [Authorize(Policy = "Moderator")] - public async Task AdminDeleteBannedUser(BannedUserDto dto) - { - if (string.IsNullOrEmpty(dto.CharacterHash)) return; - - var existingUser = - await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); - if (existingUser == null) - { - return; - } - - _dbContext.Remove(existingUser); - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(OnlineAdmins).Client_AdminDeleteBannedUser(dto).ConfigureAwait(false); - } - - [Authorize(Policy = "Admin")] - public async Task AdminDeleteForbiddenFile(ForbiddenFileDto dto) - { - if (string.IsNullOrEmpty(dto.Hash)) return; - - var existingFile = - await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash).ConfigureAwait(false); - if (existingFile == null) - { - return; - } - - _dbContext.Remove(existingFile); - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(OnlineAdmins).Client_AdminDeleteForbiddenFile(dto).ConfigureAwait(false); - } - - [Authorize(Policy = "Moderator")] - public async Task> AdminGetBannedUsers() - { - return await _dbContext.BannedUsers.AsNoTracking().Select(b => new BannedUserDto() - { - CharacterHash = b.CharacterIdentification, - Reason = b.Reason, - }).ToListAsync().ConfigureAwait(false); - } - - [Authorize(Policy = "Moderator")] - public async Task> AdminGetForbiddenFiles() - { - return await _dbContext.ForbiddenUploadEntries.AsNoTracking().Select(b => new ForbiddenFileDto() - { - Hash = b.Hash, - ForbiddenBy = b.ForbiddenBy, - }).ToListAsync().ConfigureAwait(false); - } - - [Authorize(Policy = "Moderator")] - public async Task> AdminGetOnlineUsers() - { - var users = await _dbContext.Users.AsNoTracking().ToListAsync().ConfigureAwait(false); - var redisUsers = await GetIdentFromUidsFromRedis(users.Select(u => u.UID)).ConfigureAwait(false); - return users.Select(user => new { User = user, Ident = redisUsers[user.UID] }).Where(a => !string.IsNullOrEmpty(a.Ident)).Select(b => new OnlineUserDto - { - CharacterNameHash = b.Ident, - UID = b.User.UID, - IsModerator = b.User.IsModerator, - IsAdmin = b.User.IsAdmin, - }).ToList(); - } - - [Authorize(Policy = "Moderator")] - public async Task AdminUpdateOrAddBannedUser(BannedUserDto dto) - { - if (string.IsNullOrEmpty(dto.CharacterHash)) return; - - var existingUser = - await _dbContext.BannedUsers.SingleOrDefaultAsync(b => b.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); - if (existingUser != null) - { - existingUser.Reason = dto.Reason; - _dbContext.Update(existingUser); - } - else - { - await _dbContext.BannedUsers.AddAsync(new Banned - { - CharacterIdentification = dto.CharacterHash, - Reason = dto.Reason, - }).ConfigureAwait(false); - } - - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(OnlineAdmins).Client_AdminUpdateOrAddBannedUser(dto).ConfigureAwait(false); - //var bannedUser = _clientIdentService.GetUidForCharacterIdent(dto.CharacterHash); - //if (!string.IsNullOrEmpty(bannedUser)) - //{ - // await Clients.User(bannedUser).Client_AdminForcedReconnect().ConfigureAwait(false); - //} - } - - [Authorize(Policy = "Admin")] - public async Task AdminUpdateOrAddForbiddenFile(ForbiddenFileDto dto) - { - if (string.IsNullOrEmpty(dto.Hash)) return; - - var existingForbiddenFile = - await _dbContext.ForbiddenUploadEntries.SingleOrDefaultAsync(b => b.Hash == dto.Hash).ConfigureAwait(false); - if (existingForbiddenFile != null) - { - existingForbiddenFile.ForbiddenBy = dto.ForbiddenBy; - _dbContext.Update(existingForbiddenFile); - } - else - { - await _dbContext.ForbiddenUploadEntries.AddAsync(new ForbiddenUploadEntry - { - Hash = dto.Hash, - ForbiddenBy = dto.ForbiddenBy, - }).ConfigureAwait(false); - } - - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - - await Clients.Users(OnlineAdmins).Client_AdminUpdateOrAddForbiddenFile(dto).ConfigureAwait(false); - } -} diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.ClientStubs.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.ClientStubs.cs index 684fe49..418331f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.ClientStubs.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.ClientStubs.cs @@ -1,72 +1,29 @@ -using MareSynchronos.API; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Dto; +using MareSynchronos.API.Dto.Group; +using MareSynchronos.API.Dto.User; namespace MareSynchronosServer.Hubs { public partial class MareHub { - public Task Client_UserUpdateClientPairs(ClientPairDto clientPairDto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_UpdateSystemInfo(SystemInfoDto systemInfo) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_UserReceiveCharacterData(CharacterCacheDto clientPairDto, string characterIdent) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_UserChangePairedPlayer(string characterIdent, bool isOnline) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_GroupChange(GroupDto groupDto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_GroupUserChange(GroupPairDto groupPairDto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_AdminForcedReconnect() - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_AdminDeleteBannedUser(BannedUserDto dto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_AdminDeleteForbiddenFile(ForbiddenFileDto dto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_AdminUpdateOrAddBannedUser(BannedUserDto dto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_AdminUpdateOrAddForbiddenFile(ForbiddenFileDto dto) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } - - public Task Client_DownloadReady(Guid requestId) - { - throw new PlatformNotSupportedException("Calling clientside method on server not supported"); - } + public Task Client_UpdateSystemInfo(SystemInfoDto systemInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_DownloadReady(Guid requestId) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupDelete(GroupDto groupDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupPairLeft(GroupPairDto groupPairDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupChangePermissions(GroupPermissionDto groupPermission) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupPairChangePermissions(GroupPairUserPermissionDto permissionDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupSendInfo(GroupInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserSendOffline(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserAddClientPair(UserPairDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserRemoveClientPair(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserSendOnline(OnlineUserIdentDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); + public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported"); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs index 0312388..e86f25e 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Files.cs @@ -2,13 +2,12 @@ using System.Text.RegularExpressions; using Google.Protobuf; using Grpc.Core; -using MareSynchronos.API; +using MareSynchronos.API.Dto.Files; using MareSynchronosServer.Utils; using MareSynchronosShared.Models; using MareSynchronosShared.Protos; using MareSynchronosShared.Utils; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; namespace MareSynchronosServer.Hubs; diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs index 3a6658d..8989341 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using MareSynchronosServer.Utils; using MareSynchronosShared.Utils; +using Microsoft.IdentityModel.Tokens; namespace MareSynchronosServer.Hubs; @@ -10,29 +11,23 @@ public partial class MareHub private async Task UpdateUserOnRedis() { await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); - await _redis.AddAsync("IDENT:" + UserCharaIdent, UserUID, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); } private async Task RemoveUserFromRedis() { await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); - await _redis.RemoveAsync("IDENT:" + UserCharaIdent, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false); } - public async Task GetIdentFromUidFromRedis(string uid) + private async Task GetUserIdent(string uid) { + if (uid.IsNullOrEmpty()) return string.Empty; return await _redis.GetAsync("UID:" + uid).ConfigureAwait(false); } - public async Task> GetIdentFromUidsFromRedis(IEnumerable uids) + private async Task> GetOnlineUsers(List uids) { var result = await _redis.GetAllAsync(uids.Select(u => "UID:" + u).ToArray()).ConfigureAwait(false); - return result.ToDictionary(k => k.Key.Replace("UID:", "", StringComparison.Ordinal), k => k.Value, StringComparer.Ordinal); - } - - public async Task GetUidFromIdentFromRedis(string ident) - { - return await _redis.GetAsync("IDENT:" + ident).ConfigureAwait(false); + return uids.Where(u => result.TryGetValue("UID:" + u, out var ident) && !string.IsNullOrEmpty(ident)).ToDictionary(u => u, u => result["UID:" + u], StringComparer.Ordinal); } private async Task> GetAllPairedClientsWithPauseState(string? uid = null) @@ -80,18 +75,20 @@ public partial class MareHub return ret.Where(k => !k.IsPaused).Select(k => k.UID).ToList(); } - private async Task> SendOnlineToAllPairedUsers(string arg) + private async Task> SendOnlineToAllPairedUsers() { var usersToSendDataTo = await GetAllPairedUnpausedUsers().ConfigureAwait(false); - await Clients.Users(usersToSendDataTo).Client_UserChangePairedPlayer(arg, true).ConfigureAwait(false); + var self = await _dbContext.Users.AsNoTracking().SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); + await Clients.Users(usersToSendDataTo).Client_UserSendOnline(new(self.ToUserData(), UserCharaIdent)).ConfigureAwait(false); return usersToSendDataTo; } - private async Task> SendOfflineToAllPairedUsers(string arg) + private async Task> SendOfflineToAllPairedUsers() { var usersToSendDataTo = await GetAllPairedUnpausedUsers().ConfigureAwait(false); - await Clients.Users(usersToSendDataTo).Client_UserChangePairedPlayer(arg, false).ConfigureAwait(false); + var self = await _dbContext.Users.AsNoTracking().SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); + await Clients.Users(usersToSendDataTo).Client_UserSendOffline(new(self.ToUserData())).ConfigureAwait(false); return usersToSendDataTo; } @@ -109,11 +106,11 @@ public partial class MareHub if (userPair.IsPausedPerGroup is PauseInfo.Unpaused) return; } - var groupUserIdent = await GetIdentFromUidFromRedis(groupUserPair.GroupUserUID).ConfigureAwait(false); + var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false); if (!string.IsNullOrEmpty(groupUserIdent)) { - await Clients.User(uid).Client_UserChangePairedPlayer(groupUserIdent, false).ConfigureAwait(false); - await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(userIdent, false).ConfigureAwait(false); + await Clients.User(uid).Client_UserSendOffline(new(new(groupUserPair.GroupUserUID))).ConfigureAwait(false); + await Clients.User(groupUserPair.GroupUserUID).Client_UserSendOffline(new(new(uid))).ConfigureAwait(false); } } @@ -121,7 +118,7 @@ public partial class MareHub { foreach (var pair in groupUsers) { - var pairIdent = await GetIdentFromUidFromRedis(pair.GroupUserUID).ConfigureAwait(false); + var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false); if (string.IsNullOrEmpty(pairIdent)) continue; var pairs = await GetAllPairedClientsWithPauseState(pair.GroupUserUID).ConfigureAwait(false); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs index cf935d7..b40e98f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Groups.cs @@ -1,4 +1,6 @@ -using MareSynchronos.API; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.API.Dto.Group; using MareSynchronosServer.Utils; using MareSynchronosShared.Models; using MareSynchronosShared.Utils; @@ -12,7 +14,7 @@ namespace MareSynchronosServer.Hubs; public partial class MareHub { [Authorize(Policy = "Identified")] - public async Task GroupCreate() + public async Task GroupCreate() { _logger.LogCallInfo(); var existingGroupsByUser = await _dbContext.Groups.CountAsync(u => u.OwnerUID == UserUID).ConfigureAwait(false); @@ -55,118 +57,137 @@ public partial class MareHub var self = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); - await Clients.User(UserUID).Client_GroupChange(new GroupDto() - { - GID = newGroup.GID, - OwnedBy = string.IsNullOrEmpty(self.Alias) ? self.UID : self.Alias, - IsDeleted = false, - IsPaused = false, - InvitesEnabled = true, - }).ConfigureAwait(false); + await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(newGroup.ToGroupData(), self.ToUserData(), GroupPermissions.NoneSet, GroupUserPermissions.NoneSet, GroupUserInfo.None)) + .ConfigureAwait(false); _logger.LogCallInfo(MareHubLogger.Args(gid)); - return new GroupCreatedDto() - { - GID = newGroup.GID, - Password = passwd, - }; + return new GroupPasswordDto(newGroup.ToGroupData(), passwd); } [Authorize(Policy = "Identified")] - public async Task> GroupsGetAll() + public async Task> GroupsGetAll() { _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); - return groups.Select(g => new GroupDto() - { - GID = g.GroupGID, - Alias = g.Group.Alias, - InvitesEnabled = g.Group.InvitesEnabled, - OwnedBy = string.IsNullOrEmpty(g.Group.Owner.Alias) ? g.Group.Owner.UID : g.Group.Owner.Alias, - IsPaused = g.IsPaused, - IsModerator = g.IsModerator, - }).ToList(); + return groups.Select(g => new GroupFullInfoDto(g.Group.ToGroupData(), g.Group.Owner.ToUserData(), + g.Group.GetGroupPermissions(), g.GetGroupPairPermissions(), g.GetGroupPairUserInfo())).ToList(); } [Authorize(Policy = "Identified")] - public async Task> GroupsGetUsersInGroup(string gid) + public async Task> GroupsGetUsersInGroup(GroupDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (inGroup, _) = await TryValidateUserInGroup(gid).ConfigureAwait(false); - if (!inGroup) return new List(); + var (inGroup, _) = await TryValidateUserInGroup(dto.Group.GID).ConfigureAwait(false); + if (!inGroup) return new List(); - var allPairs = await _dbContext.GroupPairs.Include(g => g.GroupUser).Where(g => g.GroupGID == gid && g.GroupUserUID != UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); - return allPairs.Select(p => new GroupPairDto() - { - GroupGID = gid, - IsPaused = p.IsPaused, - IsRemoved = false, - UserUID = p.GroupUser.UID, - UserAlias = p.GroupUser.Alias, - IsPinned = p.IsPinned, - IsModerator = p.IsModerator, - }).ToList(); + var group = await _dbContext.Groups.SingleAsync(g => g.GID == dto.Group.GID).ConfigureAwait(false); + var allPairs = await _dbContext.GroupPairs.Include(g => g.GroupUser).Where(g => g.GroupGID == dto.Group.GID && g.GroupUserUID != UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); + return allPairs.Select(p => new GroupPairFullInfoDto(group.ToGroupData(), p.GroupUser.ToUserData(), p.GetGroupPairUserInfo(), p.GetGroupPairPermissions())).ToList(); } [Authorize(Policy = "Identified")] - public async Task GroupChangeInviteState(string gid, bool enabled) + public async Task GroupChangeGroupPermissionState(GroupPermissionDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid, enabled.ToString())); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (hasRights, group) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!hasRights) return; - group.InvitesEnabled = enabled; + group.InvitesEnabled = !dto.Permissions.HasFlag(GroupPermissions.DisableInvites); + group.DisableSounds = dto.Permissions.HasFlag(GroupPermissions.DisableSounds); + group.DisableAnimations = dto.Permissions.HasFlag(GroupPermissions.DisableAnimations); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, enabled.ToString(), "Success")); - - var groupPairs = _dbContext.GroupPairs.Where(p => p.GroupGID == gid).Select(p => p.GroupUserUID).ToList(); - await Clients.Users(groupPairs).Client_GroupChange(new GroupDto() - { - GID = gid, - InvitesEnabled = enabled, - }).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); } [Authorize(Policy = "Identified")] - public async Task GroupDelete(string gid) + public async Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (hasRights, group) = await TryValidateOwner(gid).ConfigureAwait(false); + var (inGroup, groupPair) = await TryValidateUserInGroup(dto.Group.GID).ConfigureAwait(false); + if (!inGroup) return; - _logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); + var wasPaused = groupPair.IsPaused; + groupPair.DisableSounds = dto.GroupPairPermissions.IsDisableSounds(); + groupPair.DisableAnimations = dto.GroupPairPermissions.IsDisableAnimations(); + groupPair.IsPaused = dto.GroupPairPermissions.IsPaused(); - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid).ToListAsync().ConfigureAwait(false); + await _dbContext.SaveChangesAsync().ConfigureAwait(false); + + var groupPairs = _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == dto.Group.GID).ToList(); + await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupPairChangePermissions(dto).ConfigureAwait(false); + + var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); + var self = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); + + if (wasPaused == groupPair.IsPaused) return; + + foreach (var groupUserPair in groupPairs.Where(u => !string.Equals(u.GroupUserUID, UserUID, StringComparison.Ordinal)).ToList()) + { + var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal)); + if (userPair != null) + { + if (userPair.IsDirectlyPaused != PauseInfo.NoConnection) continue; + if (userPair.IsPausedExcludingGroup(dto.Group.GID) is PauseInfo.Unpaused) continue; + if (userPair.IsOtherPausedForSpecificGroup(dto.Group.GID) is PauseInfo.Paused) continue; + } + + var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false); + if (!string.IsNullOrEmpty(groupUserIdent)) + { + if (!groupPair.IsPaused) + { + await Clients.User(UserUID).Client_UserSendOnline(new(groupUserPair.ToUserData(), groupUserIdent)).ConfigureAwait(false); + await Clients.User(groupUserPair.GroupUserUID) + .Client_UserSendOnline(new(self.ToUserData(), UserCharaIdent)).ConfigureAwait(false); + } + else + { + await Clients.User(UserUID).Client_UserSendOffline(new(groupUserPair.ToUserData())).ConfigureAwait(false); + await Clients.User(groupUserPair.GroupUserUID) + .Client_UserSendOffline(new(self.ToUserData())).ConfigureAwait(false); + } + } + } + } + + [Authorize(Policy = "Identified")] + public async Task GroupDelete(GroupDto dto) + { + _logger.LogCallInfo(MareHubLogger.Args(dto)); + + var (hasRights, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false); + + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); + + var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false); _dbContext.RemoveRange(groupPairs); _dbContext.Remove(group); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupChange(new GroupDto() - { - GID = group.GID, - IsDeleted = true, - }).ConfigureAwait(false); - + await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false); await SendGroupDeletedToAll(groupPairs).ConfigureAwait(false); } [Authorize(Policy = "Identified")] - public async Task GroupJoin(string gid, string password) + public async Task GroupJoin(GroupPasswordDto dto) { - gid = gid.Trim(); + var gid = dto.Group.GID.Trim(); - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto.Group)); var group = await _dbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == gid || g.Alias == gid).ConfigureAwait(false); var existingPair = await _dbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false); - var hashedPw = StringUtils.Sha256String(password); + var hashedPw = StringUtils.Sha256String(dto.Password); var existingUserCount = await _dbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == gid).ConfigureAwait(false); var joinedGroups = await _dbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false); var isBanned = await _dbContext.GroupBans.AnyAsync(g => g.GroupGID == gid && g.BannedUserUID == UserUID).ConfigureAwait(false); @@ -192,6 +213,8 @@ public partial class MareHub { GroupGID = group.GID, GroupUserUID = UserUID, + DisableAnimations = false, + DisableSounds = false }; await _dbContext.GroupPairs.AddAsync(newPair).ConfigureAwait(false); @@ -199,29 +222,18 @@ public partial class MareHub _logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); - await Clients.User(UserUID).Client_GroupChange(new GroupDto() - { - GID = group.GID, - OwnedBy = string.IsNullOrEmpty(group.Owner.Alias) ? group.Owner.UID : group.Owner.Alias, - IsDeleted = false, - IsPaused = false, - Alias = group.Alias, - InvitesEnabled = true, - }).ConfigureAwait(false); + await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(group.ToGroupData(), group.Owner.ToUserData(), group.GetGroupPermissions(), newPair.GetGroupPairPermissions(), newPair.GetGroupPairUserInfo())).ConfigureAwait(false); var self = _dbContext.Users.Single(u => u.UID == UserUID); - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false); - await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() + var groupPairs = await _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false); + + await Clients.Users(groupPairs.Select(p => p.GroupUserUID)) + .Client_GroupPairJoined(new GroupPairFullInfoDto(group.ToGroupData(), self.ToUserData(), newPair.GetGroupPairUserInfo(), newPair.GetGroupPairPermissions())).ConfigureAwait(false); + foreach (var pair in groupPairs) { - GroupGID = group.GID, - IsPaused = false, - IsRemoved = false, - UserUID = UserUID, - UserAlias = self.Alias, - IsPinned = false, - IsModerator = false, - }).ConfigureAwait(false); + await Clients.User(UserUID).Client_GroupPairJoined(new GroupPairFullInfoDto(group.ToGroupData(), pair.ToUserData(), pair.GetGroupPairUserInfo(), pair.GetGroupPairPermissions())).ConfigureAwait(false); + } var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); @@ -232,11 +244,12 @@ public partial class MareHub if (userPair.IsPausedExcludingGroup(gid) is PauseInfo.Unpaused) continue; if (userPair.IsPausedPerGroup is PauseInfo.Paused) continue; - var groupUserIdent = await GetIdentFromUidFromRedis(groupUserPair.GroupUserUID).ConfigureAwait(false); + var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false); if (!string.IsNullOrEmpty(groupUserIdent)) { - await Clients.User(UserUID).Client_UserChangePairedPlayer(groupUserIdent, true).ConfigureAwait(false); - await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(UserCharaIdent, true).ConfigureAwait(false); + await Clients.User(UserUID).Client_UserSendOnline(new(groupUserPair.ToUserData(), groupUserIdent)).ConfigureAwait(false); + await Clients.User(groupUserPair.GroupUserUID) + .Client_UserSendOnline(new(self.ToUserData(), UserCharaIdent)).ConfigureAwait(false); } } @@ -244,12 +257,12 @@ public partial class MareHub } [Authorize(Policy = "Identified")] - public async Task> GroupCreateTempInvite(string gid, int amount) + public async Task> GroupCreateTempInvite(GroupDto dto, int amount) { - _logger.LogCallInfo(MareHubLogger.Args(gid, amount)); + _logger.LogCallInfo(MareHubLogger.Args(dto, amount)); List inviteCodes = new(); List tempInvites = new(); - var (hasRights, group) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!hasRights) return new(); var existingInvites = await _dbContext.GroupTempInvites.Where(g => g.GroupGID == group.GID).ToListAsync().ConfigureAwait(false); @@ -283,14 +296,14 @@ public partial class MareHub } [Authorize(Policy = "Identified")] - public async Task GroupLeave(string gid) + public async Task GroupLeave(GroupDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (exists, groupPair) = await TryValidateUserInGroup(gid).ConfigureAwait(false); + var (exists, groupPair) = await TryValidateUserInGroup(dto.Group.GID).ConfigureAwait(false); if (!exists) return; - var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false); + var group = await _dbContext.Groups.SingleOrDefaultAsync(g => g.GID == dto.Group.GID).ConfigureAwait(false); var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID).ToListAsync().ConfigureAwait(false); var groupPairsWithoutSelf = groupPairs.Where(p => !string.Equals(p.GroupUserUID, UserUID, StringComparison.Ordinal)).ToList(); @@ -298,18 +311,14 @@ public partial class MareHub _dbContext.GroupPairs.Remove(groupPair); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await Clients.User(UserUID).Client_GroupChange(new GroupDto() - { - GID = group.GID, - IsDeleted = true, - }).ConfigureAwait(false); + await Clients.User(UserUID).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false); bool ownerHasLeft = string.Equals(group.OwnerUID, UserUID, StringComparison.Ordinal); if (ownerHasLeft) { if (!groupPairsWithoutSelf.Any()) { - _logger.LogCallInfo(MareHubLogger.Args(gid, "Deleted")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Deleted")); _dbContext.Groups.Remove(group); } @@ -319,24 +328,18 @@ public partial class MareHub if (groupHasMigrated.Item1) { - _logger.LogCallInfo(MareHubLogger.Args(gid, "Migrated", groupHasMigrated.Item2)); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Migrated", groupHasMigrated.Item2)); - await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupChange(new GroupDto() - { - GID = group.GID, - OwnedBy = groupHasMigrated.Item2, - Alias = null, - }).ConfigureAwait(false); + var user = await _dbContext.Users.SingleAsync(u => u.UID == groupHasMigrated.Item2).ConfigureAwait(false); + + await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupSendInfo(new GroupInfoDto(group.ToGroupData(), + user.ToUserData(), group.GetGroupPermissions())).ConfigureAwait(false); } else { - _logger.LogCallInfo(MareHubLogger.Args(gid, "Deleted")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Deleted")); - await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupChange(new GroupDto() - { - GID = group.GID, - IsDeleted = true, - }).ConfigureAwait(false); + await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupDelete(dto).ConfigureAwait(false); await SendGroupDeletedToAll(groupPairs).ConfigureAwait(false); @@ -347,14 +350,9 @@ public partial class MareHub await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); - await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = group.GID, - IsRemoved = true, - UserUID = UserUID, - }).ConfigureAwait(false); + await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupPairLeft(new GroupPairDto(dto.Group, groupPair.GroupUser.ToUserData())).ConfigureAwait(false); var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); @@ -365,107 +363,50 @@ public partial class MareHub } [Authorize(Policy = "Identified")] - public async Task GroupChangePauseState(string gid, bool isPaused) + public async Task GroupRemoveUser(GroupPairDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid, isPaused)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (exists, groupPair) = await TryValidateUserInGroup(gid).ConfigureAwait(false); - if (!exists) return; - - groupPair.IsPaused = isPaused; - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - - _logger.LogCallInfo(MareHubLogger.Args(gid, isPaused, "Success")); - - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid && p.GroupUserUID != UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); - await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = gid, - IsPaused = isPaused, - UserUID = UserUID, - }).ConfigureAwait(false); - - await Clients.User(UserUID).Client_GroupChange(new GroupDto - { - GID = gid, - IsPaused = isPaused, - }).ConfigureAwait(false); - - var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); - - foreach (var groupUserPair in groupPairs) - { - var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal)); - if (userPair != null) - { - if (userPair.IsDirectlyPaused != PauseInfo.NoConnection) continue; - if (userPair.IsPausedExcludingGroup(gid) is PauseInfo.Unpaused) continue; - if (userPair.IsOtherPausedForSpecificGroup(gid) is PauseInfo.Paused) continue; - } - - var groupUserIdent = await GetIdentFromUidFromRedis(groupUserPair.GroupUserUID).ConfigureAwait(false); - if (!string.IsNullOrEmpty(groupUserIdent)) - { - await Clients.User(UserUID).Client_UserChangePairedPlayer(groupUserIdent, !isPaused).ConfigureAwait(false); - await Clients.User(groupUserPair.GroupUserUID).Client_UserChangePairedPlayer(UserCharaIdent, !isPaused).ConfigureAwait(false); - } - } - } - - [Authorize(Policy = "Identified")] - public async Task GroupRemoveUser(string gid, string uid) - { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid)); - - var (hasRights, group) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!hasRights) return; - var (userExists, groupPair) = await TryValidateUserInGroup(gid, uid).ConfigureAwait(false); + var (userExists, groupPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false); if (!userExists) return; - if (groupPair.IsModerator || string.Equals(group.OwnerUID, uid, StringComparison.Ordinal)) return; - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, "Success")); + if (groupPair.IsModerator || string.Equals(group.OwnerUID, dto.User.UID, StringComparison.Ordinal)) return; + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); _dbContext.GroupPairs.Remove(groupPair); await _dbContext.SaveChangesAsync().ConfigureAwait(false); var groupPairs = _dbContext.GroupPairs.Where(p => p.GroupGID == group.GID).AsNoTracking().ToList(); - await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = group.GID, - IsRemoved = true, - UserUID = uid, - }).ConfigureAwait(false); + await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupPairLeft(dto).ConfigureAwait(false); - var userIdent = await GetIdentFromUidFromRedis(uid).ConfigureAwait(false); + var userIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false); if (userIdent == null) return; - await Clients.User(uid).Client_GroupChange(new GroupDto() - { - GID = gid, - IsDeleted = true, - }).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_GroupDelete(new GroupDto(dto.Group)).ConfigureAwait(false); - var allUserPairs = await GetAllPairedClientsWithPauseState(uid).ConfigureAwait(false); + var allUserPairs = await GetAllPairedClientsWithPauseState(dto.User.UID).ConfigureAwait(false); foreach (var groupUserPair in groupPairs) { - await UserGroupLeave(groupUserPair, allUserPairs, userIdent, uid).ConfigureAwait(false); + await UserGroupLeave(groupUserPair, allUserPairs, userIdent, dto.User.UID).ConfigureAwait(false); } } [Authorize(Policy = "Identified")] - public async Task GroupBanUser(string gid, string uid, string reason) + public async Task GroupBanUser(GroupPairDto dto, string reason) { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid)); + _logger.LogCallInfo(MareHubLogger.Args(dto, reason)); - var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!userHasRights) return; - var (userExists, groupPair) = await TryValidateUserInGroup(gid, uid).ConfigureAwait(false); + var (userExists, groupPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false); if (!userExists) return; - if (groupPair.IsModerator || string.Equals(group.OwnerUID, uid, StringComparison.Ordinal)) return; + if (groupPair.IsModerator || string.Equals(group.OwnerUID, dto.User.UID, StringComparison.Ordinal)) return; var alias = string.IsNullOrEmpty(groupPair.GroupUser.Alias) ? "-" : groupPair.GroupUser.Alias; var ban = new GroupBan() @@ -473,107 +414,105 @@ public partial class MareHub BannedByUID = UserUID, BannedReason = $"{reason} (Alias at time of ban: {alias})", BannedOn = DateTime.UtcNow, - BannedUserUID = uid, - GroupGID = gid, + BannedUserUID = dto.User.UID, + GroupGID = dto.Group.GID, }; _dbContext.Add(ban); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - await GroupRemoveUser(gid, uid).ConfigureAwait(false); + await GroupRemoveUser(dto).ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); } [Authorize(Policy = "Identified")] - public async Task GroupUnbanUser(string gid, string uid) + public async Task GroupUnbanUser(GroupPairDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!userHasRights) return; - var banEntry = await _dbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == gid && g.BannedUserUID == uid).ConfigureAwait(false); + var banEntry = await _dbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID).ConfigureAwait(false); if (banEntry == null) return; _dbContext.Remove(banEntry); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); } [Authorize(Policy = "Identified")] - public async Task> GroupGetBannedUsers(string gid) + public async Task> GroupGetBannedUsers(GroupDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.GID).ConfigureAwait(false); if (!userHasRights) return new List(); - var banEntries = await _dbContext.GroupBans.Where(g => g.GroupGID == gid).AsNoTracking().ToListAsync().ConfigureAwait(false); + var banEntries = await _dbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync().ConfigureAwait(false); - List bannedGroupUsers = banEntries.Select(b => new BannedGroupUserDto() - { - BannedBy = b.BannedByUID, - BannedOn = b.BannedOn, - Reason = b.BannedReason, - UID = b.BannedUserUID, + List bannedGroupUsers = banEntries.Select(b => + new BannedGroupUserDto(group.ToGroupData(), b.BannedUser.ToUserData(), b.BannedReason, b.BannedOn, + b.BannedByUID)).ToList(); - }).ToList(); - - _logger.LogCallInfo(MareHubLogger.Args(gid, bannedGroupUsers.Count)); + _logger.LogCallInfo(MareHubLogger.Args(dto, bannedGroupUsers.Count)); return bannedGroupUsers; } [Authorize(Policy = "Identified")] - public async Task GroupSetModerator(string gid, string uid, bool isGroupModerator) + public async Task GroupSetUserInfo(GroupPairUserInfoDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, isGroupModerator)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (userHasRights, _) = await TryValidateOwner(gid).ConfigureAwait(false); - if (!userHasRights) return; - - var (userExists, userPair) = await TryValidateUserInGroup(gid, uid).ConfigureAwait(false); + var (userExists, userPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false); if (!userExists) return; - userPair.IsModerator = isGroupModerator; + var (userIsOwner, _) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false); + var (userIsModerator, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); + + if (dto.GroupUserInfo.HasFlag(GroupUserInfo.IsPinned) && userIsModerator && !userPair.IsPinned) + { + userPair.IsPinned = true; + } + else if (userIsModerator && userPair.IsPinned) + { + userPair.IsPinned = false; + } + + if (dto.GroupUserInfo.HasFlag(GroupUserInfo.IsModerator) && userIsOwner && !userPair.IsModerator) + { + userPair.IsModerator = true; + } + else if (userIsOwner && userPair.IsModerator) + { + userPair.IsModerator = false; + } + await _dbContext.SaveChangesAsync().ConfigureAwait(false); - var groupPairs = await _dbContext.GroupPairs.Where(g => g.GroupGID == gid).AsNoTracking().ToListAsync().ConfigureAwait(false); - - await Clients.User(uid).Client_GroupChange(new GroupDto() - { - GID = gid, - IsModerator = isGroupModerator, - }).ConfigureAwait(false); - - await Clients.Users(groupPairs.Where(p => !string.Equals(p.GroupUserUID, uid, StringComparison.Ordinal)) - .Select(g => g.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = gid, - IsModerator = isGroupModerator, - UserUID = uid, - }).ConfigureAwait(false); - - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, isGroupModerator, "Success")); + var groupPairs = await _dbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync().ConfigureAwait(false); + await Clients.Users(groupPairs).Client_GroupPairChangeUserInfo(new GroupPairUserInfoDto(dto.Group, dto.User, userPair.GetGroupPairUserInfo())).ConfigureAwait(false); } - [Authorize(Policy = "Identified")] - public async Task GroupChangeOwnership(string gid, string uid) - { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid)); - var (isOwner, group) = await TryValidateOwner(gid).ConfigureAwait(false); + [Authorize(Policy = "Identified")] + public async Task GroupChangeOwnership(GroupPairDto dto) + { + _logger.LogCallInfo(MareHubLogger.Args(dto)); + + var (isOwner, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false); if (!isOwner) return; - var (isInGroup, newOwnerPair) = await TryValidateUserInGroup(gid, uid).ConfigureAwait(false); + var (isInGroup, newOwnerPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false); if (!isInGroup) return; - var ownedShells = await _dbContext.Groups.CountAsync(g => g.OwnerUID == uid).ConfigureAwait(false); + var ownedShells = await _dbContext.Groups.CountAsync(g => g.OwnerUID == dto.User.UID).ConfigureAwait(false); if (ownedShells >= _maxExistingGroupsByUser) return; - var prevOwner = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false); + var prevOwner = await _dbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.GroupUserUID == UserUID).ConfigureAwait(false); prevOwner.IsPinned = false; group.Owner = newOwnerPair.GroupUser; group.Alias = null; @@ -581,93 +520,42 @@ public partial class MareHub newOwnerPair.IsModerator = false; await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid).Select(p => p.GroupUserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); + var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); - await Clients.Users(uid).Client_GroupChange(new GroupDto() - { - GID = gid, - OwnedBy = string.IsNullOrEmpty(group.Owner.Alias) ? group.Owner.UID : group.Owner.Alias, - IsModerator = false, - Alias = null, - }).ConfigureAwait(false); - - await Clients.Users(groupPairs).Client_GroupChange(new GroupDto() - { - GID = gid, - OwnedBy = string.IsNullOrEmpty(group.Owner.Alias) ? group.Owner.UID : group.Owner.Alias, - Alias = null, - }).ConfigureAwait(false); - - await Clients.Users(groupPairs.Where(p => !string.Equals(p, uid, StringComparison.Ordinal))).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = gid, - UserUID = uid, - IsPinned = true, - IsModerator = false, - }).ConfigureAwait(false); + await Clients.Users(groupPairs).Client_GroupSendInfo(new GroupInfoDto(group.ToGroupData(), newOwnerPair.GroupUser.ToUserData(), group.GetGroupPermissions())).ConfigureAwait(false); } [Authorize(Policy = "Identified")] - public async Task GroupChangePassword(string gid, string password) + public async Task GroupChangePassword(GroupPasswordDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (isOwner, group) = await TryValidateOwner(gid).ConfigureAwait(false); - if (!isOwner || password.Length < 10) return false; + var (isOwner, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false); + if (!isOwner || dto.Password.Length < 10) return false; - _logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); - group.HashedPassword = StringUtils.Sha256String(password); + group.HashedPassword = StringUtils.Sha256String(dto.Password); await _dbContext.SaveChangesAsync().ConfigureAwait(false); return true; } [Authorize(Policy = "Identified")] - public async Task GroupChangePinned(string gid, string uid, bool isPinned) + public async Task GroupClear(GroupDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, isPinned)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); - if (!userHasRights) return; - - var (userInGroup, groupPair) = await TryValidateUserInGroup(gid, uid).ConfigureAwait(false); - if (!userInGroup) return; - - groupPair.IsPinned = isPinned; - await _dbContext.SaveChangesAsync().ConfigureAwait(false); - - _logger.LogCallInfo(MareHubLogger.Args(gid, uid, isPinned, "Success")); - - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid).Select(p => p.GroupUserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); - - await Clients.Users(groupPairs.Where(p => !string.Equals(p, uid, StringComparison.Ordinal))).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = gid, - UserUID = uid, - IsPinned = isPinned, - }).ConfigureAwait(false); - } - - [Authorize(Policy = "Identified")] - public async Task GroupClear(string gid) - { - _logger.LogCallInfo(MareHubLogger.Args(gid)); - - var (hasRights, group) = await TryValidateGroupModeratorOrOwner(gid).ConfigureAwait(false); + var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false); if (!hasRights) return; - var groupPairs = await _dbContext.GroupPairs.Where(p => p.GroupGID == gid).ToListAsync().ConfigureAwait(false); + var groupPairs = await _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false); - await Clients.Users(groupPairs.Where(p => !p.IsPinned && !p.IsModerator).Select(g => g.GroupUserUID)).Client_GroupChange(new GroupDto() - { - GID = group.GID, - IsDeleted = true, - }).ConfigureAwait(false); + await Clients.Users(groupPairs.Where(p => !p.IsPinned && !p.IsModerator).Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(gid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); var notPinned = groupPairs.Where(g => !g.IsPinned && !g.IsModerator).ToList(); @@ -676,14 +564,9 @@ public partial class MareHub foreach (var pair in notPinned) { - await Clients.Users(groupPairs.Where(p => p.IsPinned).Select(g => g.GroupUserUID)).Client_GroupUserChange(new GroupPairDto() - { - GroupGID = pair.GroupGID, - IsRemoved = true, - UserUID = pair.GroupUserUID, - }).ConfigureAwait(false); + await Clients.Users(groupPairs.Where(p => p.IsPinned).Select(g => g.GroupUserUID)).Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false); - var pairIdent = await GetIdentFromUidFromRedis(pair.GroupUserUID).ConfigureAwait(false); + var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false); if (string.IsNullOrEmpty(pairIdent)) continue; var allUserPairs = await GetAllPairedClientsWithPauseState(pair.GroupUserUID).ConfigureAwait(false); diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs index 6de8cfb..da28cc0 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.User.cs @@ -1,5 +1,9 @@ using System.Text.RegularExpressions; -using MareSynchronos.API; +using MareSynchronos.API.Data; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.API.Dto.Group; +using MareSynchronos.API.Dto.User; using MareSynchronosServer.Utils; using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; @@ -38,16 +42,12 @@ public partial class MareHub .Where(u => u.OtherUser.UID == UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false); foreach (var pair in otherPairData) { - await Clients.User(pair.User.UID).Client_UserUpdateClientPairs(new ClientPairDto() - { - OtherUID = UserUID, - IsRemoved = true, - }).ConfigureAwait(false); + await Clients.User(pair.UserUID).Client_UserRemoveClientPair(new(userEntry.ToUserData())).ConfigureAwait(false); } foreach (var pair in groupPairs) { - await GroupLeave(pair.GroupGID).ConfigureAwait(false); + await GroupLeave(new GroupDto(new GroupData(pair.GroupGID))).ConfigureAwait(false); } _mareMetrics.IncCounter(MetricsAPI.CounterUsersRegisteredDeleted, 1); @@ -59,17 +59,18 @@ public partial class MareHub } [Authorize(Policy = "Identified")] - public async Task> UserGetOnlineCharacters() + public async Task> UserGetOnlinePairs() { _logger.LogCallInfo(); - var usersToSendOnlineTo = await SendOnlineToAllPairedUsers(UserCharaIdent).ConfigureAwait(false); - var idents = await GetIdentFromUidsFromRedis(usersToSendOnlineTo).ConfigureAwait(false); - return idents.Where(i => !string.IsNullOrEmpty(i.Value)).Select(k => k.Value).ToList(); + var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false); + var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false); + + return pairs.Select(p => new OnlineUserIdentDto(new UserData(p.Key), p.Value)).ToList(); } [Authorize(Policy = "Identified")] - public async Task> UserGetPairedClients() + public async Task> UserGetPairedClients() { _logger.LogCallInfo(); @@ -98,13 +99,16 @@ public partial class MareHub IsSynced = otherEntry != null, }; - return (await query.AsNoTracking().ToListAsync().ConfigureAwait(false)).Select(f => new ClientPairDto() + var results = await query.AsNoTracking().ToListAsync().ConfigureAwait(false); + + return results.Select(c => { - VanityUID = f.Alias, - IsPaused = f.IsPaused, - OtherUID = f.OtherUserUID, - IsSynced = f.IsSynced, - IsPausedFromOthers = f.OtherIsPaused, + var ownPerm = UserPermissions.Paired; + ownPerm.SetPaused(c.IsPaused); + var otherPerm = UserPermissions.NoneSet; + otherPerm.SetPaired(c.IsSynced); + otherPerm.SetPaused(c.OtherIsPaused); + return new UserPairDto(new(c.OtherUserUID, c.Alias), ownPerm, otherPerm); }).ToList(); } @@ -117,14 +121,14 @@ public partial class MareHub private static readonly string[] AllowedExtensionsForGamePaths = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp", ".shpk" }; [Authorize(Policy = "Identified")] - public async Task UserPushData(CharacterCacheDto characterCache, List visibleCharacterIds) + public async Task UserPushData(UserCharaDataMessageDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(visibleCharacterIds.Count)); + _logger.LogCallInfo(MareHubLogger.Args(dto.CharaData.FileReplacements.Count)); bool hadInvalidData = false; List invalidGamePaths = new(); List invalidFileSwapPaths = new(); - foreach (var replacement in characterCache.FileReplacements.SelectMany(p => p.Value)) + foreach (var replacement in dto.CharaData.FileReplacements.SelectMany(p => p.Value)) { var invalidPaths = replacement.GamePaths.Where(p => !GamePathRegex().IsMatch(p)).ToList(); invalidPaths.AddRange(replacement.GamePaths.Where(p => !AllowedExtensionsForGamePaths.Any(e => p.EndsWith(e, StringComparison.OrdinalIgnoreCase)))); @@ -153,33 +157,32 @@ public partial class MareHub } var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false); - var idents = await GetIdentFromUidsFromRedis(allPairedUsers).ConfigureAwait(false); + var idents = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false); - var allPairedUsersDict = idents - .Where(f => visibleCharacterIds.Contains(f.Value, StringComparer.Ordinal)); + var recipients = allPairedUsers.Where(f => dto.Recipients.Select(r => r.UID).Contains(f, StringComparer.Ordinal)).ToList(); - _logger.LogCallInfo(MareHubLogger.Args(visibleCharacterIds.Count, allPairedUsersDict.Count())); + _logger.LogCallInfo(MareHubLogger.Args(idents.Count, recipients.Count())); - await Clients.Users(allPairedUsersDict.Select(f => f.Key)).Client_UserReceiveCharacterData(characterCache, UserCharaIdent).ConfigureAwait(false); + await Clients.Users(recipients).Client_UserReceiveCharacterData(new OnlineUserCharaDataDto(new UserData(UserUID), dto.CharaData)).ConfigureAwait(false); _mareMetrics.IncCounter(MetricsAPI.CounterUserPushData); - _mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, allPairedUsersDict.Count()); + _mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, recipients.Count()); } [Authorize(Policy = "Identified")] - public async Task UserAddPair(string uid) + public async Task UserAddPair(UserDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(uid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); // don't allow adding yourself or nothing - uid = uid.Trim(); - if (string.Equals(uid, UserUID, StringComparison.Ordinal) || string.IsNullOrWhiteSpace(uid)) return; + var uid = dto.User.UID.Trim(); + if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal) || string.IsNullOrWhiteSpace(dto.User.UID)) return; // grab other user, check if it exists and if a pair already exists var otherUser = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == uid || u.Alias == uid).ConfigureAwait(false); if (otherUser == null) { - await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {uid}, UID does not exist").ConfigureAwait(false); + await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {dto.User.UID}, UID does not exist").ConfigureAwait(false); return; } @@ -190,14 +193,14 @@ public partial class MareHub if (existingEntry != null) { - await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {uid}, already paired").ConfigureAwait(false); + await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {dto.User.UID}, already paired").ConfigureAwait(false); return; } // grab self create new client pair and save var user = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(uid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); ClientPair wl = new ClientPair() { @@ -210,139 +213,110 @@ public partial class MareHub // get the opposite entry of the client pair var otherEntry = OppositeEntry(otherUser.UID); - await Clients.User(user.UID).Client_UserUpdateClientPairs( - new ClientPairDto() - { - VanityUID = otherUser.Alias, - OtherUID = otherUser.UID, - IsPaused = false, - IsPausedFromOthers = otherEntry?.IsPaused ?? false, - IsSynced = otherEntry != null, - }).ConfigureAwait(false); + var otherIdent = await GetUserIdent(otherUser.UID).ConfigureAwait(false); - // if there's no opposite entry do nothing - if (otherEntry == null) return; + var ownPerm = UserPermissions.Paired; + var otherPerm = UserPermissions.NoneSet; + otherPerm.SetPaired(otherEntry != null); + otherPerm.SetPaused(otherEntry?.IsPaused ?? false); + var userPairResponse = new UserPairDto(otherUser.ToUserData(), ownPerm, otherPerm); + await Clients.User(user.UID).Client_UserAddClientPair(userPairResponse).ConfigureAwait(false); // check if other user is online - var otherIdent = await GetIdentFromUidFromRedis(otherUser.UID).ConfigureAwait(false); if (otherIdent == null) return; // send push with update to other user if other user is online - await Clients.User(otherUser.UID).Client_UserUpdateClientPairs( - new ClientPairDto() - { - VanityUID = user.Alias, - OtherUID = user.UID, - IsPaused = otherEntry.IsPaused, - IsPausedFromOthers = false, - IsSynced = true, - }).ConfigureAwait(false); + await Clients.User(otherUser.UID).Client_UserAddClientPair(new UserPairDto(user.ToUserData(), otherPerm, ownPerm)).ConfigureAwait(false); - // get own ident and all pairs - var userIdent = await GetIdentFromUidFromRedis(user.UID).ConfigureAwait(false); - var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); - - // if the other user has paused the main user and there was no previous group connection don't send anything - if (!otherEntry.IsPaused && allUserPairs.Any(p => string.Equals(p.UID, otherUser.UID, StringComparison.Ordinal) && p.IsPausedPerGroup is PauseInfo.Paused or PauseInfo.NoConnection)) + if (!otherPerm.IsPaused()) { - await Clients.User(user.UID).Client_UserChangePairedPlayer(otherIdent, true).ConfigureAwait(false); - await Clients.User(otherUser.UID).Client_UserChangePairedPlayer(userIdent, true).ConfigureAwait(false); + await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherIdent)).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false); } } [Authorize(Policy = "Identified")] - public async Task UserChangePairPauseStatus(string otherUserUid, bool isPaused) + public async Task UserSetPairPermissions(UserPermissionsDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(otherUserUid, isPaused)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - if (string.Equals(otherUserUid, UserUID, StringComparison.Ordinal)) return; - ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == otherUserUid).ConfigureAwait(false); + if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) return; + ClientPair pair = await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false); if (pair == null) return; - pair.IsPaused = isPaused; + pair.IsPaused = dto.Permissions.IsPaused(); _dbContext.Update(pair); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(otherUserUid, isPaused, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); - var otherEntry = OppositeEntry(otherUserUid); + var otherEntry = OppositeEntry(dto.User.UID); + + await Clients.User(UserUID).Client_UserUpdateSelfPairPermissions(dto).ConfigureAwait(false); - await Clients.User(UserUID).Client_UserUpdateClientPairs( - new ClientPairDto() - { - OtherUID = otherUserUid, - IsPaused = isPaused, - IsPausedFromOthers = otherEntry?.IsPaused ?? false, - IsSynced = otherEntry != null, - }).ConfigureAwait(false); if (otherEntry != null) { - await Clients.User(otherUserUid).Client_UserUpdateClientPairs(new ClientPairDto() - { - OtherUID = UserUID, - IsPaused = otherEntry.IsPaused, - IsPausedFromOthers = isPaused, - IsSynced = true, - }).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), dto.Permissions)).ConfigureAwait(false); - var otherCharaIdent = await GetIdentFromUidFromRedis(pair.OtherUserUID).ConfigureAwait(false); + var otherCharaIdent = await GetUserIdent(pair.OtherUserUID).ConfigureAwait(false); if (UserCharaIdent == null || otherCharaIdent == null || otherEntry.IsPaused) return; - await Clients.User(UserUID).Client_UserChangePairedPlayer(otherCharaIdent, !isPaused).ConfigureAwait(false); - await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, !isPaused).ConfigureAwait(false); + if (dto.Permissions.IsPaused()) + { + await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false); + } + else + { + await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherCharaIdent)).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false); + } } } [Authorize(Policy = "Identified")] - public async Task UserRemovePair(string otherUserUid) + public async Task UserRemovePair(UserDto dto) { - _logger.LogCallInfo(MareHubLogger.Args(otherUserUid)); + _logger.LogCallInfo(MareHubLogger.Args(dto)); - if (string.Equals(otherUserUid, UserUID, StringComparison.Ordinal)) return; + if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) return; // check if client pair even exists ClientPair callerPair = - await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == otherUserUid).ConfigureAwait(false); - bool callerHadPaused = callerPair.IsPaused; + await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false); if (callerPair == null) return; + bool callerHadPaused = callerPair.IsPaused; + // delete from database, send update info to users pair list _dbContext.ClientPairs.Remove(callerPair); await _dbContext.SaveChangesAsync().ConfigureAwait(false); - _logger.LogCallInfo(MareHubLogger.Args(otherUserUid, "Success")); + _logger.LogCallInfo(MareHubLogger.Args(dto, "Success")); - await Clients.User(UserUID) - .Client_UserUpdateClientPairs(new ClientPairDto() - { - OtherUID = otherUserUid, - IsRemoved = true, - }).ConfigureAwait(false); + await Clients.User(UserUID).Client_UserRemoveClientPair(dto).ConfigureAwait(false); // check if opposite entry exists - var oppositeClientPair = OppositeEntry(otherUserUid); + var oppositeClientPair = OppositeEntry(dto.User.UID); if (oppositeClientPair == null) return; // check if other user is online, if no then there is no need to do anything further - var otherIdent = await GetIdentFromUidFromRedis(otherUserUid).ConfigureAwait(false); + var otherIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false); if (otherIdent == null) return; // get own ident and - await Clients.User(otherUserUid).Client_UserUpdateClientPairs( - new ClientPairDto() - { - OtherUID = UserUID, - IsPausedFromOthers = false, - IsSynced = false, - }).ConfigureAwait(false); + await Clients.User(dto.User.UID) + .Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), + UserPermissions.NoneSet)).ConfigureAwait(false); + // if the other user had paused the user the state will be offline for either, do nothing bool otherHadPaused = oppositeClientPair.IsPaused; if (!callerHadPaused && otherHadPaused) return; var allUsers = await GetAllPairedClientsWithPauseState().ConfigureAwait(false); - var pauseEntry = allUsers.SingleOrDefault(f => string.Equals(f.UID, otherUserUid, StringComparison.Ordinal)); + var pauseEntry = allUsers.SingleOrDefault(f => string.Equals(f.UID, dto.User.UID, StringComparison.Ordinal)); var isPausedInGroup = pauseEntry == null || pauseEntry.IsPausedPerGroup is PauseInfo.Paused or PauseInfo.NoConnection; // if neither user had paused each other and both are in unpaused groups, state will be online for both, do nothing @@ -351,15 +325,15 @@ public partial class MareHub // if neither user had paused each other and either is not in an unpaused group with each other, change state to offline if (!callerHadPaused && !otherHadPaused && isPausedInGroup) { - await Clients.User(UserUID).Client_UserChangePairedPlayer(otherIdent, false).ConfigureAwait(false); - await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, false).ConfigureAwait(false); + await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false); } // if the caller had paused other but not the other has paused the caller and they are in an unpaused group together, change state to online if (callerHadPaused && !otherHadPaused && !isPausedInGroup) { - await Clients.User(UserUID).Client_UserChangePairedPlayer(otherIdent, true).ConfigureAwait(false); - await Clients.User(otherUserUid).Client_UserChangePairedPlayer(UserCharaIdent, true).ConfigureAwait(false); + await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherIdent)).ConfigureAwait(false); + await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index 1bd6aff..2b58e60 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -1,4 +1,7 @@ -using MareSynchronos.API; +using MareSynchronos.API.Data; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Dto; +using MareSynchronos.API.SignalR; using MareSynchronosServer.Services; using MareSynchronosServer.Utils; using MareSynchronosShared; @@ -66,14 +69,14 @@ public partial class MareHub : Hub, IMareHub await _dbContext.SaveChangesAsync().ConfigureAwait(false); await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Welcome to Mare Synchronos \"" + _shardName + "\", Current Online Users: " + _systemInfoService.SystemInfoDto.OnlineUsers).ConfigureAwait(false); + await SendOnlineToAllPairedUsers().ConfigureAwait(false); - return new ConnectionDto() + return new ConnectionDto(new UserData(dbUser.UID, dbUser.Alias)) { ServerVersion = IMareHub.ApiVersion, - UID = string.IsNullOrEmpty(dbUser.Alias) ? dbUser.UID : dbUser.Alias, IsAdmin = dbUser.IsAdmin, IsModerator = dbUser.IsModerator, - ServerInfo = new ServerInfoDto() + ServerInfo = new ServerInfo() { MaxGroupsCreatedByUser = _maxExistingGroupsByUser, ShardName = _shardName, @@ -118,7 +121,7 @@ public partial class MareHub : Hub, IMareHub await RemoveUserFromRedis().ConfigureAwait(false); - await SendOfflineToAllPairedUsers(UserCharaIdent).ConfigureAwait(false); + await SendOfflineToAllPairedUsers().ConfigureAwait(false); _dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == UserUID)); await _dbContext.SaveChangesAsync().ConfigureAwait(false); diff --git a/MareSynchronosServer/MareSynchronosServer/Services/ClientMessageService.cs b/MareSynchronosServer/MareSynchronosServer/Services/ClientMessageService.cs index 8b8acf8..ca40ec4 100644 --- a/MareSynchronosServer/MareSynchronosServer/Services/ClientMessageService.cs +++ b/MareSynchronosServer/MareSynchronosServer/Services/ClientMessageService.cs @@ -1,5 +1,6 @@ using Grpc.Core; -using MareSynchronos.API; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.SignalR; using MareSynchronosServer.Hubs; using MareSynchronosShared.Protos; using Microsoft.AspNetCore.SignalR; diff --git a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs index f62cb9e..2df0c32 100644 --- a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs @@ -1,4 +1,5 @@ -using MareSynchronos.API; +using MareSynchronos.API.Dto; +using MareSynchronos.API.SignalR; using MareSynchronosServer.Hubs; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index db7b209..814d01b 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -1,4 +1,3 @@ -using MareSynchronos.API; using Microsoft.EntityFrameworkCore; using MareSynchronosServer.Hubs; using Microsoft.AspNetCore.Http.Connections; @@ -24,6 +23,7 @@ using StackExchange.Redis; using StackExchange.Redis.Extensions.Core.Configuration; using System.Net; using StackExchange.Redis.Extensions.System.Text.Json; +using MareSynchronos.API.SignalR; namespace MareSynchronosServer; diff --git a/MareSynchronosServer/MareSynchronosServer/Utils/Extensions.cs b/MareSynchronosServer/MareSynchronosServer/Utils/Extensions.cs new file mode 100644 index 0000000..37ab869 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Utils/Extensions.cs @@ -0,0 +1,51 @@ +using MareSynchronos.API.Data; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Data.Extensions; +using MareSynchronosShared.Models; + +namespace MareSynchronosServer.Utils +{ + public static class Extensions + { + public static GroupData ToGroupData(this Group group) + { + return new GroupData(group.GID, group.Alias); + } + + public static UserData ToUserData(this GroupPair pair) + { + return new UserData(pair.GroupUser.UID, pair.GroupUser.Alias); + } + + public static UserData ToUserData(this User user) + { + return new UserData(user.UID, user.Alias); + } + + public static GroupPermissions GetGroupPermissions(this Group group) + { + var permissions = GroupPermissions.NoneSet; + permissions.SetDisableAnimations(group.DisableAnimations); + permissions.SetDisableSounds(group.DisableSounds); + permissions.SetDisableInvites(!group.InvitesEnabled); + return permissions; + } + + public static GroupUserPermissions GetGroupPairPermissions(this GroupPair groupPair) + { + var permissions = GroupUserPermissions.NoneSet; + permissions.SetDisableAnimations(groupPair.DisableAnimations); + permissions.SetDisableSounds(groupPair.DisableSounds); + permissions.SetPaused(groupPair.IsPaused); + return permissions; + } + + public static GroupUserInfo GetGroupPairUserInfo(this GroupPair groupPair) + { + var groupUserInfo = GroupUserInfo.None; + groupUserInfo.SetPinned(groupPair.IsPinned); + groupUserInfo.SetModerator(groupPair.IsModerator); + return groupUserInfo; + } + } +} diff --git a/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.Designer.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.Designer.cs new file mode 100644 index 0000000..883a1c3 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.Designer.cs @@ -0,0 +1,530 @@ +// +using System; +using MareSynchronosShared.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + [DbContext(typeof(MareDbContext))] + [Migration("20230126163758_GroupPerms")] + partial class GroupPerms + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MareSynchronosShared.Models.Auth", b => + { + b.Property("HashedKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("hashed_key"); + + b.Property("IsBanned") + .HasColumnType("boolean") + .HasColumnName("is_banned"); + + b.Property("UserUID") + .HasColumnType("character varying(10)") + .HasColumnName("user_uid"); + + b.HasKey("HashedKey") + .HasName("pk_auth"); + + b.HasIndex("UserUID") + .HasDatabaseName("ix_auth_user_uid"); + + b.ToTable("auth", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.Banned", b => + { + b.Property("CharacterIdentification") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("character_identification"); + + b.Property("Reason") + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasColumnName("timestamp"); + + b.HasKey("CharacterIdentification") + .HasName("pk_banned_users"); + + b.ToTable("banned_users", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b => + { + b.Property("DiscordIdOrLodestoneAuth") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("discord_id_or_lodestone_auth"); + + b.HasKey("DiscordIdOrLodestoneAuth") + .HasName("pk_banned_registrations"); + + b.ToTable("banned_registrations", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b => + { + b.Property("UserUID") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("user_uid"); + + b.Property("OtherUserUID") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("other_user_uid"); + + b.Property("AllowReceivingMessages") + .HasColumnType("boolean") + .HasColumnName("allow_receiving_messages"); + + b.Property("IsPaused") + .HasColumnType("boolean") + .HasColumnName("is_paused"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasColumnName("timestamp"); + + b.HasKey("UserUID", "OtherUserUID") + .HasName("pk_client_pairs"); + + b.HasIndex("OtherUserUID") + .HasDatabaseName("ix_client_pairs_other_user_uid"); + + b.HasIndex("UserUID") + .HasDatabaseName("ix_client_pairs_user_uid"); + + b.ToTable("client_pairs", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b => + { + b.Property("Hash") + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("hash"); + + b.Property("Size") + .HasColumnType("bigint") + .HasColumnName("size"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasColumnName("timestamp"); + + b.Property("UploadDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("upload_date"); + + b.Property("Uploaded") + .HasColumnType("boolean") + .HasColumnName("uploaded"); + + b.Property("UploaderUID") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("uploader_uid"); + + b.HasKey("Hash") + .HasName("pk_file_caches"); + + b.HasIndex("UploaderUID") + .HasDatabaseName("ix_file_caches_uploader_uid"); + + b.ToTable("file_caches", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b => + { + b.Property("Hash") + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("hash"); + + b.Property("ForbiddenBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("forbidden_by"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasColumnName("timestamp"); + + b.HasKey("Hash") + .HasName("pk_forbidden_upload_entries"); + + b.ToTable("forbidden_upload_entries", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.Group", b => + { + b.Property("GID") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("gid"); + + b.Property("Alias") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("alias"); + + b.Property("DisableAnimations") + .HasColumnType("boolean") + .HasColumnName("disable_animations"); + + b.Property("DisableSounds") + .HasColumnType("boolean") + .HasColumnName("disable_sounds"); + + b.Property("HashedPassword") + .HasColumnType("text") + .HasColumnName("hashed_password"); + + b.Property("InvitesEnabled") + .HasColumnType("boolean") + .HasColumnName("invites_enabled"); + + b.Property("OwnerUID") + .HasColumnType("character varying(10)") + .HasColumnName("owner_uid"); + + b.HasKey("GID") + .HasName("pk_groups"); + + b.HasIndex("OwnerUID") + .HasDatabaseName("ix_groups_owner_uid"); + + b.ToTable("groups", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b => + { + b.Property("GroupGID") + .HasColumnType("character varying(20)") + .HasColumnName("group_gid"); + + b.Property("BannedUserUID") + .HasColumnType("character varying(10)") + .HasColumnName("banned_user_uid"); + + b.Property("BannedByUID") + .HasColumnType("character varying(10)") + .HasColumnName("banned_by_uid"); + + b.Property("BannedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("banned_on"); + + b.Property("BannedReason") + .HasColumnType("text") + .HasColumnName("banned_reason"); + + b.HasKey("GroupGID", "BannedUserUID") + .HasName("pk_group_bans"); + + b.HasIndex("BannedByUID") + .HasDatabaseName("ix_group_bans_banned_by_uid"); + + b.HasIndex("BannedUserUID") + .HasDatabaseName("ix_group_bans_banned_user_uid"); + + b.HasIndex("GroupGID") + .HasDatabaseName("ix_group_bans_group_gid"); + + b.ToTable("group_bans", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b => + { + b.Property("GroupGID") + .HasColumnType("character varying(20)") + .HasColumnName("group_gid"); + + b.Property("GroupUserUID") + .HasColumnType("character varying(10)") + .HasColumnName("group_user_uid"); + + b.Property("DisableAnimations") + .HasColumnType("boolean") + .HasColumnName("disable_animations"); + + b.Property("DisableSounds") + .HasColumnType("boolean") + .HasColumnName("disable_sounds"); + + b.Property("IsModerator") + .HasColumnType("boolean") + .HasColumnName("is_moderator"); + + b.Property("IsPaused") + .HasColumnType("boolean") + .HasColumnName("is_paused"); + + b.Property("IsPinned") + .HasColumnType("boolean") + .HasColumnName("is_pinned"); + + b.HasKey("GroupGID", "GroupUserUID") + .HasName("pk_group_pairs"); + + b.HasIndex("GroupGID") + .HasDatabaseName("ix_group_pairs_group_gid"); + + b.HasIndex("GroupUserUID") + .HasDatabaseName("ix_group_pairs_group_user_uid"); + + b.ToTable("group_pairs", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b => + { + b.Property("GroupGID") + .HasColumnType("character varying(20)") + .HasColumnName("group_gid"); + + b.Property("Invite") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("invite"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_date"); + + b.HasKey("GroupGID", "Invite") + .HasName("pk_group_temp_invites"); + + b.HasIndex("GroupGID") + .HasDatabaseName("ix_group_temp_invites_group_gid"); + + b.HasIndex("Invite") + .HasDatabaseName("ix_group_temp_invites_invite"); + + b.ToTable("group_temp_invites", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b => + { + b.Property("DiscordId") + .ValueGeneratedOnAdd() + .HasColumnType("numeric(20,0)") + .HasColumnName("discord_id"); + + b.Property("HashedLodestoneId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("hashed_lodestone_id"); + + b.Property("LodestoneAuthString") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("lodestone_auth_string"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.Property("UserUID") + .HasColumnType("character varying(10)") + .HasColumnName("user_uid"); + + b.HasKey("DiscordId") + .HasName("pk_lodestone_auth"); + + b.HasIndex("UserUID") + .HasDatabaseName("ix_lodestone_auth_user_uid"); + + b.ToTable("lodestone_auth", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.User", b => + { + b.Property("UID") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("uid"); + + b.Property("Alias") + .HasMaxLength(15) + .HasColumnType("character varying(15)") + .HasColumnName("alias"); + + b.Property("IsAdmin") + .HasColumnType("boolean") + .HasColumnName("is_admin"); + + b.Property("IsModerator") + .HasColumnType("boolean") + .HasColumnName("is_moderator"); + + b.Property("LastLoggedIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_logged_in"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasColumnName("timestamp"); + + b.HasKey("UID") + .HasName("pk_users"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.Auth", b => + { + b.HasOne("MareSynchronosShared.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID") + .HasConstraintName("fk_auth_users_user_temp_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b => + { + b.HasOne("MareSynchronosShared.Models.User", "OtherUser") + .WithMany() + .HasForeignKey("OtherUserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_client_pairs_users_other_user_temp_id1"); + + b.HasOne("MareSynchronosShared.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_client_pairs_users_user_temp_id2"); + + b.Navigation("OtherUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b => + { + b.HasOne("MareSynchronosShared.Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderUID") + .HasConstraintName("fk_file_caches_users_uploader_uid"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.Group", b => + { + b.HasOne("MareSynchronosShared.Models.User", "Owner") + .WithMany() + .HasForeignKey("OwnerUID") + .HasConstraintName("fk_groups_users_owner_temp_id7"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b => + { + b.HasOne("MareSynchronosShared.Models.User", "BannedBy") + .WithMany() + .HasForeignKey("BannedByUID") + .HasConstraintName("fk_group_bans_users_banned_by_temp_id4"); + + b.HasOne("MareSynchronosShared.Models.User", "BannedUser") + .WithMany() + .HasForeignKey("BannedUserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_group_bans_users_banned_user_temp_id5"); + + b.HasOne("MareSynchronosShared.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupGID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_group_bans_groups_group_temp_id"); + + b.Navigation("BannedBy"); + + b.Navigation("BannedUser"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b => + { + b.HasOne("MareSynchronosShared.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupGID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_group_pairs_groups_group_temp_id1"); + + b.HasOne("MareSynchronosShared.Models.User", "GroupUser") + .WithMany() + .HasForeignKey("GroupUserUID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_group_pairs_users_group_user_temp_id6"); + + b.Navigation("Group"); + + b.Navigation("GroupUser"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b => + { + b.HasOne("MareSynchronosShared.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupGID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_group_temp_invites_groups_group_gid"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b => + { + b.HasOne("MareSynchronosShared.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID") + .HasConstraintName("fk_lodestone_auth_users_user_uid"); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.cs new file mode 100644 index 0000000..0729490 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/20230126163758_GroupPerms.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + /// + public partial class GroupPerms : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "disable_animations", + table: "groups", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "disable_sounds", + table: "groups", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "disable_animations", + table: "group_pairs", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "disable_sounds", + table: "group_pairs", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "disable_animations", + table: "groups"); + + migrationBuilder.DropColumn( + name: "disable_sounds", + table: "groups"); + + migrationBuilder.DropColumn( + name: "disable_animations", + table: "group_pairs"); + + migrationBuilder.DropColumn( + name: "disable_sounds", + table: "group_pairs"); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs b/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs index 776462c..e51dece 100644 --- a/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs +++ b/MareSynchronosServer/MareSynchronosShared/Migrations/MareDbContextModelSnapshot.cs @@ -195,6 +195,14 @@ namespace MareSynchronosServer.Migrations .HasColumnType("character varying(50)") .HasColumnName("alias"); + b.Property("DisableAnimations") + .HasColumnType("boolean") + .HasColumnName("disable_animations"); + + b.Property("DisableSounds") + .HasColumnType("boolean") + .HasColumnName("disable_sounds"); + b.Property("HashedPassword") .HasColumnType("text") .HasColumnName("hashed_password"); @@ -263,6 +271,14 @@ namespace MareSynchronosServer.Migrations .HasColumnType("character varying(10)") .HasColumnName("group_user_uid"); + b.Property("DisableAnimations") + .HasColumnType("boolean") + .HasColumnName("disable_animations"); + + b.Property("DisableSounds") + .HasColumnType("boolean") + .HasColumnName("disable_sounds"); + b.Property("IsModerator") .HasColumnType("boolean") .HasColumnName("is_moderator"); diff --git a/MareSynchronosServer/MareSynchronosShared/Models/Group.cs b/MareSynchronosServer/MareSynchronosShared/Models/Group.cs index b5f27e4..18fab03 100644 --- a/MareSynchronosServer/MareSynchronosShared/Models/Group.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/Group.cs @@ -13,4 +13,6 @@ public class Group public string Alias { get; set; } public bool InvitesEnabled { get; set; } public string HashedPassword { get; set; } + public bool DisableSounds { get; set; } + public bool DisableAnimations { get; set; } } diff --git a/MareSynchronosServer/MareSynchronosShared/Models/GroupPair.cs b/MareSynchronosServer/MareSynchronosShared/Models/GroupPair.cs index 873b1dc..b75e143 100644 --- a/MareSynchronosServer/MareSynchronosShared/Models/GroupPair.cs +++ b/MareSynchronosServer/MareSynchronosShared/Models/GroupPair.cs @@ -9,4 +9,6 @@ public class GroupPair public bool IsPaused { get; set; } public bool IsPinned { get; set; } public bool IsModerator { get; set; } + public bool DisableSounds { get; set; } + public bool DisableAnimations { get; set; } } diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/CacheController.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/CacheController.cs index a8669cb..b058c1d 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/CacheController.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/CacheController.cs @@ -1,4 +1,4 @@ -using MareSynchronos.API; +using MareSynchronos.API.Routes; using MareSynchronosShared.Utils; using MareSynchronosStaticFilesServer.Services; using MareSynchronosStaticFilesServer.Utils; diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/RequestController.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/RequestController.cs index 81c3872..17b7f64 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/RequestController.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/RequestController.cs @@ -1,4 +1,4 @@ -using MareSynchronos.API; +using MareSynchronos.API.Routes; using MareSynchronosShared.Utils; using MareSynchronosStaticFilesServer.Services; using Microsoft.AspNetCore.Mvc; diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/ServerFilesController.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/ServerFilesController.cs index 313eb73..7d38da6 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/ServerFilesController.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Controllers/ServerFilesController.cs @@ -1,4 +1,4 @@ -using MareSynchronos.API; +using MareSynchronos.API.Routes; using MareSynchronosShared.Utils; using MareSynchronosStaticFilesServer.Services; using Microsoft.AspNetCore.Authorization; diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/CachedFileProvider.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/CachedFileProvider.cs index 0508f12..dbd7ac8 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/CachedFileProvider.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/CachedFileProvider.cs @@ -3,8 +3,8 @@ using MareSynchronosShared.Services; using MareSynchronosStaticFilesServer.Utils; using System.Collections.Concurrent; using System.Net.Http.Headers; -using MareSynchronos.API; using MareSynchronosShared.Utils; +using MareSynchronos.API.Routes; namespace MareSynchronosStaticFilesServer.Services; diff --git a/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/RequestQueueService.cs b/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/RequestQueueService.cs index 200e88e..d1ca796 100644 --- a/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/RequestQueueService.cs +++ b/MareSynchronosServer/MareSynchronosStaticFilesServer/Services/RequestQueueService.cs @@ -1,10 +1,11 @@ -using MareSynchronos.API; +using MareSynchronos.API.Routes; using MareSynchronosShared.Metrics; using MareSynchronosShared.Services; using MareSynchronosStaticFilesServer.Utils; using Microsoft.AspNetCore.SignalR; using System.Collections.Concurrent; using System.Timers; +using MareSynchronos.API.SignalR; namespace MareSynchronosStaticFilesServer.Services;