add redis for character identification
This commit is contained in:
		| @@ -16,8 +16,8 @@ namespace MareSynchronosServer.Hubs | |||||||
|  |  | ||||||
|         private bool IsModerator => _dbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsModerator || IsAdmin; |         private bool IsModerator => _dbContext.Users.Single(b => b.UID == AuthenticatedUserId).IsModerator || IsAdmin; | ||||||
|  |  | ||||||
|         private List<string> OnlineAdmins => _dbContext.Users.Where(u => !string.IsNullOrEmpty(u.CharacterIdentification) && (u.IsModerator || u.IsAdmin)) |         private List<string> OnlineAdmins => _dbContext.Users.Where(u => (u.IsModerator || u.IsAdmin)).Select(u => u.UID).ToList(); | ||||||
|                                 .Select(u => u.UID).ToList(); |  | ||||||
|         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] |         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] | ||||||
|         [HubMethodName(Api.SendAdminChangeModeratorStatus)] |         [HubMethodName(Api.SendAdminChangeModeratorStatus)] | ||||||
|         public async Task ChangeModeratorStatus(string uid, bool isModerator) |         public async Task ChangeModeratorStatus(string uid, bool isModerator) | ||||||
| @@ -101,13 +101,14 @@ namespace MareSynchronosServer.Hubs | |||||||
|         { |         { | ||||||
|             if (!IsModerator) return null; |             if (!IsModerator) return null; | ||||||
|  |  | ||||||
|             return await _dbContext.Users.AsNoTracking().Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto |             var users = await _dbContext.Users.AsNoTracking().ToListAsync().ConfigureAwait(false); | ||||||
|  |             return users.Where(c => !string.IsNullOrEmpty(_clientIdentService.GetCharacterIdentForUid(c.UID))).Select(b => new OnlineUserDto | ||||||
|             { |             { | ||||||
|                 CharacterNameHash = b.CharacterIdentification, |                 CharacterNameHash = _clientIdentService.GetCharacterIdentForUid(b.UID), | ||||||
|                 UID = b.UID, |                 UID = b.UID, | ||||||
|                 IsModerator = b.IsModerator, |                 IsModerator = b.IsModerator, | ||||||
|                 IsAdmin = b.IsAdmin |                 IsAdmin = b.IsAdmin | ||||||
|             }).ToListAsync().ConfigureAwait(false); |             }).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] |         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] | ||||||
| @@ -134,11 +135,10 @@ namespace MareSynchronosServer.Hubs | |||||||
|  |  | ||||||
|             await _dbContext.SaveChangesAsync().ConfigureAwait(false); |             await _dbContext.SaveChangesAsync().ConfigureAwait(false); | ||||||
|             await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto).ConfigureAwait(false); |             await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto).ConfigureAwait(false); | ||||||
|             var bannedUser = |             var bannedUser = _clientIdentService.GetUidForCharacterIdent(dto.CharacterHash); | ||||||
|                 await _dbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false); |             if (!string.IsNullOrEmpty(bannedUser)) | ||||||
|             if (bannedUser != null) |  | ||||||
|             { |             { | ||||||
|                 await Clients.User(bannedUser.UID).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false); |                 await Clients.User(bannedUser).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ namespace MareSynchronosServer.Hubs | |||||||
|                     IsForbidden = forbiddenFile != null, |                     IsForbidden = forbiddenFile != null, | ||||||
|                     Hash = hash.Key, |                     Hash = hash.Key, | ||||||
|                     Size = hash.Value, |                     Size = hash.Value, | ||||||
|                     Url = new Uri(cdnFullUri, hash.Key.ToUpperInvariant()).ToString() |                     Url = new Uri(_cdnFullUri, hash.Key.ToUpperInvariant()).ToString() | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ namespace MareSynchronosServer.Hubs | |||||||
|         { |         { | ||||||
|             _logger.LogInformation("User {AuthenticatedUserId} deleted their account", AuthenticatedUserId); |             _logger.LogInformation("User {AuthenticatedUserId} deleted their account", AuthenticatedUserId); | ||||||
|  |  | ||||||
|  |  | ||||||
|             string userid = AuthenticatedUserId; |             string userid = AuthenticatedUserId; | ||||||
|             var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid).ConfigureAwait(false); |             var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == userid).ConfigureAwait(false); | ||||||
|  |             var charaIdent = _clientIdentService.GetCharacterIdentForUid(userid); | ||||||
|             var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == userid).ToListAsync().ConfigureAwait(false); |             var ownPairData = await _dbContext.ClientPairs.Where(u => u.User.UID == userid).ToListAsync().ConfigureAwait(false); | ||||||
|             var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == userid).ConfigureAwait(false); |             var auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == userid).ConfigureAwait(false); | ||||||
|             var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == userid).ConfigureAwait(false); |             var lodestone = await _dbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == userid).ConfigureAwait(false); | ||||||
| @@ -52,7 +52,7 @@ namespace MareSynchronosServer.Hubs | |||||||
|                     { |                     { | ||||||
|                         OtherUID = userid, |                         OtherUID = userid, | ||||||
|                         IsRemoved = true |                         IsRemoved = true | ||||||
|                     }, userEntry.CharacterIdentification).ConfigureAwait(false); |                     }, charaIdent).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             _mareMetrics.DecGauge(MetricsAPI.GaugePairs, ownPairData.Count + otherPairData.Count); |             _mareMetrics.DecGauge(MetricsAPI.GaugePairs, ownPairData.Count + otherPairData.Count); | ||||||
| @@ -77,14 +77,18 @@ namespace MareSynchronosServer.Hubs | |||||||
|                 .Include(u => u.User) |                 .Include(u => u.User) | ||||||
|                 .Include(u => u.OtherUser) |                 .Include(u => u.OtherUser) | ||||||
|                 .Where(w => w.User.UID == ownUser.UID && !w.IsPaused) |                 .Where(w => w.User.UID == ownUser.UID && !w.IsPaused) | ||||||
|                 .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) |                 //.Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) | ||||||
|                 .Select(e => e.OtherUser).ToListAsync().ConfigureAwait(false); |                 .Select(e => e.OtherUser).ToListAsync().ConfigureAwait(false); | ||||||
|  |             var otherOnlineUsers = | ||||||
|  |                 otherUsers.Where(u => !string.IsNullOrEmpty(_clientIdentService.GetCharacterIdentForUid(u.UID))); | ||||||
|             var otherEntries = await _dbContext.ClientPairs.AsNoTracking() |             var otherEntries = await _dbContext.ClientPairs.AsNoTracking() | ||||||
|                 .Include(u => u.User) |                 .Include(u => u.User) | ||||||
|                 .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync().ConfigureAwait(false); |                 .Where(u => otherOnlineUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused) | ||||||
|  |                 .ToListAsync().ConfigureAwait(false); | ||||||
|  |             var ownIdent = _clientIdentService.GetCharacterIdentForUid(ownUser.UID); | ||||||
|  |  | ||||||
|             await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownUser.CharacterIdentification).ConfigureAwait(false); |             await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownIdent).ConfigureAwait(false); | ||||||
|             return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList(); |             return otherEntries.Select(e => _clientIdentService.GetCharacterIdentForUid(e.User.UID)).Distinct().ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] |         [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] | ||||||
| @@ -152,12 +156,14 @@ namespace MareSynchronosServer.Hubs | |||||||
|                     userToOther.UserUID == user.UID |                     userToOther.UserUID == user.UID | ||||||
|                     && !userToOther.IsPaused |                     && !userToOther.IsPaused | ||||||
|                     && !otherToUser.IsPaused |                     && !otherToUser.IsPaused | ||||||
|                     && visibleCharacterIds.Contains(userToOther.OtherUser.CharacterIdentification) |  | ||||||
|                 select otherToUser.UserUID; |                 select otherToUser.UserUID; | ||||||
|  |  | ||||||
|             var otherEntries = await query.ToListAsync().ConfigureAwait(false); |             var otherEntries = await query.ToListAsync().ConfigureAwait(false); | ||||||
|  |             otherEntries = | ||||||
|  |                 otherEntries.Where(c => !string.IsNullOrEmpty(_clientIdentService.GetCharacterIdentForUid(c))).ToList(); | ||||||
|  |             var ownIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); | ||||||
|  |  | ||||||
|             await Clients.Users(otherEntries).SendAsync(Api.OnUserReceiveCharacterData, characterCache, user.CharacterIdentification).ConfigureAwait(false); |             await Clients.Users(otherEntries).SendAsync(Api.OnUserReceiveCharacterData, characterCache, ownIdent).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             _mareMetrics.IncCounter(MetricsAPI.CounterUserPushData); |             _mareMetrics.IncCounter(MetricsAPI.CounterUserPushData); | ||||||
|             _mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, otherEntries.Count); |             _mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, otherEntries.Count); | ||||||
| @@ -199,6 +205,7 @@ namespace MareSynchronosServer.Hubs | |||||||
|                 }, string.Empty).ConfigureAwait(false); |                 }, string.Empty).ConfigureAwait(false); | ||||||
|             if (otherEntry != null) |             if (otherEntry != null) | ||||||
|             { |             { | ||||||
|  |                 var userIdent = _clientIdentService.GetCharacterIdentForUid(user.UID); | ||||||
|                 await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, |                 await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, | ||||||
|                     new ClientPairDto() |                     new ClientPairDto() | ||||||
|                     { |                     { | ||||||
| @@ -207,14 +214,15 @@ namespace MareSynchronosServer.Hubs | |||||||
|                         IsPaused = otherEntry.IsPaused, |                         IsPaused = otherEntry.IsPaused, | ||||||
|                         IsPausedFromOthers = false, |                         IsPausedFromOthers = false, | ||||||
|                         IsSynced = true |                         IsSynced = true | ||||||
|                     }, user.CharacterIdentification).ConfigureAwait(false); |                     }, userIdent).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) |                 var otherIdent = _clientIdentService.GetCharacterIdentForUid(otherUser.UID); | ||||||
|  |                 if (!string.IsNullOrEmpty(otherIdent)) | ||||||
|                 { |                 { | ||||||
|                     await Clients.User(user.UID) |                     await Clients.User(user.UID) | ||||||
|                         .SendAsync(Api.OnUserAddOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false); |                         .SendAsync(Api.OnUserAddOnlinePairedPlayer, otherIdent).ConfigureAwait(false); | ||||||
|                     await Clients.User(otherUser.UID) |                     await Clients.User(otherUser.UID) | ||||||
|                         .SendAsync(Api.OnUserAddOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false); |                         .SendAsync(Api.OnUserAddOnlinePairedPlayer, userIdent).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -233,8 +241,8 @@ namespace MareSynchronosServer.Hubs | |||||||
|             pair.IsPaused = isPaused; |             pair.IsPaused = isPaused; | ||||||
|             _dbContext.Update(pair); |             _dbContext.Update(pair); | ||||||
|             await _dbContext.SaveChangesAsync().ConfigureAwait(false); |             await _dbContext.SaveChangesAsync().ConfigureAwait(false); | ||||||
|             var selfCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false)).CharacterIdentification; |             var selfCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); | ||||||
|             var otherCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == otherUserUid).ConfigureAwait(false)).CharacterIdentification; |             var otherCharaIdent = _clientIdentService.GetCharacterIdentForUid(pair.OtherUserUID); | ||||||
|             var otherEntry = OppositeEntry(otherUserUid); |             var otherEntry = OppositeEntry(otherUserUid); | ||||||
|  |  | ||||||
|             await Clients.User(AuthenticatedUserId) |             await Clients.User(AuthenticatedUserId) | ||||||
| @@ -282,27 +290,29 @@ namespace MareSynchronosServer.Hubs | |||||||
|             _dbContext.ClientPairs.Remove(wl); |             _dbContext.ClientPairs.Remove(wl); | ||||||
|             await _dbContext.SaveChangesAsync().ConfigureAwait(false); |             await _dbContext.SaveChangesAsync().ConfigureAwait(false); | ||||||
|             var otherEntry = OppositeEntry(uid); |             var otherEntry = OppositeEntry(uid); | ||||||
|  |             var otherIdent = _clientIdentService.GetCharacterIdentForUid(otherUser.UID); | ||||||
|             await Clients.User(sender.UID) |             await Clients.User(sender.UID) | ||||||
|                 .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() |                 .SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() | ||||||
|                 { |                 { | ||||||
|                     OtherUID = otherUser.UID, |                     OtherUID = otherUser.UID, | ||||||
|                     IsRemoved = true |                     IsRemoved = true | ||||||
|                 }, otherUser.CharacterIdentification).ConfigureAwait(false); |                 }, otherIdent).ConfigureAwait(false); | ||||||
|             if (otherEntry != null) |             if (otherEntry != null) | ||||||
|             { |             { | ||||||
|                 if (!string.IsNullOrEmpty(otherUser.CharacterIdentification)) |                 if (!string.IsNullOrEmpty(otherIdent)) | ||||||
|                 { |                 { | ||||||
|  |                     var ownIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); | ||||||
|                     await Clients.User(sender.UID) |                     await Clients.User(sender.UID) | ||||||
|                         .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false); |                         .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherIdent).ConfigureAwait(false); | ||||||
|                     await Clients.User(otherUser.UID) |                     await Clients.User(otherUser.UID) | ||||||
|                         .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, sender.CharacterIdentification).ConfigureAwait(false); |                         .SendAsync(Api.OnUserRemoveOnlinePairedPlayer, ownIdent).ConfigureAwait(false); | ||||||
|                     await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() |                     await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto() | ||||||
|                     { |                     { | ||||||
|                         OtherUID = sender.UID, |                         OtherUID = sender.UID, | ||||||
|                         IsPaused = otherEntry.IsPaused, |                         IsPaused = otherEntry.IsPaused, | ||||||
|                         IsPausedFromOthers = false, |                         IsPausedFromOthers = false, | ||||||
|                         IsSynced = false |                         IsSynced = false | ||||||
|                     }, sender.CharacterIdentification).ConfigureAwait(false); |                     }, ownIdent).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,11 +3,13 @@ using System.Linq; | |||||||
| using System.Security.Claims; | using System.Security.Claims; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using MareSynchronos.API; | using MareSynchronos.API; | ||||||
|  | using MareSynchronosServer.Services; | ||||||
| using MareSynchronosShared.Authentication; | using MareSynchronosShared.Authentication; | ||||||
| using MareSynchronosShared.Data; | using MareSynchronosShared.Data; | ||||||
| using MareSynchronosShared.Metrics; | using MareSynchronosShared.Metrics; | ||||||
| using MareSynchronosShared.Models; | using MareSynchronosShared.Models; | ||||||
| using MareSynchronosShared.Protos; | using MareSynchronosShared.Protos; | ||||||
|  | using MareSynchronosShared.Services; | ||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.AspNetCore.Http; | using Microsoft.AspNetCore.Http; | ||||||
| using Microsoft.AspNetCore.SignalR; | using Microsoft.AspNetCore.SignalR; | ||||||
| @@ -15,27 +17,30 @@ using Microsoft.EntityFrameworkCore; | |||||||
| using Microsoft.Extensions.Configuration; | using Microsoft.Extensions.Configuration; | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
| namespace MareSynchronosServer.Hubs | namespace MareSynchronosServer.Hubs; | ||||||
| { |  | ||||||
| public partial class MareHub : Hub | public partial class MareHub : Hub | ||||||
| { | { | ||||||
|     private readonly MareMetrics _mareMetrics; |     private readonly MareMetrics _mareMetrics; | ||||||
|     private readonly AuthService.AuthServiceClient _authServiceClient; |     private readonly AuthService.AuthServiceClient _authServiceClient; | ||||||
|     private readonly FileService.FileServiceClient _fileServiceClient; |     private readonly FileService.FileServiceClient _fileServiceClient; | ||||||
|     private readonly SystemInfoService _systemInfoService; |     private readonly SystemInfoService _systemInfoService; | ||||||
|         private readonly IHttpContextAccessor contextAccessor; |     private readonly IHttpContextAccessor _contextAccessor; | ||||||
|  |     private readonly IClientIdentificationService _clientIdentService; | ||||||
|     private readonly ILogger<MareHub> _logger; |     private readonly ILogger<MareHub> _logger; | ||||||
|     private readonly MareDbContext _dbContext; |     private readonly MareDbContext _dbContext; | ||||||
|         private readonly Uri cdnFullUri; |     private readonly Uri _cdnFullUri; | ||||||
|     public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient, |     public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient, | ||||||
|             MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor) |         MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor, | ||||||
|  |         IClientIdentificationService clientIdentService) | ||||||
|     { |     { | ||||||
|         _mareMetrics = mareMetrics; |         _mareMetrics = mareMetrics; | ||||||
|         _authServiceClient = authServiceClient; |         _authServiceClient = authServiceClient; | ||||||
|         _fileServiceClient = fileServiceClient; |         _fileServiceClient = fileServiceClient; | ||||||
|         _systemInfoService = systemInfoService; |         _systemInfoService = systemInfoService; | ||||||
|             cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl")); |         _cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl")); | ||||||
|             this.contextAccessor = contextAccessor; |         _contextAccessor = contextAccessor; | ||||||
|  |         _clientIdentService = clientIdentService; | ||||||
|         _logger = logger; |         _logger = logger; | ||||||
|         _dbContext = mareDbContext; |         _dbContext = mareDbContext; | ||||||
|     } |     } | ||||||
| @@ -57,20 +62,17 @@ namespace MareSynchronosServer.Hubs | |||||||
|         if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification)) |         if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification)) | ||||||
|         { |         { | ||||||
|             var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false)); |             var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false)); | ||||||
|                 if (!string.IsNullOrEmpty(user.CharacterIdentification) && characterIdentification != user.CharacterIdentification) |             var existingIdent = _clientIdentService.GetCharacterIdentForUid(userId); | ||||||
|  |             if (!string.IsNullOrEmpty(existingIdent) && characterIdentification != existingIdent) | ||||||
|             { |             { | ||||||
|                 return new ConnectionDto() |                 return new ConnectionDto() | ||||||
|                 { |                 { | ||||||
|                     ServerVersion = Api.Version |                     ServerVersion = Api.Version | ||||||
|                 }; |                 }; | ||||||
|             } |             } | ||||||
|                 else if (string.IsNullOrEmpty(user.CharacterIdentification)) |  | ||||||
|                 { |  | ||||||
|                     _mareMetrics.IncGauge(MetricsAPI.GaugeAuthorizedConnections); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|             user.LastLoggedIn = DateTime.UtcNow; |             user.LastLoggedIn = DateTime.UtcNow; | ||||||
|                 user.CharacterIdentification = characterIdentification; |             _clientIdentService.MarkUserOnline(user.UID, characterIdentification); | ||||||
|             await _dbContext.SaveChangesAsync().ConfigureAwait(false); |             await _dbContext.SaveChangesAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|             return new ConnectionDto |             return new ConnectionDto | ||||||
| @@ -90,7 +92,7 @@ namespace MareSynchronosServer.Hubs | |||||||
|  |  | ||||||
|     public override async Task OnConnectedAsync() |     public override async Task OnConnectedAsync() | ||||||
|     { |     { | ||||||
|             _logger.LogInformation("Connection from {ip}", contextAccessor.GetIpAddress()); |         _logger.LogInformation("Connection from {ip}", _contextAccessor.GetIpAddress()); | ||||||
|         _mareMetrics.IncGauge(MetricsAPI.GaugeConnections); |         _mareMetrics.IncGauge(MetricsAPI.GaugeConnections); | ||||||
|         await base.OnConnectedAsync().ConfigureAwait(false); |         await base.OnConnectedAsync().ConfigureAwait(false); | ||||||
|     } |     } | ||||||
| @@ -99,9 +101,11 @@ namespace MareSynchronosServer.Hubs | |||||||
|     { |     { | ||||||
|         _mareMetrics.DecGauge(MetricsAPI.GaugeConnections); |         _mareMetrics.DecGauge(MetricsAPI.GaugeConnections); | ||||||
|  |  | ||||||
|             var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); |         var userCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId); | ||||||
|             if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) |  | ||||||
|  |         if (!string.IsNullOrEmpty(userCharaIdent)) | ||||||
|         { |         { | ||||||
|  |             var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId)!.ConfigureAwait(false); | ||||||
|             _mareMetrics.DecGauge(MetricsAPI.GaugeAuthorizedConnections); |             _mareMetrics.DecGauge(MetricsAPI.GaugeAuthorizedConnections); | ||||||
|  |  | ||||||
|             _logger.LogInformation("Disconnect from {id}", AuthenticatedUserId); |             _logger.LogInformation("Disconnect from {id}", AuthenticatedUserId); | ||||||
| @@ -126,11 +130,11 @@ namespace MareSynchronosServer.Hubs | |||||||
|                 select otherToUser.UserUID; |                 select otherToUser.UserUID; | ||||||
|             var otherEntries = await query.ToListAsync().ConfigureAwait(false); |             var otherEntries = await query.ToListAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false); |             await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, userCharaIdent).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             _dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID)); |             _dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID)); | ||||||
|  |  | ||||||
|                 user.CharacterIdentification = null; |             _clientIdentService.MarkUserOffline(user.UID); | ||||||
|             await _dbContext.SaveChangesAsync().ConfigureAwait(false); |             await _dbContext.SaveChangesAsync().ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -144,4 +148,3 @@ namespace MareSynchronosServer.Hubs | |||||||
|         return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); |         return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| } |  | ||||||
|   | |||||||
| @@ -29,11 +29,6 @@ namespace MareSynchronosServer | |||||||
|                     context.SaveChanges(); |                     context.SaveChanges(); | ||||||
|  |  | ||||||
|                     // clean up residuals |                     // clean up residuals | ||||||
|                     var users = context.Users; |  | ||||||
|                     foreach (var user in users) |  | ||||||
|                     { |  | ||||||
|                         user.CharacterIdentification = null; |  | ||||||
|                     } |  | ||||||
|                     var looseFiles = context.Files.Where(f => f.Uploaded == false); |                     var looseFiles = context.Files.Where(f => f.Uploaded == false); | ||||||
|                     var unfinishedRegistrations = context.LodeStoneAuth.Where(c => c.StartedAt != null); |                     var unfinishedRegistrations = context.LodeStoneAuth.Where(c => c.StartedAt != null); | ||||||
|                     context.RemoveRange(unfinishedRegistrations); |                     context.RemoveRange(unfinishedRegistrations); | ||||||
|   | |||||||
| @@ -1,32 +1,32 @@ | |||||||
| using System; | using System; | ||||||
| using System.Linq; |  | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using MareSynchronos.API; | using MareSynchronos.API; | ||||||
| using MareSynchronosServer.Hubs; | using MareSynchronosServer.Hubs; | ||||||
| using MareSynchronosShared.Data; | using MareSynchronosShared.Data; | ||||||
| using MareSynchronosShared.Metrics; | using MareSynchronosShared.Metrics; | ||||||
| using MareSynchronosShared.Models; | using MareSynchronosShared.Services; | ||||||
| using MareSynchronosShared.Protos; |  | ||||||
| using Microsoft.AspNetCore.SignalR; | using Microsoft.AspNetCore.SignalR; | ||||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||||
| using Microsoft.Extensions.Hosting; | using Microsoft.Extensions.Hosting; | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
| 
 | 
 | ||||||
| namespace MareSynchronosServer; | namespace MareSynchronosServer.Services; | ||||||
| 
 | 
 | ||||||
| public class SystemInfoService : IHostedService, IDisposable | public class SystemInfoService : IHostedService, IDisposable | ||||||
| { | { | ||||||
|     private readonly MareMetrics _mareMetrics; |     private readonly MareMetrics _mareMetrics; | ||||||
|  |     private readonly IClientIdentificationService clientIdentService; | ||||||
|     private readonly IServiceProvider _services; |     private readonly IServiceProvider _services; | ||||||
|     private readonly ILogger<SystemInfoService> _logger; |     private readonly ILogger<SystemInfoService> _logger; | ||||||
|     private readonly IHubContext<MareHub> _hubContext; |     private readonly IHubContext<MareHub> _hubContext; | ||||||
|     private Timer _timer; |     private Timer _timer; | ||||||
|     public SystemInfoDto SystemInfoDto { get; private set; } = new(); |     public SystemInfoDto SystemInfoDto { get; private set; } = new(); | ||||||
| 
 | 
 | ||||||
|     public SystemInfoService(MareMetrics mareMetrics, IServiceProvider services, ILogger<SystemInfoService> logger, IHubContext<MareHub> hubContext) |     public SystemInfoService(MareMetrics mareMetrics, IClientIdentificationService clientIdentService, IServiceProvider services, ILogger<SystemInfoService> logger, IHubContext<MareHub> hubContext) | ||||||
|     { |     { | ||||||
|         _mareMetrics = mareMetrics; |         _mareMetrics = mareMetrics; | ||||||
|  |         this.clientIdentService = clientIdentService; | ||||||
|         _services = services; |         _services = services; | ||||||
|         _logger = logger; |         _logger = logger; | ||||||
|         _hubContext = hubContext; |         _hubContext = hubContext; | ||||||
| @@ -56,8 +56,6 @@ public class SystemInfoService : IHostedService, IDisposable | |||||||
|             using var scope = _services.CreateScope(); |             using var scope = _services.CreateScope(); | ||||||
|             using var db = scope.ServiceProvider.GetService<MareDbContext>()!; |             using var db = scope.ServiceProvider.GetService<MareDbContext>()!; | ||||||
| 
 | 
 | ||||||
|             var users = db.Users.Count(c => c.CharacterIdentification != null); |  | ||||||
| 
 |  | ||||||
|             SystemInfoDto = new SystemInfoDto() |             SystemInfoDto = new SystemInfoDto() | ||||||
|             { |             { | ||||||
|                 CacheUsage = 0, |                 CacheUsage = 0, | ||||||
| @@ -65,7 +63,7 @@ public class SystemInfoService : IHostedService, IDisposable | |||||||
|                 RAMUsage = 0, |                 RAMUsage = 0, | ||||||
|                 NetworkIn = 0, |                 NetworkIn = 0, | ||||||
|                 NetworkOut = 0, |                 NetworkOut = 0, | ||||||
|                 OnlineUsers = users, |                 OnlineUsers = clientIdentService.GetOnlineUsers(), | ||||||
|                 UploadedFiles = 0 |                 UploadedFiles = 0 | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
| @@ -19,6 +19,8 @@ using Grpc.Net.Client.Configuration; | |||||||
| using Prometheus; | using Prometheus; | ||||||
| using MareSynchronosShared.Metrics; | using MareSynchronosShared.Metrics; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using MareSynchronosServer.Services; | ||||||
|  | using MareSynchronosShared.Services; | ||||||
|  |  | ||||||
| namespace MareSynchronosServer | namespace MareSynchronosServer | ||||||
| { | { | ||||||
| @@ -101,18 +103,18 @@ namespace MareSynchronosServer | |||||||
|                 options.EnableThreadSafetyChecks(false); |                 options.EnableThreadSafetyChecks(false); | ||||||
|             }, mareConfig.GetValue("DbContextPoolSize", 1024)); |             }, mareConfig.GetValue("DbContextPoolSize", 1024)); | ||||||
|  |  | ||||||
|  |              | ||||||
|             services.AddHostedService(provider => provider.GetService<SystemInfoService>()); |             services.AddHostedService(provider => provider.GetService<SystemInfoService>()); | ||||||
|  |  | ||||||
|             services.AddAuthentication(options => |             services.AddAuthentication(options => | ||||||
|             { |             { | ||||||
|                 options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; |                 options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; | ||||||
|                 }) |             }).AddScheme<AuthenticationSchemeOptions, SecretKeyGrpcAuthenticationHandler>(SecretKeyGrpcAuthenticationHandler.AuthScheme, _ => { }); | ||||||
|                 .AddScheme<AuthenticationSchemeOptions, SecretKeyGrpcAuthenticationHandler>(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { }); |  | ||||||
|             services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); |             services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); | ||||||
|  |  | ||||||
|             services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); |             services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); | ||||||
|  |  | ||||||
|             var signalRserviceBuilder = services.AddSignalR(hubOptions => |             var signalRServiceBuilder = services.AddSignalR(hubOptions => | ||||||
|             { |             { | ||||||
|                 hubOptions.MaximumReceiveMessageSize = long.MaxValue; |                 hubOptions.MaximumReceiveMessageSize = long.MaxValue; | ||||||
|                 hubOptions.EnableDetailedErrors = true; |                 hubOptions.EnableDetailedErrors = true; | ||||||
| @@ -120,13 +122,28 @@ namespace MareSynchronosServer | |||||||
|                 hubOptions.StreamBufferCapacity = 200; |                 hubOptions.StreamBufferCapacity = 200; | ||||||
|                 hubOptions.AddFilter<SignalRLimitFilter>(); |                 hubOptions.AddFilter<SignalRLimitFilter>(); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|  |             // add redis related options | ||||||
|             var redis = mareConfig.GetValue("RedisConnectionString", string.Empty); |             var redis = mareConfig.GetValue("RedisConnectionString", string.Empty); | ||||||
|             if (!string.IsNullOrEmpty(redis)) |             if (!string.IsNullOrEmpty(redis)) | ||||||
|             { |             { | ||||||
|                 signalRserviceBuilder.AddStackExchangeRedis(redis, options => |                 signalRServiceBuilder.AddStackExchangeRedis(redis, options => | ||||||
|                 { |                 { | ||||||
|                     options.Configuration.ChannelPrefix = "MareSynchronos"; |                     options.Configuration.ChannelPrefix = "MareSynchronos"; | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  |                 services.AddStackExchangeRedisCache(opt => | ||||||
|  |                 { | ||||||
|  |                     opt.Configuration = redis; | ||||||
|  |                     opt.InstanceName = "MareSynchronos"; | ||||||
|  |                 }); | ||||||
|  |                 services.AddSingleton<IClientIdentificationService, DistributedClientIdentificationService>(); | ||||||
|  |                 services.AddHostedService(p => p.GetService<DistributedClientIdentificationService>()); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 services.AddSingleton<IClientIdentificationService, LocalClientIdentificationService>(); | ||||||
|  |                 services.AddHostedService(p => p.GetService<LocalClientIdentificationService>()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ using MareSynchronosServices.Authentication; | |||||||
| using MareSynchronosShared.Data; | using MareSynchronosShared.Data; | ||||||
| using MareSynchronosShared.Metrics; | using MareSynchronosShared.Metrics; | ||||||
| using MareSynchronosShared.Models; | using MareSynchronosShared.Models; | ||||||
|  | using MareSynchronosShared.Services; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| using Microsoft.Extensions.Configuration; | using Microsoft.Extensions.Configuration; | ||||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||||
| @@ -26,6 +27,7 @@ public class DiscordBot : IHostedService | |||||||
| { | { | ||||||
|     private readonly CleanupService cleanupService; |     private readonly CleanupService cleanupService; | ||||||
|     private readonly MareMetrics metrics; |     private readonly MareMetrics metrics; | ||||||
|  |     private readonly IClientIdentificationService clientService; | ||||||
|     private readonly IServiceProvider services; |     private readonly IServiceProvider services; | ||||||
|     private readonly IConfiguration _configuration; |     private readonly IConfiguration _configuration; | ||||||
|     private readonly ILogger<DiscordBot> logger; |     private readonly ILogger<DiscordBot> logger; | ||||||
| @@ -44,10 +46,11 @@ public class DiscordBot : IHostedService | |||||||
|  |  | ||||||
|     private SemaphoreSlim semaphore; |     private SemaphoreSlim semaphore; | ||||||
|  |  | ||||||
|     public DiscordBot(CleanupService cleanupService, MareMetrics metrics, IServiceProvider services, IConfiguration configuration, ILogger<DiscordBot> logger) |     public DiscordBot(CleanupService cleanupService, MareMetrics metrics, IClientIdentificationService clientService, IServiceProvider services, IConfiguration configuration, ILogger<DiscordBot> logger) | ||||||
|     { |     { | ||||||
|         this.cleanupService = cleanupService; |         this.cleanupService = cleanupService; | ||||||
|         this.metrics = metrics; |         this.metrics = metrics; | ||||||
|  |         this.clientService = clientService; | ||||||
|         this.services = services; |         this.services = services; | ||||||
|         _configuration = configuration.GetRequiredSection("MareSynchronos"); |         _configuration = configuration.GetRequiredSection("MareSynchronos"); | ||||||
|         this.logger = logger; |         this.logger = logger; | ||||||
| @@ -687,13 +690,7 @@ public class DiscordBot : IHostedService | |||||||
|         updateStatusCts = new(); |         updateStatusCts = new(); | ||||||
|         while (!updateStatusCts.IsCancellationRequested) |         while (!updateStatusCts.IsCancellationRequested) | ||||||
|         { |         { | ||||||
|             await using var scope = services.CreateAsyncScope(); |             await discordClient.SetActivityAsync(new Game("Mare for " + clientService.GetOnlineUsers() + " Users")).ConfigureAwait(false); | ||||||
|             await using (var db = scope.ServiceProvider.GetRequiredService<MareDbContext>()) |  | ||||||
|             { |  | ||||||
|                 var users = db.Users.Count(c => c.CharacterIdentification != null); |  | ||||||
|                 await discordClient.SetActivityAsync(new Game("Mare for " + users + " Users")).ConfigureAwait(false); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); |             await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ using Microsoft.Extensions.Configuration; | |||||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||||
| using Prometheus; | using Prometheus; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using MareSynchronosShared.Services; | ||||||
|  |  | ||||||
| namespace MareSynchronosServices; | namespace MareSynchronosServices; | ||||||
|  |  | ||||||
| @@ -49,6 +50,24 @@ public class Startup | |||||||
|         services.AddHostedService(provider => provider.GetService<CleanupService>()); |         services.AddHostedService(provider => provider.GetService<CleanupService>()); | ||||||
|         services.AddHostedService<DiscordBot>(); |         services.AddHostedService<DiscordBot>(); | ||||||
|         services.AddGrpc(); |         services.AddGrpc(); | ||||||
|  |  | ||||||
|  |         // add redis related options | ||||||
|  |         var redis = Configuration.GetSection("MareSynchronos").GetValue("RedisConnectionString", string.Empty); | ||||||
|  |         if (!string.IsNullOrEmpty(redis)) | ||||||
|  |         { | ||||||
|  |             services.AddStackExchangeRedisCache(opt => | ||||||
|  |             { | ||||||
|  |                 opt.Configuration = redis; | ||||||
|  |                 opt.InstanceName = "MareSynchronos"; | ||||||
|  |             }); | ||||||
|  |             services.AddSingleton<IClientIdentificationService, DistributedClientIdentificationService>(); | ||||||
|  |             services.AddHostedService(p => p.GetService<DistributedClientIdentificationService>()); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             services.AddSingleton<IClientIdentificationService, LocalClientIdentificationService>(); | ||||||
|  |             services.AddHostedService(p => p.GetService<LocalClientIdentificationService>()); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |     public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ public class MareDbContext : DbContext | |||||||
|     { |     { | ||||||
|         modelBuilder.Entity<Auth>().ToTable("auth"); |         modelBuilder.Entity<Auth>().ToTable("auth"); | ||||||
|         modelBuilder.Entity<User>().ToTable("users"); |         modelBuilder.Entity<User>().ToTable("users"); | ||||||
|         modelBuilder.Entity<User>().HasIndex(c => c.CharacterIdentification); |         //modelBuilder.Entity<User>().HasIndex(c => c.CharacterIdentification); | ||||||
|         modelBuilder.Entity<FileCache>().ToTable("file_caches"); |         modelBuilder.Entity<FileCache>().ToTable("file_caches"); | ||||||
|         modelBuilder.Entity<FileCache>().HasIndex(c => c.UploaderUID); |         modelBuilder.Entity<FileCache>().HasIndex(c => c.UploaderUID); | ||||||
|         modelBuilder.Entity<ClientPair>().ToTable("client_pairs"); |         modelBuilder.Entity<ClientPair>().ToTable("client_pairs"); | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ | |||||||
| 			<PrivateAssets>all</PrivateAssets> | 			<PrivateAssets>all</PrivateAssets> | ||||||
| 			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | 			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
| 		</PackageReference> | 		</PackageReference> | ||||||
|  | 		<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.8" /> | ||||||
| 		<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" /> | 		<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" /> | ||||||
| 		<PackageReference Include="prometheus-net" Version="6.0.0" /> | 		<PackageReference Include="prometheus-net" Version="6.0.0" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ namespace MareSynchronosShared.Models | |||||||
|         [Key] |         [Key] | ||||||
|         [MaxLength(10)] |         [MaxLength(10)] | ||||||
|         public string UID { get; set; } |         public string UID { get; set; } | ||||||
|         [MaxLength(100)] |         //[MaxLength(100)] | ||||||
|         public string CharacterIdentification { get; set; } |         //public string CharacterIdentification { get; set; } | ||||||
|         [Timestamp] |         [Timestamp] | ||||||
|         public byte[] Timestamp { get; set; } |         public byte[] Timestamp { get; set; } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,62 @@ | |||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using MareSynchronosShared.Metrics; | ||||||
|  |  | ||||||
|  | namespace MareSynchronosShared.Services; | ||||||
|  |  | ||||||
|  | public abstract class BaseClientIdentificationService : IClientIdentificationService | ||||||
|  | { | ||||||
|  |     private readonly MareMetrics metrics; | ||||||
|  |     protected ConcurrentDictionary<string, string> OnlineClients = new(); | ||||||
|  |     protected BaseClientIdentificationService(MareMetrics metrics) | ||||||
|  |     { | ||||||
|  |         this.metrics = metrics; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual int GetOnlineUsers() | ||||||
|  |     { | ||||||
|  |         return OnlineClients.Count; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public string? GetUidForCharacterIdent(string characterIdent) | ||||||
|  |     { | ||||||
|  |         var result = OnlineClients.SingleOrDefault(u => | ||||||
|  |             string.Compare(u.Value, characterIdent, StringComparison.InvariantCultureIgnoreCase) == 0); | ||||||
|  |         return result.Equals(new KeyValuePair<string, string>()) ? null : result.Key; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual string? GetCharacterIdentForUid(string uid) | ||||||
|  |     { | ||||||
|  |         if (!OnlineClients.TryGetValue(uid, out var result)) | ||||||
|  |         { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual void MarkUserOnline(string uid, string charaIdent) | ||||||
|  |     { | ||||||
|  |         OnlineClients[uid] = charaIdent; | ||||||
|  |         metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual void MarkUserOffline(string uid) | ||||||
|  |     { | ||||||
|  |         if (OnlineClients.TryRemove(uid, out _)) | ||||||
|  |         { | ||||||
|  |             metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Task StartAsync(CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         return Task.CompletedTask; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual Task StopAsync(CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, 0); | ||||||
|  |         OnlineClients = new(); | ||||||
|  |         return Task.CompletedTask; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | using System.Text; | ||||||
|  | using MareSynchronosShared.Metrics; | ||||||
|  | using Microsoft.Extensions.Caching.Distributed; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using StackExchange.Redis; | ||||||
|  |  | ||||||
|  | namespace MareSynchronosShared.Services; | ||||||
|  |  | ||||||
|  | public class DistributedClientIdentificationService : BaseClientIdentificationService | ||||||
|  | { | ||||||
|  |     private readonly IDistributedCache distributedCache; | ||||||
|  |     private readonly IConfiguration configuration; | ||||||
|  |     private const string RedisPrefix = "uidcache:"; | ||||||
|  |  | ||||||
|  |     public DistributedClientIdentificationService(MareMetrics metrics, IDistributedCache distributedCache, IConfiguration configuration) : base(metrics) | ||||||
|  |     { | ||||||
|  |         this.distributedCache = distributedCache; | ||||||
|  |         this.configuration = configuration.GetSection("MareSynchronos"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override int GetOnlineUsers() | ||||||
|  |     { | ||||||
|  |         var redis = configuration.GetValue<string>("RedisConnectionString"); | ||||||
|  |         var conn = ConnectionMultiplexer.Connect(redis); | ||||||
|  |         var endpoint = conn.GetEndPoints().First(); | ||||||
|  |         return conn.GetServer(endpoint).Keys(pattern: RedisPrefix + "*").Count(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override string? GetCharacterIdentForUid(string uid) | ||||||
|  |     { | ||||||
|  |         var localIdent = base.GetCharacterIdentForUid(uid); | ||||||
|  |         if (localIdent != null) return localIdent; | ||||||
|  |         var cachedIdent = distributedCache.Get(RedisPrefix + uid); | ||||||
|  |         return cachedIdent == null ? null : Encoding.UTF8.GetString(cachedIdent); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override void MarkUserOffline(string uid) | ||||||
|  |     { | ||||||
|  |         base.MarkUserOffline(uid); | ||||||
|  |         distributedCache.Remove(RedisPrefix + uid); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override void MarkUserOnline(string uid, string charaIdent) | ||||||
|  |     { | ||||||
|  |         base.MarkUserOnline(uid, charaIdent); | ||||||
|  |         distributedCache.Set(RedisPrefix + uid, Encoding.UTF8.GetBytes(charaIdent)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override Task StopAsync(CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         foreach (var uid in OnlineClients) | ||||||
|  |         { | ||||||
|  |             distributedCache.Remove(RedisPrefix + uid.Key); | ||||||
|  |         } | ||||||
|  |         return base.StopAsync(cancellationToken); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | using Microsoft.Extensions.Hosting; | ||||||
|  |  | ||||||
|  | namespace MareSynchronosShared.Services; | ||||||
|  |  | ||||||
|  | public interface IClientIdentificationService : IHostedService | ||||||
|  | { | ||||||
|  |     int GetOnlineUsers(); | ||||||
|  |     string? GetUidForCharacterIdent(string characterIdent); | ||||||
|  |     string? GetCharacterIdentForUid(string uid); | ||||||
|  |     void MarkUserOnline(string uid, string charaIdent); | ||||||
|  |     void MarkUserOffline(string uid); | ||||||
|  | } | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | using MareSynchronosShared.Metrics; | ||||||
|  |  | ||||||
|  | namespace MareSynchronosShared.Services; | ||||||
|  |  | ||||||
|  | public class LocalClientIdentificationService : BaseClientIdentificationService | ||||||
|  | { | ||||||
|  |     public LocalClientIdentificationService(MareMetrics metrics) : base(metrics) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 rootdarkarchon
					rootdarkarchon