add redis for character identification

This commit is contained in:
rootdarkarchon
2022-09-13 11:45:09 +02:00
parent ea48fb3947
commit 1f66b2c980
16 changed files with 356 additions and 175 deletions

View File

@@ -16,8 +16,8 @@ namespace MareSynchronosServer.Hubs
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))
.Select(u => u.UID).ToList();
private List<string> OnlineAdmins => _dbContext.Users.Where(u => (u.IsModerator || u.IsAdmin)).Select(u => u.UID).ToList();
[Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)]
[HubMethodName(Api.SendAdminChangeModeratorStatus)]
public async Task ChangeModeratorStatus(string uid, bool isModerator)
@@ -101,13 +101,14 @@ namespace MareSynchronosServer.Hubs
{
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,
IsModerator = b.IsModerator,
IsAdmin = b.IsAdmin
}).ToListAsync().ConfigureAwait(false);
}).ToList();
}
[Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)]
@@ -134,11 +135,10 @@ namespace MareSynchronosServer.Hubs
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
await Clients.Users(OnlineAdmins).SendAsync(Api.OnAdminUpdateOrAddBannedUser, dto).ConfigureAwait(false);
var bannedUser =
await _dbContext.Users.SingleOrDefaultAsync(u => u.CharacterIdentification == dto.CharacterHash).ConfigureAwait(false);
if (bannedUser != null)
var bannedUser = _clientIdentService.GetUidForCharacterIdent(dto.CharacterHash);
if (!string.IsNullOrEmpty(bannedUser))
{
await Clients.User(bannedUser.UID).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false);
await Clients.User(bannedUser).SendAsync(Api.OnAdminForcedReconnect).ConfigureAwait(false);
}
}

View File

@@ -76,7 +76,7 @@ namespace MareSynchronosServer.Hubs
IsForbidden = forbiddenFile != null,
Hash = hash.Key,
Size = hash.Value,
Url = new Uri(cdnFullUri, hash.Key.ToUpperInvariant()).ToString()
Url = new Uri(_cdnFullUri, hash.Key.ToUpperInvariant()).ToString()
});
}

View File

@@ -21,9 +21,9 @@ namespace MareSynchronosServer.Hubs
{
_logger.LogInformation("User {AuthenticatedUserId} deleted their account", AuthenticatedUserId);
string userid = AuthenticatedUserId;
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 auth = await _dbContext.Auth.SingleAsync(u => u.UserUID == 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,
IsRemoved = true
}, userEntry.CharacterIdentification).ConfigureAwait(false);
}, charaIdent).ConfigureAwait(false);
}
_mareMetrics.DecGauge(MetricsAPI.GaugePairs, ownPairData.Count + otherPairData.Count);
@@ -77,14 +77,18 @@ namespace MareSynchronosServer.Hubs
.Include(u => u.User)
.Include(u => u.OtherUser)
.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);
var otherOnlineUsers =
otherUsers.Where(u => !string.IsNullOrEmpty(_clientIdentService.GetCharacterIdentForUid(u.UID)));
var otherEntries = await _dbContext.ClientPairs.AsNoTracking()
.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);
return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList();
await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(Api.OnUserAddOnlinePairedPlayer, ownIdent).ConfigureAwait(false);
return otherEntries.Select(e => _clientIdentService.GetCharacterIdentForUid(e.User.UID)).Distinct().ToList();
}
[Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)]
@@ -152,12 +156,14 @@ namespace MareSynchronosServer.Hubs
userToOther.UserUID == user.UID
&& !userToOther.IsPaused
&& !otherToUser.IsPaused
&& visibleCharacterIds.Contains(userToOther.OtherUser.CharacterIdentification)
select otherToUser.UserUID;
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.CounterUserPushDataTo, otherEntries.Count);
@@ -199,6 +205,7 @@ namespace MareSynchronosServer.Hubs
}, string.Empty).ConfigureAwait(false);
if (otherEntry != null)
{
var userIdent = _clientIdentService.GetCharacterIdentForUid(user.UID);
await Clients.User(otherUser.UID).SendAsync(Api.OnUserUpdateClientPairs,
new ClientPairDto()
{
@@ -207,14 +214,15 @@ namespace MareSynchronosServer.Hubs
IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = false,
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)
.SendAsync(Api.OnUserAddOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false);
.SendAsync(Api.OnUserAddOnlinePairedPlayer, otherIdent).ConfigureAwait(false);
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;
_dbContext.Update(pair);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
var selfCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false)).CharacterIdentification;
var otherCharaIdent = (await _dbContext.Users.SingleAsync(u => u.UID == otherUserUid).ConfigureAwait(false)).CharacterIdentification;
var selfCharaIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
var otherCharaIdent = _clientIdentService.GetCharacterIdentForUid(pair.OtherUserUID);
var otherEntry = OppositeEntry(otherUserUid);
await Clients.User(AuthenticatedUserId)
@@ -282,27 +290,29 @@ namespace MareSynchronosServer.Hubs
_dbContext.ClientPairs.Remove(wl);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
var otherEntry = OppositeEntry(uid);
var otherIdent = _clientIdentService.GetCharacterIdentForUid(otherUser.UID);
await Clients.User(sender.UID)
.SendAsync(Api.OnUserUpdateClientPairs, new ClientPairDto()
{
OtherUID = otherUser.UID,
IsRemoved = true
}, otherUser.CharacterIdentification).ConfigureAwait(false);
}, otherIdent).ConfigureAwait(false);
if (otherEntry != null)
{
if (!string.IsNullOrEmpty(otherUser.CharacterIdentification))
if (!string.IsNullOrEmpty(otherIdent))
{
var ownIdent = _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId);
await Clients.User(sender.UID)
.SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherUser.CharacterIdentification).ConfigureAwait(false);
.SendAsync(Api.OnUserRemoveOnlinePairedPlayer, otherIdent).ConfigureAwait(false);
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()
{
OtherUID = sender.UID,
IsPaused = otherEntry.IsPaused,
IsPausedFromOthers = false,
IsSynced = false
}, sender.CharacterIdentification).ConfigureAwait(false);
}, ownIdent).ConfigureAwait(false);
}
}

View File

@@ -3,11 +3,13 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using MareSynchronos.API;
using MareSynchronosServer.Services;
using MareSynchronosShared.Authentication;
using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Models;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
@@ -15,27 +17,30 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
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 IHttpContextAccessor _contextAccessor;
private readonly IClientIdentificationService _clientIdentService;
private readonly ILogger<MareHub> _logger;
private readonly MareDbContext _dbContext;
private readonly Uri cdnFullUri;
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)
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor,
IClientIdentificationService clientIdentService)
{
_mareMetrics = mareMetrics;
_authServiceClient = authServiceClient;
_fileServiceClient = fileServiceClient;
_systemInfoService = systemInfoService;
cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl"));
this.contextAccessor = contextAccessor;
_cdnFullUri = new Uri(configuration.GetRequiredSection("MareSynchronos").GetValue<string>("CdnFullUrl"));
_contextAccessor = contextAccessor;
_clientIdentService = clientIdentService;
_logger = logger;
_dbContext = mareDbContext;
}
@@ -57,20 +62,17 @@ namespace MareSynchronosServer.Hubs
if (!string.IsNullOrEmpty(userId) && !isBanned && !string.IsNullOrEmpty(characterIdentification))
{
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()
{
ServerVersion = Api.Version
};
}
else if (string.IsNullOrEmpty(user.CharacterIdentification))
{
_mareMetrics.IncGauge(MetricsAPI.GaugeAuthorizedConnections);
}
user.LastLoggedIn = DateTime.UtcNow;
user.CharacterIdentification = characterIdentification;
_clientIdentService.MarkUserOnline(user.UID, characterIdentification);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
return new ConnectionDto
@@ -90,7 +92,7 @@ namespace MareSynchronosServer.Hubs
public override async Task OnConnectedAsync()
{
_logger.LogInformation("Connection from {ip}", contextAccessor.GetIpAddress());
_logger.LogInformation("Connection from {ip}", _contextAccessor.GetIpAddress());
_mareMetrics.IncGauge(MetricsAPI.GaugeConnections);
await base.OnConnectedAsync().ConfigureAwait(false);
}
@@ -99,9 +101,11 @@ namespace MareSynchronosServer.Hubs
{
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false);
if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification))
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);
@@ -126,11 +130,11 @@ namespace MareSynchronosServer.Hubs
select otherToUser.UserUID;
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));
user.CharacterIdentification = null;
_clientIdentService.MarkUserOffline(user.UID);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
@@ -143,5 +147,4 @@ namespace MareSynchronosServer.Hubs
{
return await _dbContext.Users.AsNoTrackingWithIdentityResolution().SingleAsync(u => u.UID == AuthenticatedUserId).ConfigureAwait(false);
}
}
}

View File

@@ -29,11 +29,6 @@ namespace MareSynchronosServer
context.SaveChanges();
// 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 unfinishedRegistrations = context.LodeStoneAuth.Where(c => c.StartedAt != null);
context.RemoveRange(unfinishedRegistrations);

View File

@@ -1,32 +1,32 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MareSynchronos.API;
using MareSynchronosServer.Hubs;
using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Models;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Services;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MareSynchronosServer;
namespace MareSynchronosServer.Services;
public class SystemInfoService : IHostedService, IDisposable
{
private readonly MareMetrics _mareMetrics;
private readonly IClientIdentificationService clientIdentService;
private readonly IServiceProvider _services;
private readonly ILogger<SystemInfoService> _logger;
private readonly IHubContext<MareHub> _hubContext;
private Timer _timer;
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;
this.clientIdentService = clientIdentService;
_services = services;
_logger = logger;
_hubContext = hubContext;
@@ -56,8 +56,6 @@ public class SystemInfoService : IHostedService, IDisposable
using var scope = _services.CreateScope();
using var db = scope.ServiceProvider.GetService<MareDbContext>()!;
var users = db.Users.Count(c => c.CharacterIdentification != null);
SystemInfoDto = new SystemInfoDto()
{
CacheUsage = 0,
@@ -65,7 +63,7 @@ public class SystemInfoService : IHostedService, IDisposable
RAMUsage = 0,
NetworkIn = 0,
NetworkOut = 0,
OnlineUsers = users,
OnlineUsers = clientIdentService.GetOnlineUsers(),
UploadedFiles = 0
};

View File

@@ -19,6 +19,8 @@ using Grpc.Net.Client.Configuration;
using Prometheus;
using MareSynchronosShared.Metrics;
using System.Collections.Generic;
using MareSynchronosServer.Services;
using MareSynchronosShared.Services;
namespace MareSynchronosServer
{
@@ -101,18 +103,18 @@ namespace MareSynchronosServer
options.EnableThreadSafetyChecks(false);
}, mareConfig.GetValue("DbContextPoolSize", 1024));
services.AddHostedService(provider => provider.GetService<SystemInfoService>());
services.AddAuthentication(options =>
{
options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme;
})
.AddScheme<AuthenticationSchemeOptions, SecretKeyGrpcAuthenticationHandler>(SecretKeyGrpcAuthenticationHandler.AuthScheme, options => { });
}).AddScheme<AuthenticationSchemeOptions, SecretKeyGrpcAuthenticationHandler>(SecretKeyGrpcAuthenticationHandler.AuthScheme, _ => { });
services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
var signalRserviceBuilder = services.AddSignalR(hubOptions =>
var signalRServiceBuilder = services.AddSignalR(hubOptions =>
{
hubOptions.MaximumReceiveMessageSize = long.MaxValue;
hubOptions.EnableDetailedErrors = true;
@@ -120,13 +122,28 @@ namespace MareSynchronosServer
hubOptions.StreamBufferCapacity = 200;
hubOptions.AddFilter<SignalRLimitFilter>();
});
// add redis related options
var redis = mareConfig.GetValue("RedisConnectionString", string.Empty);
if (!string.IsNullOrEmpty(redis))
{
signalRserviceBuilder.AddStackExchangeRedis(redis, options =>
signalRServiceBuilder.AddStackExchangeRedis(redis, options =>
{
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>());
}
}

View File

@@ -14,6 +14,7 @@ using MareSynchronosServices.Authentication;
using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Models;
using MareSynchronosShared.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -26,6 +27,7 @@ public class DiscordBot : IHostedService
{
private readonly CleanupService cleanupService;
private readonly MareMetrics metrics;
private readonly IClientIdentificationService clientService;
private readonly IServiceProvider services;
private readonly IConfiguration _configuration;
private readonly ILogger<DiscordBot> logger;
@@ -44,10 +46,11 @@ public class DiscordBot : IHostedService
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.metrics = metrics;
this.clientService = clientService;
this.services = services;
_configuration = configuration.GetRequiredSection("MareSynchronos");
this.logger = logger;
@@ -687,13 +690,7 @@ public class DiscordBot : IHostedService
updateStatusCts = new();
while (!updateStatusCts.IsCancellationRequested)
{
await using var scope = services.CreateAsyncScope();
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 discordClient.SetActivityAsync(new Game("Mare for " + clientService.GetOnlineUsers() + " Users")).ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false);
}
}

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Prometheus;
using System.Collections.Generic;
using MareSynchronosShared.Services;
namespace MareSynchronosServices;
@@ -49,6 +50,24 @@ public class Startup
services.AddHostedService(provider => provider.GetService<CleanupService>());
services.AddHostedService<DiscordBot>();
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)

View File

@@ -45,7 +45,7 @@ public class MareDbContext : DbContext
{
modelBuilder.Entity<Auth>().ToTable("auth");
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>().HasIndex(c => c.UploaderUID);
modelBuilder.Entity<ClientPair>().ToTable("client_pairs");

View File

@@ -30,6 +30,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
<PackageReference Include="prometheus-net" Version="6.0.0" />
</ItemGroup>

View File

@@ -7,8 +7,8 @@ namespace MareSynchronosShared.Models
[Key]
[MaxLength(10)]
public string UID { get; set; }
[MaxLength(100)]
public string CharacterIdentification { get; set; }
//[MaxLength(100)]
//public string CharacterIdentification { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,10 @@
using MareSynchronosShared.Metrics;
namespace MareSynchronosShared.Services;
public class LocalClientIdentificationService : BaseClientIdentificationService
{
public LocalClientIdentificationService(MareMetrics metrics) : base(metrics)
{
}
}