forked from Eauldane/SnowcloakServer
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
7c30c1b3ba | |||
ef20bb5f97 | |||
5a6bf4e7c2 | |||
919ab864ce | |||
a741f25bde | |||
3aa4e62f28 | |||
afea311da6 | |||
17a5354627 | |||
9ea2a70c33 | |||
d23184b1ec | |||
f07fd3990b | |||
b987a58caa | |||
a314664c40 | |||
d4a6949a50 | |||
3b932420d3
|
|||
cbd7610809 | |||
271a6b0373
|
|||
74155ce171
|
|||
ef4c463471
|
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack.Annotations" Version="2.5.198" />
|
||||
<PackageReference Include="MessagePack.Annotations" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -2,3 +2,5 @@
|
||||
|
||||
# MA0048: File name must match type name
|
||||
dotnet_diagnostic.MA0048.severity = suggestion
|
||||
# MA0051: Method too long
|
||||
dotnet_diagnostic.MA0051.severity = none
|
@@ -16,6 +16,7 @@ using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace MareSynchronosAuthService.Controllers;
|
||||
|
||||
@@ -112,7 +113,7 @@ public class JwtController : Controller
|
||||
return Unauthorized("You are permanently banned.");
|
||||
}
|
||||
|
||||
var existingIdent = await _redis.GetAsync<string>("UID:" + authResult.Uid);
|
||||
var existingIdent = await _redis.GetAsync<string>("UID:" + authResult.Uid, CommandFlags.PreferReplica);
|
||||
if (!string.IsNullOrEmpty(existingIdent)) return Unauthorized("Already logged in to this account. Reconnect in 60 seconds. If you keep seeing this issue, restart your game.");
|
||||
|
||||
var token = CreateToken(new List<Claim>()
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
@@ -23,12 +23,12 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.219">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -8,21 +8,11 @@ using MareSynchronosShared.Utils.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.RegularExpressions;
|
||||
using MareSynchronosShared.Models;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
|
||||
namespace MareSynchronosAuthService.Services;
|
||||
|
||||
internal record IpRegistrationCount
|
||||
{
|
||||
private int count = 1;
|
||||
public int Count => count;
|
||||
public Task ResetTask { get; set; }
|
||||
public CancellationTokenSource ResetTaskCts { get; set; }
|
||||
public void IncreaseCount()
|
||||
{
|
||||
Interlocked.Increment(ref count);
|
||||
}
|
||||
}
|
||||
|
||||
public class AccountRegistrationService
|
||||
{
|
||||
private readonly MareMetrics _metrics;
|
||||
@@ -30,52 +20,35 @@ public class AccountRegistrationService
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IConfigurationService<AuthServiceConfiguration> _configurationService;
|
||||
private readonly ILogger<AccountRegistrationService> _logger;
|
||||
private readonly ConcurrentDictionary<string, IpRegistrationCount> _registrationsPerIp = new(StringComparer.Ordinal);
|
||||
private readonly IRedisDatabase _redis;
|
||||
|
||||
|
||||
private Regex _registrationUserAgentRegex = new Regex(@"^MareSynchronos/", RegexOptions.Compiled);
|
||||
|
||||
public AccountRegistrationService(MareMetrics metrics, MareDbContext mareDbContext,
|
||||
IServiceScopeFactory serviceScopeFactory, IConfigurationService<AuthServiceConfiguration> configuration,
|
||||
ILogger<AccountRegistrationService> logger)
|
||||
ILogger<AccountRegistrationService> logger, IRedisDatabase redisDb)
|
||||
{
|
||||
_mareDbContext = mareDbContext;
|
||||
_logger = logger;
|
||||
_configurationService = configuration;
|
||||
_metrics = metrics;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_redis = redisDb;
|
||||
}
|
||||
|
||||
public async Task<RegisterReplyV2Dto> RegisterAccountAsync(string ua, string ip, string hashedSecretKey)
|
||||
{
|
||||
var reply = new RegisterReplyV2Dto();
|
||||
|
||||
if (!_registrationUserAgentRegex.Match(ua).Success)
|
||||
if (string.IsNullOrEmpty(ua) || !ua.StartsWith("MareSynchronos/", StringComparison.Ordinal))
|
||||
{
|
||||
reply.ErrorMessage = "User-Agent not allowed";
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (_registrationsPerIp.TryGetValue(ip, out var registrationCount)
|
||||
&& registrationCount.Count >= _configurationService.GetValueOrDefault(nameof(AuthServiceConfiguration.RegisterIpLimit), 3))
|
||||
var registrationsByIp = await _redis.GetAsync<int>("IPREG:" + ip, CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
if (registrationsByIp >= _configurationService.GetValueOrDefault(nameof(AuthServiceConfiguration.RegisterIpLimit), 3))
|
||||
{
|
||||
_logger.LogWarning("Rejecting {ip} for registration spam", ip);
|
||||
|
||||
if (registrationCount.ResetTask == null)
|
||||
{
|
||||
registrationCount.ResetTaskCts = new CancellationTokenSource();
|
||||
|
||||
if (registrationCount.ResetTaskCts != null)
|
||||
registrationCount.ResetTaskCts.Cancel();
|
||||
|
||||
registrationCount.ResetTask = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(_configurationService.GetValueOrDefault(nameof(AuthServiceConfiguration.RegisterIpDurationInMinutes), 10))).ConfigureAwait(false);
|
||||
|
||||
}).ContinueWith((t) =>
|
||||
{
|
||||
_registrationsPerIp.Remove(ip, out _);
|
||||
}, registrationCount.ResetTaskCts.Token);
|
||||
}
|
||||
reply.ErrorMessage = "Too many registrations from this IP. Please try again later.";
|
||||
return reply;
|
||||
}
|
||||
@@ -85,18 +58,12 @@ public class AccountRegistrationService
|
||||
var hasValidUid = false;
|
||||
while (!hasValidUid)
|
||||
{
|
||||
var uid = StringUtils.GenerateRandomString(7);
|
||||
var uid = StringUtils.GenerateRandomString(8);
|
||||
if (_mareDbContext.Users.Any(u => u.UID == uid || u.Alias == uid)) continue;
|
||||
user.UID = uid;
|
||||
hasValidUid = true;
|
||||
}
|
||||
|
||||
// make the first registered user on the service to admin
|
||||
if (!await _mareDbContext.Users.AnyAsync().ConfigureAwait(false))
|
||||
{
|
||||
user.IsAdmin = true;
|
||||
}
|
||||
|
||||
user.LastLoggedIn = DateTime.UtcNow;
|
||||
|
||||
var auth = new Auth()
|
||||
@@ -115,38 +82,14 @@ public class AccountRegistrationService
|
||||
reply.Success = true;
|
||||
reply.UID = user.UID;
|
||||
|
||||
RecordIpRegistration(ip);
|
||||
|
||||
await _redis.Database.StringIncrementAsync($"IPREG:{ip}").ConfigureAwait(false);
|
||||
// Naive implementation, but should be good enough. A true sliding window *probably* isn't necessary.
|
||||
await _redis.Database.KeyExpireAsync($"IPREG:{ip}", TimeSpan.
|
||||
FromMinutes(_configurationService.GetValueOrDefault(nameof(
|
||||
AuthServiceConfiguration.RegisterIpDurationInMinutes), 60))).
|
||||
ConfigureAwait(false);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private void RecordIpRegistration(string ip)
|
||||
{
|
||||
var whitelisted = _configurationService.GetValueOrDefault(nameof(AuthServiceConfiguration.WhitelistedIps), new List<string>());
|
||||
if (!whitelisted.Any(w => ip.Contains(w, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (_registrationsPerIp.TryGetValue(ip, out var count))
|
||||
{
|
||||
count.IncreaseCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
count = _registrationsPerIp[ip] = new IpRegistrationCount();
|
||||
|
||||
if (count.ResetTaskCts != null)
|
||||
count.ResetTaskCts.Cancel();
|
||||
|
||||
count.ResetTaskCts = new CancellationTokenSource();
|
||||
|
||||
count.ResetTask = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(_configurationService.GetValueOrDefault(nameof(AuthServiceConfiguration.RegisterIpDurationInMinutes), 10))).ConfigureAwait(false);
|
||||
|
||||
}).ContinueWith((t) =>
|
||||
{
|
||||
_registrationsPerIp.Remove(ip, out _);
|
||||
}, count.ResetTaskCts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -165,19 +165,22 @@ public class Startup
|
||||
|
||||
var options = ConfigurationOptions.Parse(redisConnection);
|
||||
|
||||
var endpoint = options.EndPoints[0];
|
||||
string address = "";
|
||||
int port = 0;
|
||||
if (endpoint is DnsEndPoint dnsEndPoint) { address = dnsEndPoint.Host; port = dnsEndPoint.Port; }
|
||||
if (endpoint is IPEndPoint ipEndPoint) { address = ipEndPoint.Address.ToString(); port = ipEndPoint.Port; }
|
||||
var hosts = options.EndPoints
|
||||
.Select(ep =>
|
||||
{
|
||||
if (ep is DnsEndPoint dns) return (host: dns.Host, port: dns.Port);
|
||||
if (ep is IPEndPoint ip) return (host: ip.Address.ToString(), port: ip.Port);
|
||||
return (host: (string?)null, port: 0);
|
||||
})
|
||||
.Where(x => x.host != null)
|
||||
.Distinct()
|
||||
.Select(x => new RedisHost { Host = x.host!, Port = x.port })
|
||||
.ToArray();
|
||||
var redisConfiguration = new RedisConfiguration()
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
AbortOnConnectFail = false,
|
||||
KeyPrefix = "",
|
||||
Hosts = new RedisHost[]
|
||||
{
|
||||
new RedisHost(){ Host = address, Port = port },
|
||||
},
|
||||
Hosts = hosts,
|
||||
AllowAdmin = true,
|
||||
ConnectTimeout = options.ConnectTimeout,
|
||||
Database = 0,
|
||||
@@ -187,11 +190,11 @@ public class Startup
|
||||
{
|
||||
Mode = ServerEnumerationStrategy.ModeOptions.All,
|
||||
TargetRole = ServerEnumerationStrategy.TargetRoleOptions.Any,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.Throw,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.IgnoreIfOtherAvailable,
|
||||
},
|
||||
MaxValueLength = 1024,
|
||||
PoolSize = mareConfig.GetValue(nameof(ServerConfiguration.RedisPool), 50),
|
||||
SyncTimeout = options.SyncTimeout,
|
||||
SyncTimeout = Math.Max(options.SyncTimeout, 10000),
|
||||
};
|
||||
|
||||
services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfiguration);
|
||||
@@ -211,7 +214,7 @@ public class Startup
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
builder.MigrationsAssembly("MareSynchronosShared");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
}, mareConfig.GetValue(nameof(MareConfigurationBase.DbContextPoolSize), 1024));
|
||||
services.AddDbContextFactory<MareDbContext>(options =>
|
||||
{
|
||||
@@ -220,7 +223,7 @@ public class Startup
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
builder.MigrationsAssembly("MareSynchronosShared");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace MareSynchronosServer.Hubs;
|
||||
|
||||
@@ -118,14 +119,14 @@ public partial class MareHub
|
||||
|
||||
private async Task<Dictionary<string, string>> GetOnlineUsers(List<string> uids)
|
||||
{
|
||||
var result = await _redis.GetAllAsync<string>(uids.Select(u => "UID:" + u).ToHashSet(StringComparer.Ordinal)).ConfigureAwait(false);
|
||||
var result = await _redis.GetAllAsync<string>(uids.Select(u => "UID:" + u).ToHashSet(StringComparer.Ordinal), CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
return uids.Where(u => result.TryGetValue("UID:" + u, out var ident) && !string.IsNullOrEmpty(ident)).ToDictionary(u => u, u => result["UID:" + u], StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private async Task<string> GetUserIdent(string uid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(uid)) return string.Empty;
|
||||
return await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
||||
return await _redis.GetAsync<string>("UID:" + uid, CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task RemoveUserFromRedis()
|
||||
@@ -202,6 +203,8 @@ public partial class MareHub
|
||||
private async Task UpdateUserOnRedis()
|
||||
{
|
||||
await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
await _redis.SetAddAsync($"connections:{UserCharaIdent}", Context.ConnectionId).ConfigureAwait(false);
|
||||
await _redis.UpdateExpiryAsync($"connections:{UserCharaIdent}", TimeSpan.FromSeconds(60)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null)
|
||||
|
@@ -6,6 +6,7 @@ using MareSynchronosShared.Utils;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace MareSynchronosServer.Hubs;
|
||||
|
||||
@@ -13,12 +14,12 @@ public partial class MareHub
|
||||
{
|
||||
private async Task<string?> GetUserGposeLobby()
|
||||
{
|
||||
return await _redis.GetAsync<string>(GposeLobbyUser).ConfigureAwait(false);
|
||||
return await _redis.GetAsync<string>(GposeLobbyUser, CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<List<string>> GetUsersInLobby(string lobbyId, bool includeSelf = false)
|
||||
{
|
||||
var users = await _redis.GetAsync<List<string>>($"GposeLobby:{lobbyId}").ConfigureAwait(false);
|
||||
var users = await _redis.GetAsync<List<string>>($"GposeLobby:{lobbyId}", CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
return users?.Where(u => includeSelf || !string.Equals(u, UserUID, StringComparison.Ordinal)).ToList() ?? [];
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ public partial class MareHub
|
||||
while (string.IsNullOrEmpty(lobbyId))
|
||||
{
|
||||
lobbyId = StringUtils.GenerateRandomString(30, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
||||
var result = await _redis.GetAsync<List<string>>($"GposeLobby:{lobbyId}").ConfigureAwait(false);
|
||||
var result = await _redis.GetAsync<List<string>>($"GposeLobby:{lobbyId}", CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
if (result != null)
|
||||
lobbyId = string.Empty;
|
||||
}
|
||||
|
@@ -212,10 +212,10 @@ public partial class MareHub
|
||||
throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}.");
|
||||
}
|
||||
|
||||
var gid = StringUtils.GenerateRandomString(9);
|
||||
var gid = StringUtils.GenerateRandomString(10);
|
||||
while (await DbContext.Groups.AnyAsync(g => g.GID == "SNOW-" + gid).ConfigureAwait(false))
|
||||
{
|
||||
gid = StringUtils.GenerateRandomString(9);
|
||||
gid = StringUtils.GenerateRandomString(10);
|
||||
}
|
||||
gid = "SNOW-" + gid;
|
||||
|
||||
|
@@ -500,13 +500,13 @@ public partial class MareHub
|
||||
await Clients.Caller.Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^([a-z0-9_ '+&,\.\-\{\}]+\/)+([a-z0-9_ '+&,\.\-\{\}]+\.[a-z]{3,4})$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
[GeneratedRegex(@"^([a-z0-9_ '+&,\.\-\{\}]+\/)+([a-z0-9_ '+&,\.\-\{\}]+\.[a-z]{3,4})$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeoutMilliseconds: 1000) ]
|
||||
private static partial Regex GamePathRegex();
|
||||
|
||||
[GeneratedRegex(@"^[A-Z0-9]{40}$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
[GeneratedRegex(@"^[A-Z0-9]{40}$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeoutMilliseconds: 1000)]
|
||||
private static partial Regex HashRegex();
|
||||
|
||||
[GeneratedRegex("^[-a-zA-Z0-9@:%._\\+~#=]{1,256}[\\.,][a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$")]
|
||||
[GeneratedRegex("^[-a-zA-Z0-9@:%._\\+~#=]{1,256}[\\.,][a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$", RegexOptions.None, matchTimeoutMilliseconds: 1000)]
|
||||
private static partial Regex UrlRegex();
|
||||
|
||||
private ClientPair OppositeEntry(string otherUID) =>
|
||||
|
@@ -81,7 +81,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Information, "Welcome to Snowcloak! Current Online Users: " + _systemInfoService.SystemInfoDto.OnlineUsers).ConfigureAwait(false);
|
||||
|
||||
Context.Items["IsClientConnected"] = true;
|
||||
return new ConnectionDto(new UserData(dbUser.UID, string.IsNullOrWhiteSpace(dbUser.Alias) ? null : dbUser.Alias))
|
||||
{
|
||||
CurrentClientVersion = _expectedClientVersion,
|
||||
@@ -103,9 +103,16 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
[Authorize(Policy = "Authenticated")]
|
||||
public async Task<bool> CheckClientHealth()
|
||||
{
|
||||
await UpdateUserOnRedis().ConfigureAwait(false);
|
||||
// Key missing -> health check failed!
|
||||
var exists = await _redis.ExistsAsync("UID:" + UserUID).ConfigureAwait(false);
|
||||
if (!exists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Key exists -> health is good!
|
||||
await UpdateUserOnRedis().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Authenticated")]
|
||||
@@ -115,8 +122,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress(), UserCharaIdent));
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args("A client reconnected.", _contextAccessor.GetIpAddress(), UserCharaIdent));
|
||||
await UpdateUserOnRedis().ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
@@ -128,17 +134,19 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||
public override async Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
_mareMetrics.DecGaugeWithLabels(MetricsAPI.GaugeConnections, labels: Continent);
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress(), UserCharaIdent));
|
||||
if (exception != null)
|
||||
_logger.LogCallWarning(MareHubLogger.Args(_contextAccessor.GetIpAddress(), exception.Message, exception.StackTrace));
|
||||
|
||||
await GposeLobbyLeave().ConfigureAwait(false);
|
||||
await RemoveUserFromRedis().ConfigureAwait(false);
|
||||
|
||||
await SendOfflineToAllPairedUsers().ConfigureAwait(false);
|
||||
await _redis.SetRemoveAsync($"connections:{UserCharaIdent}", Context.ConnectionId).ConfigureAwait(false);
|
||||
var connections = await _redis.SetMembersAsync<string>($"connections:{UserCharaIdent}").ConfigureAwait(false);
|
||||
if (connections.Length == 0)
|
||||
{
|
||||
await GposeLobbyLeave().ConfigureAwait(false);
|
||||
await RemoveUserFromRedis().ConfigureAwait(false);
|
||||
await SendOfflineToAllPairedUsers().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<UserSecretsId>aspnet-MareSynchronosServer-BA82A12A-0B30-463C-801D-B7E81318CD50</UserSecretsId>
|
||||
<AssemblyVersion>1.1.0.0</AssemblyVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -25,12 +25,12 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.219">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.7.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -3,6 +3,7 @@ using MareSynchronos.API.SignalR;
|
||||
using MareSynchronosServer.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace MareSynchronosServer.Services;
|
||||
|
||||
@@ -153,7 +154,7 @@ public sealed class GPoseLobbyDistributionService : IHostedService, IDisposable
|
||||
if (!lobbyId.Value.Values.Any())
|
||||
continue;
|
||||
|
||||
var gposeLobbyUsers = await _redisDb.GetAsync<List<string>>($"GposeLobby:{lobbyId.Key}").ConfigureAwait(false);
|
||||
var gposeLobbyUsers = await _redisDb.GetAsync<List<string>>($"GposeLobby:{lobbyId.Key}", CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
if (gposeLobbyUsers == null)
|
||||
continue;
|
||||
|
||||
@@ -200,7 +201,7 @@ public sealed class GPoseLobbyDistributionService : IHostedService, IDisposable
|
||||
if (!lobbyId.Value.Values.Any())
|
||||
continue;
|
||||
|
||||
var gposeLobbyUsers = await _redisDb.GetAsync<List<string>>($"GposeLobby:{lobbyId.Key}").ConfigureAwait(false);
|
||||
var gposeLobbyUsers = await _redisDb.GetAsync<List<string>>($"GposeLobby:{lobbyId.Key}", CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
if (gposeLobbyUsers == null)
|
||||
continue;
|
||||
|
||||
|
@@ -7,6 +7,7 @@ using MareSynchronosShared.Services;
|
||||
using MareSynchronosShared.Utils.Configuration;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
|
||||
namespace MareSynchronosServer.Services;
|
||||
@@ -19,6 +20,7 @@ public class SystemInfoService : IHostedService, IDisposable
|
||||
private readonly ILogger<SystemInfoService> _logger;
|
||||
private readonly IHubContext<MareHub, IMareHub> _hubContext;
|
||||
private readonly IRedisDatabase _redis;
|
||||
private readonly IConnectionMultiplexer _connectionMultiplexer;
|
||||
private Timer _timer;
|
||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||
|
||||
@@ -31,6 +33,7 @@ public class SystemInfoService : IHostedService, IDisposable
|
||||
_logger = logger;
|
||||
_hubContext = hubContext;
|
||||
_redis = redisDb;
|
||||
_connectionMultiplexer = _redis.Database.Multiplexer;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
@@ -52,8 +55,9 @@ public class SystemInfoService : IHostedService, IDisposable
|
||||
|
||||
_mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableWorkerThreads, workerThreads);
|
||||
_mareMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableIOWorkerThreads, ioThreads);
|
||||
var onlineUsers = _connectionMultiplexer.GetServers().Where(server => !server.IsReplica).SelectMany(server => server.Keys(0, "UID:*", pageSize:1000)).Count();
|
||||
|
||||
|
||||
var onlineUsers = (_redis.SearchKeysAsync("UID:*").GetAwaiter().GetResult()).Count();
|
||||
SystemInfoDto = new SystemInfoDto()
|
||||
{
|
||||
OnlineUsers = onlineUsers,
|
||||
@@ -63,7 +67,7 @@ public class SystemInfoService : IHostedService, IDisposable
|
||||
{
|
||||
_logger.LogTrace("Sending System Info, Online Users: {onlineUsers}", onlineUsers);
|
||||
|
||||
_hubContext.Clients.All.Client_UpdateSystemInfo(SystemInfoDto);
|
||||
_ = _hubContext.Clients.All.Client_UpdateSystemInfo(SystemInfoDto);
|
||||
|
||||
using var scope = _services.CreateScope();
|
||||
using var db = scope.ServiceProvider.GetService<MareDbContext>()!;
|
||||
|
@@ -142,19 +142,22 @@ public class Startup
|
||||
|
||||
var options = ConfigurationOptions.Parse(redisConnection);
|
||||
|
||||
var endpoint = options.EndPoints[0];
|
||||
string address = "";
|
||||
int port = 0;
|
||||
if (endpoint is DnsEndPoint dnsEndPoint) { address = dnsEndPoint.Host; port = dnsEndPoint.Port; }
|
||||
if (endpoint is IPEndPoint ipEndPoint) { address = ipEndPoint.Address.ToString(); port = ipEndPoint.Port; }
|
||||
var hosts = options.EndPoints
|
||||
.Select(ep =>
|
||||
{
|
||||
if (ep is DnsEndPoint dns) return (host: dns.Host, port: dns.Port);
|
||||
if (ep is IPEndPoint ip) return (host: ip.Address.ToString(), port: ip.Port);
|
||||
return (host: (string?)null, port: 0);
|
||||
})
|
||||
.Where(x => x.host != null)
|
||||
.Distinct()
|
||||
.Select(x => new RedisHost { Host = x.host!, Port = x.port })
|
||||
.ToArray();
|
||||
var redisConfiguration = new RedisConfiguration()
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
AbortOnConnectFail = false,
|
||||
KeyPrefix = "",
|
||||
Hosts = new RedisHost[]
|
||||
{
|
||||
new RedisHost(){ Host = address, Port = port },
|
||||
},
|
||||
Hosts = hosts,
|
||||
AllowAdmin = true,
|
||||
ConnectTimeout = options.ConnectTimeout,
|
||||
Database = 0,
|
||||
@@ -164,11 +167,11 @@ public class Startup
|
||||
{
|
||||
Mode = ServerEnumerationStrategy.ModeOptions.All,
|
||||
TargetRole = ServerEnumerationStrategy.TargetRoleOptions.Any,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.Throw,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.IgnoreIfOtherAvailable,
|
||||
},
|
||||
MaxValueLength = 1024,
|
||||
PoolSize = mareConfig.GetValue(nameof(ServerConfiguration.RedisPool), 50),
|
||||
SyncTimeout = options.SyncTimeout,
|
||||
SyncTimeout = Math.Max(options.SyncTimeout, 10000),
|
||||
};
|
||||
|
||||
services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfiguration);
|
||||
@@ -242,7 +245,7 @@ public class Startup
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
builder.MigrationsAssembly("MareSynchronosShared");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
}, mareConfig.GetValue(nameof(MareConfigurationBase.DbContextPoolSize), 1024));
|
||||
services.AddDbContextFactory<MareDbContext>(options =>
|
||||
{
|
||||
@@ -251,7 +254,7 @@ public class Startup
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
builder.MigrationsAssembly("MareSynchronosShared");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -82,7 +82,7 @@ public class MareModule : InteractionModuleBase
|
||||
var hasValidUid = false;
|
||||
while (!hasValidUid)
|
||||
{
|
||||
var uid = StringUtils.GenerateRandomString(7);
|
||||
var uid = StringUtils.GenerateRandomString(8);
|
||||
if (db.Users.Any(u => u.UID == uid || u.Alias == uid)) continue;
|
||||
user.UID = uid;
|
||||
hasValidUid = true;
|
||||
@@ -510,7 +510,7 @@ public class MareModule : InteractionModuleBase
|
||||
var hasValidUid = false;
|
||||
while (!hasValidUid)
|
||||
{
|
||||
var uid = StringUtils.GenerateRandomString(7);
|
||||
var uid = StringUtils.GenerateRandomString(8);
|
||||
if (await db.Users.AnyAsync(u => u.UID == uid || u.Alias == uid).ConfigureAwait(false)) continue;
|
||||
newUser.UID = uid;
|
||||
hasValidUid = true;
|
||||
@@ -646,7 +646,7 @@ public class MareModule : InteractionModuleBase
|
||||
var auth = await db.Auth.Include(u => u.PrimaryUser).SingleOrDefaultAsync(u => u.UserUID == dbUser.UID).ConfigureAwait(false);
|
||||
var groups = await db.Groups.Where(g => g.OwnerUID == dbUser.UID).ToListAsync().ConfigureAwait(false);
|
||||
var groupsJoined = await db.GroupPairs.Where(g => g.GroupUserUID == dbUser.UID).ToListAsync().ConfigureAwait(false);
|
||||
var identity = await _connectionMultiplexer.GetDatabase().StringGetAsync("UID:" + dbUser.UID).ConfigureAwait(false);
|
||||
var identity = await _connectionMultiplexer.GetDatabase().StringGetAsync("UID:" + dbUser.UID, CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
|
||||
eb.WithTitle("User Information");
|
||||
eb.WithDescription("This is the user information for Discord User <@" + userToCheckForDiscordId + ">" + Environment.NewLine + Environment.NewLine
|
||||
@@ -1195,7 +1195,7 @@ public class MareModule : InteractionModuleBase
|
||||
var hasValidUid = false;
|
||||
while (!hasValidUid)
|
||||
{
|
||||
var uid = StringUtils.GenerateRandomString(7);
|
||||
var uid = StringUtils.GenerateRandomString(8);
|
||||
if (db.Users.Any(u => u.UID == uid || u.Alias == uid)) continue;
|
||||
user.UID = uid;
|
||||
hasValidUid = true;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="3.18.0" />
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.219">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -45,7 +45,7 @@ public class Startup
|
||||
{
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
}, Configuration.GetValue(nameof(MareConfigurationBase.DbContextPoolSize), 1024));
|
||||
|
||||
services.AddSingleton(m => new MareMetrics(m.GetService<ILogger<MareMetrics>>(), new List<string> { },
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ByteSize" Version="2.1.2" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Karambolo.Extensions.Logging.File" Version="3.6.3" />
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212">
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.219">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -30,25 +31,25 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.19">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.19" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.7.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="prometheus-net" Version="8.2.1" />
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.9.11" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="10.2.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="10.2.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.System.Text.Json" Version="10.2.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.7.1" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.9.17" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="11.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="11.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.System.Text.Json" Version="11.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -30,7 +30,7 @@ public class UserRequirementHandler : AuthorizationHandler<UserRequirement, HubI
|
||||
|
||||
if ((requirement.Requirements & UserRequirements.Identified) is UserRequirements.Identified)
|
||||
{
|
||||
var ident = await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
||||
var ident = await _redis.GetAsync<string>("UID:" + uid, CommandFlags.PreferReplica).ConfigureAwait(false);
|
||||
if (ident == RedisValue.EmptyString) context.Fail();
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.8" />
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.219">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -108,7 +108,7 @@ public class Startup
|
||||
{
|
||||
builder.MigrationsHistoryTable("_efmigrationshistory", "public");
|
||||
}).UseSnakeCaseNamingConvention();
|
||||
options.EnableThreadSafetyChecks(false);
|
||||
options.EnableThreadSafetyChecks();
|
||||
}, mareConfig.GetValue(nameof(MareConfigurationBase.DbContextPoolSize), 1024));
|
||||
|
||||
var signalRServiceBuilder = services.AddSignalR(hubOptions =>
|
||||
@@ -142,19 +142,22 @@ public class Startup
|
||||
|
||||
var options = ConfigurationOptions.Parse(redisConnection);
|
||||
|
||||
var endpoint = options.EndPoints[0];
|
||||
string address = "";
|
||||
int port = 0;
|
||||
if (endpoint is DnsEndPoint dnsEndPoint) { address = dnsEndPoint.Host; port = dnsEndPoint.Port; }
|
||||
if (endpoint is IPEndPoint ipEndPoint) { address = ipEndPoint.Address.ToString(); port = ipEndPoint.Port; }
|
||||
var hosts = options.EndPoints
|
||||
.Select(ep =>
|
||||
{
|
||||
if (ep is DnsEndPoint dns) return (host: dns.Host, port: dns.Port);
|
||||
if (ep is IPEndPoint ip) return (host: ip.Address.ToString(), port: ip.Port);
|
||||
return (host: (string?)null, port: 0);
|
||||
})
|
||||
.Where(x => x.host != null)
|
||||
.Distinct()
|
||||
.Select(x => new RedisHost { Host = x.host!, Port = x.port })
|
||||
.ToArray();
|
||||
var redisConfiguration = new RedisConfiguration()
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
AbortOnConnectFail = false,
|
||||
KeyPrefix = "",
|
||||
Hosts = new RedisHost[]
|
||||
{
|
||||
new RedisHost(){ Host = address, Port = port },
|
||||
},
|
||||
Hosts = hosts,
|
||||
AllowAdmin = true,
|
||||
ConnectTimeout = options.ConnectTimeout,
|
||||
Database = 0,
|
||||
@@ -164,11 +167,11 @@ public class Startup
|
||||
{
|
||||
Mode = ServerEnumerationStrategy.ModeOptions.All,
|
||||
TargetRole = ServerEnumerationStrategy.TargetRoleOptions.Any,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.Throw,
|
||||
UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.IgnoreIfOtherAvailable,
|
||||
},
|
||||
MaxValueLength = 1024,
|
||||
PoolSize = mareConfig.GetValue(nameof(ServerConfiguration.RedisPool), 50),
|
||||
SyncTimeout = options.SyncTimeout,
|
||||
SyncTimeout = Math.Max(options.SyncTimeout, 10000),
|
||||
};
|
||||
|
||||
services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfiguration);
|
||||
|
Reference in New Issue
Block a user