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,133 +17,134 @@ 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 AuthService.AuthServiceClient _authServiceClient;
|
||||||
|
private readonly FileService.FileServiceClient _fileServiceClient;
|
||||||
|
private readonly SystemInfoService _systemInfoService;
|
||||||
|
private readonly IHttpContextAccessor _contextAccessor;
|
||||||
|
private readonly IClientIdentificationService _clientIdentService;
|
||||||
|
private readonly ILogger<MareHub> _logger;
|
||||||
|
private readonly MareDbContext _dbContext;
|
||||||
|
private readonly Uri _cdnFullUri;
|
||||||
|
public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient,
|
||||||
|
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor,
|
||||||
|
IClientIdentificationService clientIdentService)
|
||||||
{
|
{
|
||||||
private readonly MareMetrics _mareMetrics;
|
_mareMetrics = mareMetrics;
|
||||||
private readonly AuthService.AuthServiceClient _authServiceClient;
|
_authServiceClient = authServiceClient;
|
||||||
private readonly FileService.FileServiceClient _fileServiceClient;
|
_fileServiceClient = fileServiceClient;
|
||||||
private readonly SystemInfoService _systemInfoService;
|
_systemInfoService = systemInfoService;
|
||||||
private readonly IHttpContextAccessor contextAccessor;
|
_cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl"));
|
||||||
private readonly ILogger<MareHub> _logger;
|
_contextAccessor = contextAccessor;
|
||||||
private readonly MareDbContext _dbContext;
|
_clientIdentService = clientIdentService;
|
||||||
private readonly Uri cdnFullUri;
|
_logger = logger;
|
||||||
public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient,
|
_dbContext = mareDbContext;
|
||||||
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor)
|
}
|
||||||
|
|
||||||
|
[HubMethodName(Api.InvokeHeartbeat)]
|
||||||
|
[Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)]
|
||||||
|
public async Task<ConnectionDto> Heartbeat(string characterIdentification)
|
||||||
|
{
|
||||||
|
_mareMetrics.IncCounter(MetricsAPI.CounterInitializedConnections);
|
||||||
|
|
||||||
|
var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
_logger.LogInformation("Connection from {userId}, CI: {characterIdentification}", userId, characterIdentification);
|
||||||
|
|
||||||
|
await Clients.Caller.SendAsync(Api.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification))
|
||||||
{
|
{
|
||||||
_mareMetrics = mareMetrics;
|
var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false));
|
||||||
_authServiceClient = authServiceClient;
|
var existingIdent = _clientIdentService.GetCharacterIdentForUid(userId);
|
||||||
_fileServiceClient = fileServiceClient;
|
if (!string.IsNullOrEmpty(existingIdent) && characterIdentification != existingIdent)
|
||||||
_systemInfoService = systemInfoService;
|
|
||||||
cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl"));
|
|
||||||
this.contextAccessor = contextAccessor;
|
|
||||||
_logger = logger;
|
|
||||||
_dbContext = mareDbContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HubMethodName(Api.InvokeHeartbeat)]
|
|
||||||
[Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)]
|
|
||||||
public async Task<ConnectionDto> Heartbeat(string characterIdentification)
|
|
||||||
{
|
|
||||||
_mareMetrics.IncCounter(MetricsAPI.CounterInitializedConnections);
|
|
||||||
|
|
||||||
var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
|
|
||||||
|
|
||||||
_logger.LogInformation("Connection from {userId}, CI: {characterIdentification}", userId, characterIdentification);
|
|
||||||
|
|
||||||
await Clients.Caller.SendAsync(Api.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var isBanned = await _dbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification))
|
|
||||||
{
|
{
|
||||||
var user = (await _dbContext.Users.SingleAsync(u => u.UID == userId).ConfigureAwait(false));
|
return new ConnectionDto()
|
||||||
if (!string.IsNullOrEmpty(user.CharacterIdentification) && characterIdentification != user.CharacterIdentification)
|
|
||||||
{
|
{
|
||||||
return new ConnectionDto()
|
ServerVersion = Api.Version
|
||||||
{
|
|
||||||
ServerVersion = Api.Version
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(user.CharacterIdentification))
|
|
||||||
{
|
|
||||||
_mareMetrics.IncGauge(MetricsAPI.GaugeAuthorizedConnections);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.LastLoggedIn = DateTime.UtcNow;
|
|
||||||
user.CharacterIdentification = characterIdentification;
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
return new ConnectionDto
|
|
||||||
{
|
|
||||||
ServerVersion = Api.Version,
|
|
||||||
UID = string.IsNullOrEmpty(user.Alias) ? user.UID : user.Alias,
|
|
||||||
IsModerator = user.IsModerator,
|
|
||||||
IsAdmin = user.IsAdmin
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ConnectionDto()
|
user.LastLoggedIn = DateTime.UtcNow;
|
||||||
|
_clientIdentService.MarkUserOnline(user.UID, characterIdentification);
|
||||||
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
return new ConnectionDto
|
||||||
{
|
{
|
||||||
ServerVersion = Api.Version
|
ServerVersion = Api.Version,
|
||||||
|
UID = string.IsNullOrEmpty(user.Alias) ? user.UID : user.Alias,
|
||||||
|
IsModerator = user.IsModerator,
|
||||||
|
IsAdmin = user.IsAdmin
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task OnConnectedAsync()
|
return new ConnectionDto()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Connection from {ip}", contextAccessor.GetIpAddress());
|
ServerVersion = Api.Version
|
||||||
_mareMetrics.IncGauge(MetricsAPI.GaugeConnections);
|
};
|
||||||
await base.OnConnectedAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task OnDisconnectedAsync(Exception exception)
|
|
||||||
{
|
|
||||||
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
|
|
||||||
|
|
||||||
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false);
|
|
||||||
if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification))
|
|
||||||
{
|
|
||||||
_mareMetrics.DecGauge(MetricsAPI.GaugeAuthorizedConnections);
|
|
||||||
|
|
||||||
_logger.LogInformation("Disconnect from {id}", AuthenticatedUserId);
|
|
||||||
|
|
||||||
var query =
|
|
||||||
from userToOther in _dbContext.ClientPairs
|
|
||||||
join otherToUser in _dbContext.ClientPairs
|
|
||||||
on new
|
|
||||||
{
|
|
||||||
user = userToOther.UserUID,
|
|
||||||
other = userToOther.OtherUserUID
|
|
||||||
|
|
||||||
} equals new
|
|
||||||
{
|
|
||||||
user = otherToUser.OtherUserUID,
|
|
||||||
other = otherToUser.UserUID
|
|
||||||
}
|
|
||||||
where
|
|
||||||
userToOther.UserUID == user.UID
|
|
||||||
&& !userToOther.IsPaused
|
|
||||||
&& !otherToUser.IsPaused
|
|
||||||
select otherToUser.UserUID;
|
|
||||||
var otherEntries = await query.ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, user.CharacterIdentification).ConfigureAwait(false);
|
|
||||||
|
|
||||||
_dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID));
|
|
||||||
|
|
||||||
user.CharacterIdentification = null;
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await base.OnDisconnectedAsync(exception).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown";
|
|
||||||
|
|
||||||
protected async Task<User> GetAuthenticatedUserUntrackedAsync()
|
|
||||||
{
|
|
||||||
return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public override async Task OnConnectedAsync()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Connection from {ip}", _contextAccessor.GetIpAddress());
|
||||||
|
_mareMetrics.IncGauge(MetricsAPI.GaugeConnections);
|
||||||
|
await base.OnConnectedAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task OnDisconnectedAsync(Exception exception)
|
||||||
|
{
|
||||||
|
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
|
||||||
|
|
||||||
|
var userCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(userCharaIdent))
|
||||||
|
{
|
||||||
|
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId)!.ConfigureAwait(false);
|
||||||
|
_mareMetrics.DecGauge(MetricsAPI.GaugeAuthorizedConnections);
|
||||||
|
|
||||||
|
_logger.LogInformation("Disconnect from {id}", AuthenticatedUserId);
|
||||||
|
|
||||||
|
var query =
|
||||||
|
from userToOther in _dbContext.ClientPairs
|
||||||
|
join otherToUser in _dbContext.ClientPairs
|
||||||
|
on new
|
||||||
|
{
|
||||||
|
user = userToOther.UserUID,
|
||||||
|
other = userToOther.OtherUserUID
|
||||||
|
|
||||||
|
} equals new
|
||||||
|
{
|
||||||
|
user = otherToUser.OtherUserUID,
|
||||||
|
other = otherToUser.UserUID
|
||||||
|
}
|
||||||
|
where
|
||||||
|
userToOther.UserUID == user.UID
|
||||||
|
&& !userToOther.IsPaused
|
||||||
|
&& !otherToUser.IsPaused
|
||||||
|
select otherToUser.UserUID;
|
||||||
|
var otherEntries = await query.ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Clients.Users(otherEntries).SendAsync(Api.OnUserRemoveOnlinePairedPlayer, userCharaIdent).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_dbContext.RemoveRange(_dbContext.Files.Where(f => !f.Uploaded && f.UploaderUID == user.UID));
|
||||||
|
|
||||||
|
_clientIdentService.MarkUserOffline(user.UID);
|
||||||
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.OnDisconnectedAsync(exception).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown";
|
||||||
|
|
||||||
|
protected async Task<User> GetAuthenticatedUserUntrackedAsync()
|
||||||
|
{
|
||||||
|
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