rework server responsibilities (#18)
* rework server responsibilities add remote configuration * start metrics only when compiled as not debug * add some more logging to discord bot * fixes of some casts * make metrics port configurable, minor fixes * add docker bullshit * md formatting * adjustments to docker stuff * fix docker json files, fix some stuff in discord bot, add /useradd for Discord bot * adjust docker configs and fix sharded.bat * fixes for logs, cache file provider repeat trying to open filestream Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using MareSynchronosServer;
|
||||
using MareSynchronosShared.Data;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -13,7 +12,6 @@ public class SecretKeyAuthenticationHandler : AuthenticationHandler<Authenticati
|
||||
{
|
||||
public const string AuthScheme = "SecretKeyGrpcAuth";
|
||||
|
||||
private readonly MareDbContext _mareDbContext;
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly SecretKeyAuthenticatorService secretKeyAuthenticatorService;
|
||||
|
||||
@@ -26,6 +24,12 @@ public class SecretKeyAuthenticationHandler : AuthenticationHandler<Authenticati
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
var endpoint = Context.GetEndpoint();
|
||||
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
|
||||
{
|
||||
return AuthenticateResult.NoResult();
|
||||
}
|
||||
|
||||
if (!Request.Headers.TryGetValue("Authorization", out var authHeader))
|
||||
{
|
||||
authHeader = string.Empty;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using MareSynchronosShared.Services;
|
||||
using MareSynchronosShared.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -13,25 +14,15 @@ public class SecretKeyAuthenticatorService
|
||||
{
|
||||
private readonly MareMetrics _metrics;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IConfigurationService<MareConfigurationAuthBase> _configurationService;
|
||||
private readonly ILogger<SecretKeyAuthenticatorService> _logger;
|
||||
private readonly ConcurrentDictionary<string, SecretKeyAuthReply> _cachedPositiveResponses = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, SecretKeyFailedAuthorization?> _failedAuthorizations = new(StringComparer.Ordinal);
|
||||
private readonly int _failedAttemptsForTempBan;
|
||||
private readonly int _tempBanMinutes;
|
||||
private readonly List<string> _whitelistedIps;
|
||||
|
||||
public SecretKeyAuthenticatorService(MareMetrics metrics, IServiceScopeFactory serviceScopeFactory, IOptions<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger)
|
||||
public SecretKeyAuthenticatorService(MareMetrics metrics, IServiceScopeFactory serviceScopeFactory, IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
var config = configuration.Value;
|
||||
_failedAttemptsForTempBan = config.FailedAuthForTempBan;
|
||||
_tempBanMinutes = config.TempBanDurationInMinutes;
|
||||
_whitelistedIps = config.WhitelistedIps;
|
||||
foreach (var ip in _whitelistedIps)
|
||||
{
|
||||
logger.LogInformation("Whitelisted IP: " + ip);
|
||||
}
|
||||
|
||||
_configurationService = configuration;
|
||||
_metrics = metrics;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
@@ -46,7 +37,8 @@ public class SecretKeyAuthenticatorService
|
||||
return cachedPositiveResponse;
|
||||
}
|
||||
|
||||
if (_failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization) && existingFailedAuthorization.FailedAttempts > _failedAttemptsForTempBan)
|
||||
if (_failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization)
|
||||
&& existingFailedAuthorization.FailedAttempts > _configurationService.GetValueOrDefault(nameof(MareConfigurationAuthBase.FailedAuthForTempBan), 5))
|
||||
{
|
||||
if (existingFailedAuthorization.ResetTask == null)
|
||||
{
|
||||
@@ -54,7 +46,7 @@ public class SecretKeyAuthenticatorService
|
||||
|
||||
existingFailedAuthorization.ResetTask = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(_tempBanMinutes)).ConfigureAwait(false);
|
||||
await Task.Delay(TimeSpan.FromMinutes(_configurationService.GetValueOrDefault(nameof(MareConfigurationAuthBase.TempBanDurationInMinutes), 5))).ConfigureAwait(false);
|
||||
|
||||
}).ContinueWith((t) =>
|
||||
{
|
||||
@@ -96,7 +88,8 @@ public class SecretKeyAuthenticatorService
|
||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationFailures);
|
||||
|
||||
_logger.LogWarning("Failed authorization from {ip}", ip);
|
||||
if (!_whitelistedIps.Any(w => ip.Contains(w, StringComparison.OrdinalIgnoreCase)))
|
||||
var whitelisted = _configurationService.GetValueOrDefault(nameof(MareConfigurationAuthBase.WhitelistedIps), new List<string>());
|
||||
if (!whitelisted.Any(w => ip.Contains(w, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (_failedAuthorizations.TryGetValue(ip, out var auth))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MareSynchronosServer;
|
||||
namespace MareSynchronosShared;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
|
||||
@@ -20,6 +20,19 @@ service IdentificationService {
|
||||
rpc ReceiveStreamIdentStatusChange (ServerMessage) returns (stream IdentChange);
|
||||
}
|
||||
|
||||
service ConfigurationService {
|
||||
rpc GetConfigurationEntry (KeyMessage) returns (ValueMessage);
|
||||
}
|
||||
|
||||
message KeyMessage {
|
||||
string key = 1;
|
||||
string default = 2;
|
||||
}
|
||||
|
||||
message ValueMessage {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message Empty { }
|
||||
|
||||
message MultiUidMessage {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Grpc.Core;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Utils;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
[Authorize]
|
||||
[AllowAnonymous]
|
||||
public class GrpcConfigurationService<T> : ConfigurationService.ConfigurationServiceBase where T : class, IMareConfiguration
|
||||
{
|
||||
private readonly T _config;
|
||||
private readonly ILogger<GrpcConfigurationService<T>> logger;
|
||||
|
||||
public GrpcConfigurationService(IOptions<T> config, ILogger<GrpcConfigurationService<T>> logger)
|
||||
{
|
||||
_config = config.Value;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public override Task<ValueMessage> GetConfigurationEntry(KeyMessage request, ServerCallContext context)
|
||||
{
|
||||
logger.LogInformation("Remote requested {key}", request.Key);
|
||||
var returnVal = _config.SerializeValue(request.Key, request.Default);
|
||||
return Task.FromResult(new ValueMessage() { Value = returnVal });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using MareSynchronosShared.Utils;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
public interface IConfigurationService<T> where T : class, IMareConfiguration
|
||||
{
|
||||
bool IsMain { get; }
|
||||
T1 GetValue<T1>(string key);
|
||||
T1 GetValueOrDefault<T1>(string key, T1 defaultValue);
|
||||
string ToString();
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using Grpc.Net.ClientFactory;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using static MareSynchronosShared.Protos.ConfigurationService;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
public class MareConfigurationServiceClient<T> : IConfigurationService<T> where T : class, IMareConfiguration
|
||||
{
|
||||
internal record RemoteCachedEntry(object Value, DateTime Inserted);
|
||||
|
||||
private readonly T _config;
|
||||
private readonly ConcurrentDictionary<string, RemoteCachedEntry> _cachedRemoteProperties = new(StringComparer.Ordinal);
|
||||
private readonly ILogger<MareConfigurationServiceClient<T>> _logger;
|
||||
private readonly ConfigurationServiceClient _configurationServiceClient;
|
||||
|
||||
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptions<T> config, ConfigurationServiceClient configurationServiceClient)
|
||||
{
|
||||
_config = config.Value;
|
||||
_logger = logger;
|
||||
_configurationServiceClient = configurationServiceClient;
|
||||
}
|
||||
|
||||
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptions<T> config, GrpcClientFactory grpcClientFactory, string grpcClientName)
|
||||
{
|
||||
_config = config.Value;
|
||||
_logger = logger;
|
||||
_configurationServiceClient = grpcClientFactory.CreateClient<ConfigurationServiceClient>(grpcClientName);
|
||||
}
|
||||
|
||||
public bool IsMain => false;
|
||||
|
||||
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
|
||||
{
|
||||
var prop = _config.GetType().GetProperty(key);
|
||||
if (prop == null) return defaultValue;
|
||||
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}");
|
||||
bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
|
||||
if (isRemote)
|
||||
{
|
||||
bool isCurrent = false;
|
||||
if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30))
|
||||
{
|
||||
isCurrent = true;
|
||||
}
|
||||
|
||||
if (!isCurrent)
|
||||
{
|
||||
var result = GetValueFromGrpc(key, defaultValue, prop.PropertyType);
|
||||
if (result == null) return defaultValue;
|
||||
_cachedRemoteProperties[key] = result;
|
||||
return (T1)_cachedRemoteProperties[key].Value;
|
||||
}
|
||||
}
|
||||
|
||||
var value = prop.GetValue(_config);
|
||||
return (T1)value;
|
||||
}
|
||||
|
||||
private RemoteCachedEntry? GetValueFromGrpc(string key, object defaultValue, Type t)
|
||||
{
|
||||
// grab stuff from grpc
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Getting {key} from Grpc", key);
|
||||
var response = _configurationServiceClient.GetConfigurationEntry(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) });
|
||||
_logger.LogInformation("Grpc Response for {key} = {value}", key, response.Value);
|
||||
return new RemoteCachedEntry(JsonSerializer.Deserialize(response.Value, t), DateTime.Now);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public T1 GetValue<T1>(string key)
|
||||
{
|
||||
var prop = _config.GetType().GetProperty(key);
|
||||
if (prop == null) throw new KeyNotFoundException(key);
|
||||
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}");
|
||||
bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
|
||||
if (isRemote)
|
||||
{
|
||||
bool isCurrent = false;
|
||||
if (_cachedRemoteProperties.TryGetValue(key, out var existingEntry) && existingEntry.Inserted > DateTime.Now - TimeSpan.FromMinutes(30))
|
||||
{
|
||||
isCurrent = true;
|
||||
}
|
||||
|
||||
if (!isCurrent)
|
||||
{
|
||||
var result = GetValueFromGrpc(key, null, prop.PropertyType);
|
||||
if (result == null) throw new KeyNotFoundException(key);
|
||||
_cachedRemoteProperties[key] = result;
|
||||
}
|
||||
|
||||
if (!_cachedRemoteProperties.ContainsKey(key)) throw new KeyNotFoundException(key);
|
||||
|
||||
return (T1)_cachedRemoteProperties[key].Value;
|
||||
}
|
||||
|
||||
var value = prop.GetValue(_config);
|
||||
return (T1)value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var props = _config.GetType().GetProperties();
|
||||
StringBuilder sb = new();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any();
|
||||
var mi = GetType().GetMethod(nameof(GetValue)).MakeGenericMethod(prop.PropertyType);
|
||||
var val = mi.Invoke(this, new[] { prop.Name });
|
||||
var value = isRemote ? val : prop.GetValue(_config);
|
||||
sb.AppendLine($"{prop.Name} (IsRemote: {isRemote}) => {value}");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using MareSynchronosShared.Utils;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
public class MareConfigurationServiceServer<T> : IConfigurationService<T> where T : class, IMareConfiguration
|
||||
{
|
||||
private readonly T _config;
|
||||
public bool IsMain => true;
|
||||
|
||||
public MareConfigurationServiceServer(IOptions<T> config)
|
||||
{
|
||||
_config = config.Value;
|
||||
}
|
||||
|
||||
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
|
||||
{
|
||||
return _config.GetValueOrDefault<T1>(key, defaultValue);
|
||||
}
|
||||
|
||||
public T1 GetValue<T1>(string key)
|
||||
{
|
||||
return _config.GetValue<T1>(key);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var props = _config.GetType().GetProperties();
|
||||
StringBuilder sb = new();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name} (IsRemote: {prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()}) => {prop.GetValue(_config)}");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public interface IMareConfiguration
|
||||
{
|
||||
T GetValueOrDefault<T>(string key, T defaultValue);
|
||||
T GetValue<T>(string key);
|
||||
string SerializeValue(string key, string defaultValue);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class MareConfigurationAuthBase : MareConfigurationBase
|
||||
{
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
[RemoteConfiguration]
|
||||
public int FailedAuthForTempBan { get; set; } = 5;
|
||||
[RemoteConfiguration]
|
||||
public int TempBanDurationInMinutes { get; set; } = 5;
|
||||
[RemoteConfiguration]
|
||||
public List<string> WhitelistedIps { get; set; } = new();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}");
|
||||
sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}");
|
||||
sb.AppendLine($"{nameof(WhitelistedIps)} => {string.Join(", ", WhitelistedIps)}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,46 @@
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class MareConfigurationBase
|
||||
public class MareConfigurationBase : IMareConfiguration
|
||||
{
|
||||
public int DbContextPoolSize { get; set; } = 100;
|
||||
public string ShardName { get; set; } = string.Empty;
|
||||
public int MetricsPort { get; set; } = 4981;
|
||||
|
||||
public T GetValue<T>(string key)
|
||||
{
|
||||
var prop = GetType().GetProperty(key);
|
||||
if (prop == null) throw new KeyNotFoundException(key);
|
||||
if (prop.PropertyType != typeof(T)) throw new ArgumentException($"Requested {key} with T:{typeof(T)}, where {key} is {prop.PropertyType}");
|
||||
return (T)prop.GetValue(this);
|
||||
}
|
||||
|
||||
public T GetValueOrDefault<T>(string key, T defaultValue)
|
||||
{
|
||||
var prop = GetType().GetProperty(key);
|
||||
if (prop.PropertyType != typeof(T)) throw new ArgumentException($"Requested {key} with T:{typeof(T)}, where {key} is {prop.PropertyType}");
|
||||
if (prop == null) return defaultValue;
|
||||
return (T)prop.GetValue(this);
|
||||
}
|
||||
|
||||
public string SerializeValue(string key, string defaultValue)
|
||||
{
|
||||
var prop = GetType().GetProperty(key);
|
||||
if (prop == null) return defaultValue;
|
||||
if (prop.GetCustomAttribute<RemoteConfigurationAttribute>() == null) return defaultValue;
|
||||
return JsonSerializer.Serialize(prop.GetValue(this), prop.PropertyType);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(ShardName)} => {ShardName}");
|
||||
sb.AppendLine($"{nameof(DbContextPoolSize)} => {DbContextPoolSize}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class MareConfigurationAuthBase : MareConfigurationBase
|
||||
{
|
||||
public int DbContextPoolSize { get; set; } = 100;
|
||||
public int FailedAuthForTempBan { get; set; } = 5;
|
||||
public int TempBanDurationInMinutes { get; set; } = 5;
|
||||
public List<string> WhitelistedIps { get; set; } = new();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}");
|
||||
sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}");
|
||||
sb.AppendLine($"{nameof(WhitelistedIps)} => {string.Join(", ", WhitelistedIps)}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class RemoteConfigurationAttribute : Attribute { }
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class ServerConfiguration : MareConfigurationAuthBase
|
||||
{
|
||||
public string RedisConnectionString { get; set; } = string.Empty;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public Uri CdnFullUrl { get; set; } = null;
|
||||
[RemoteConfiguration]
|
||||
public Uri StaticFileServiceAddress { get; set; } = null;
|
||||
[RemoteConfiguration]
|
||||
public int MaxExistingGroupsByUser { get; set; } = 3;
|
||||
[RemoteConfiguration]
|
||||
public int MaxJoinedGroupsByUser { get; set; } = 6;
|
||||
[RemoteConfiguration]
|
||||
public int MaxGroupUserCount { get; set; } = 100;
|
||||
[RemoteConfiguration]
|
||||
public bool PurgeUnusedAccounts { get; set; } = false;
|
||||
[RemoteConfiguration]
|
||||
public int PurgeUnusedAccountsPeriodInDays { get; set; } = 14;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(CdnFullUrl)} => {CdnFullUrl}");
|
||||
sb.AppendLine($"{nameof(StaticFileServiceAddress)} => {StaticFileServiceAddress}");
|
||||
sb.AppendLine($"{nameof(RedisConnectionString)} => {RedisConnectionString}");
|
||||
sb.AppendLine($"{nameof(MaxExistingGroupsByUser)} => {MaxExistingGroupsByUser}");
|
||||
sb.AppendLine($"{nameof(MaxJoinedGroupsByUser)} => {MaxJoinedGroupsByUser}");
|
||||
sb.AppendLine($"{nameof(MaxGroupUserCount)} => {MaxGroupUserCount}");
|
||||
sb.AppendLine($"{nameof(PurgeUnusedAccounts)} => {PurgeUnusedAccounts}");
|
||||
sb.AppendLine($"{nameof(PurgeUnusedAccountsPeriodInDays)} => {PurgeUnusedAccountsPeriodInDays}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class ServicesConfiguration : MareConfigurationBase
|
||||
{
|
||||
public string DiscordBotToken { get; set; } = string.Empty;
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(DiscordBotToken)} => {DiscordBotToken}");
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,68 @@
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public static class SharedDbFunctions
|
||||
{
|
||||
public static async Task PurgeUser(ILogger _logger, User user, MareDbContext dbContext, int maxGroupsByUser)
|
||||
{
|
||||
_logger.LogInformation("Purging user: {uid}", user.UID);
|
||||
|
||||
var lodestone = dbContext.LodeStoneAuth.SingleOrDefault(a => a.User.UID == user.UID);
|
||||
|
||||
if (lodestone != null)
|
||||
{
|
||||
dbContext.Remove(lodestone);
|
||||
}
|
||||
|
||||
var auth = dbContext.Auth.Single(a => a.UserUID == user.UID);
|
||||
|
||||
var userFiles = dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == user.UID).ToList();
|
||||
dbContext.Files.RemoveRange(userFiles);
|
||||
|
||||
var ownPairData = dbContext.ClientPairs.Where(u => u.User.UID == user.UID).ToList();
|
||||
dbContext.ClientPairs.RemoveRange(ownPairData);
|
||||
var otherPairData = dbContext.ClientPairs.Include(u => u.User)
|
||||
.Where(u => u.OtherUser.UID == user.UID).ToList();
|
||||
dbContext.ClientPairs.RemoveRange(otherPairData);
|
||||
|
||||
var userJoinedGroups = await dbContext.GroupPairs.Include(g => g.Group).Where(u => u.GroupUserUID == user.UID).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var userGroupPair in userJoinedGroups)
|
||||
{
|
||||
bool ownerHasLeft = string.Equals(userGroupPair.Group.OwnerUID, user.UID, StringComparison.Ordinal);
|
||||
|
||||
if (ownerHasLeft)
|
||||
{
|
||||
var groupPairs = await dbContext.GroupPairs.Where(g => g.GroupGID == userGroupPair.GroupGID && g.GroupUserUID != user.UID).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
if (!groupPairs.Any())
|
||||
{
|
||||
_logger.LogInformation("Group {gid} has no new owner, deleting", userGroupPair.GroupGID);
|
||||
dbContext.Groups.Remove(userGroupPair.Group);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = await MigrateOrDeleteGroup(dbContext, userGroupPair.Group, groupPairs, maxGroupsByUser).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
dbContext.GroupPairs.Remove(userGroupPair);
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_logger.LogInformation("User purged: {uid}", user.UID);
|
||||
|
||||
dbContext.Auth.Remove(auth);
|
||||
dbContext.Users.Remove(user);
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task<(bool, string)> MigrateOrDeleteGroup(MareDbContext context, Group group, List<GroupPair> groupPairs, int maxGroupsByUser)
|
||||
{
|
||||
bool groupHasMigrated = false;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using MareSynchronosShared.Utils;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosStaticFilesServer;
|
||||
|
||||
public class StaticFilesServerConfiguration : MareConfigurationBase
|
||||
{
|
||||
public Uri FileServerGrpcAddress { get; set; } = null;
|
||||
public int ForcedDeletionOfFilesAfterHours { get; set; } = -1;
|
||||
public double CacheSizeHardLimitInGiB { get; set; } = -1;
|
||||
public int UnusedFileRetentionPeriodInDays { get; set; } = 14;
|
||||
public string CacheDirectory { get; set; }
|
||||
public Uri? RemoteCacheSourceUri { get; set; } = null;
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(FileServerGrpcAddress)} => {FileServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(ForcedDeletionOfFilesAfterHours)} => {ForcedDeletionOfFilesAfterHours}");
|
||||
sb.AppendLine($"{nameof(CacheSizeHardLimitInGiB)} => {CacheSizeHardLimitInGiB}");
|
||||
sb.AppendLine($"{nameof(UnusedFileRetentionPeriodInDays)} => {UnusedFileRetentionPeriodInDays}");
|
||||
sb.AppendLine($"{nameof(CacheDirectory)} => {CacheDirectory}");
|
||||
sb.AppendLine($"{nameof(RemoteCacheSourceUri)} => {RemoteCacheSourceUri}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user