- reload configs on change
- remove grpc config service replace with http - add messaging to channel
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronosServer.Utils;
|
||||
using MareSynchronosShared.Metrics;
|
||||
|
||||
@@ -55,8 +55,15 @@ public class Program
|
||||
}
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
public static IHostBuilder CreateHostBuilder(string[] args)
|
||||
{
|
||||
var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddConsole();
|
||||
});
|
||||
var logger = loggerFactory.CreateLogger<Startup>();
|
||||
return Host.CreateDefaultBuilder(args)
|
||||
.UseSystemd()
|
||||
.UseConsoleLifetime()
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
@@ -67,6 +74,7 @@ public class Program
|
||||
builder.AddConfiguration(ctx.Configuration.GetSection("Logging"));
|
||||
builder.AddFile(o => o.RootPath = AppContext.BaseDirectory);
|
||||
});
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.UseStartup(ctx => new Startup(ctx.Configuration, logger));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ using MareSynchronosServer.RequirementHandlers;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosShared.Services;
|
||||
using Prometheus;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Grpc.Net.ClientFactory;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
@@ -26,14 +24,19 @@ using StackExchange.Redis.Extensions.System.Text.Json;
|
||||
using MareSynchronos.API.SignalR;
|
||||
using MessagePack;
|
||||
using MessagePack.Resolvers;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using MareSynchronosServer.Controllers;
|
||||
|
||||
namespace MareSynchronosServer;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
private readonly ILogger<Startup> _logger;
|
||||
|
||||
public Startup(IConfiguration configuration, ILogger<Startup> logger)
|
||||
{
|
||||
Configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
@@ -68,16 +71,27 @@ public class Startup
|
||||
ConfigureMareServices(services, mareConfig);
|
||||
|
||||
services.AddHealthChecks();
|
||||
services.AddControllers();
|
||||
services.AddControllers().ConfigureApplicationPartManager(a =>
|
||||
{
|
||||
a.FeatureProviders.Remove(a.FeatureProviders.OfType<ControllerFeatureProvider>().First());
|
||||
if (mareConfig.GetValue<Uri>(nameof(ServerConfiguration.MainServerAddress), defaultValue: null) == null)
|
||||
{
|
||||
a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(MareServerConfigurationController), typeof(MareAuthBaseConfigurationController), typeof(JwtController)));
|
||||
}
|
||||
else
|
||||
{
|
||||
a.FeatureProviders.Add(new AllowedControllersFeatureProvider());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void ConfigureMareServices(IServiceCollection services, IConfigurationSection mareConfig)
|
||||
private void ConfigureMareServices(IServiceCollection services, IConfigurationSection mareConfig)
|
||||
{
|
||||
bool isMainServer = mareConfig.GetValue<Uri>(nameof(ServerConfiguration.MainServerGrpcAddress), defaultValue: null) == null;
|
||||
bool isMainServer = mareConfig.GetValue<Uri>(nameof(ServerConfiguration.MainServerAddress), defaultValue: null) == null;
|
||||
|
||||
services.Configure<ServerConfiguration>(mareConfig);
|
||||
services.Configure<MareConfigurationBase>(mareConfig);
|
||||
services.Configure<MareConfigurationAuthBase>(mareConfig);
|
||||
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.Configure<MareConfigurationBase>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
|
||||
services.AddSingleton<ServerTokenGenerator>();
|
||||
services.AddSingleton<SystemInfoService>();
|
||||
@@ -217,6 +231,7 @@ public class Startup
|
||||
{
|
||||
policy.AddRequirements(new UserRequirement(UserRequirements.Identified | UserRequirements.Moderator | UserRequirements.Administrator));
|
||||
});
|
||||
options.AddPolicy("Internal", new AuthorizationPolicyBuilder().RequireClaim(MareClaimTypes.Internal, "true").Build());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -264,34 +279,9 @@ public class Startup
|
||||
{
|
||||
if (!isMainServer)
|
||||
{
|
||||
var noRetryConfig = new MethodConfig
|
||||
{
|
||||
Names = { MethodName.Default },
|
||||
RetryPolicy = null,
|
||||
};
|
||||
services.AddSingleton<IConfigurationService<ServerConfiguration>, MareConfigurationServiceClient<ServerConfiguration>>();
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
|
||||
|
||||
services.AddGrpcClient<ConfigurationService.ConfigurationServiceClient>("MainServer", c =>
|
||||
{
|
||||
c.Address = new Uri(mareConfig.GetValue<string>(nameof(ServerConfiguration.MainServerGrpcAddress)));
|
||||
}).ConfigureChannel(c =>
|
||||
{
|
||||
c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } };
|
||||
c.HttpHandler = new SocketsHttpHandler()
|
||||
{
|
||||
EnableMultipleHttp2Connections = true,
|
||||
};
|
||||
});
|
||||
|
||||
services.AddSingleton<IConfigurationService<ServerConfiguration>>(c => new MareConfigurationServiceClient<ServerConfiguration>(
|
||||
c.GetService<ILogger<MareConfigurationServiceClient<ServerConfiguration>>>(),
|
||||
c.GetService<IOptions<ServerConfiguration>>(),
|
||||
c.GetService<GrpcClientFactory>(),
|
||||
"MainServer"));
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>>(c => new MareConfigurationServiceClient<MareConfigurationAuthBase>(
|
||||
c.GetService<ILogger<MareConfigurationServiceClient<MareConfigurationAuthBase>>>(),
|
||||
c.GetService<IOptions<MareConfigurationAuthBase>>(),
|
||||
c.GetService<GrpcClientFactory>(),
|
||||
"MainServer"));
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>());
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
|
||||
}
|
||||
@@ -358,12 +348,18 @@ public class Startup
|
||||
|
||||
if (config.IsMain)
|
||||
{
|
||||
endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous();
|
||||
endpoints.MapGrpcService<GrpcClientMessageService>().AllowAnonymous();
|
||||
}
|
||||
|
||||
endpoints.MapHealthChecks("/health").AllowAnonymous();
|
||||
endpoints.MapControllers().AllowAnonymous();
|
||||
endpoints.MapControllers();
|
||||
|
||||
foreach (var source in endpoints.DataSources.SelectMany(e => e.Endpoints).Cast<RouteEndpoint>())
|
||||
{
|
||||
if (source == null) continue;
|
||||
_logger.LogInformation("Endpoint: {url} ", source.RoutePattern.RawText);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,18 +29,21 @@ public class MareModule : InteractionModuleBase
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly DiscordBotServices _botServices;
|
||||
private readonly IConfigurationService<ServerConfiguration> _mareClientConfigurationService;
|
||||
private readonly IConfigurationService<ServicesConfiguration> _mareServicesConfiguration;
|
||||
private readonly GrpcClientFactory _grpcClientFactory;
|
||||
private readonly IConnectionMultiplexer _connectionMultiplexer;
|
||||
private Random random = new();
|
||||
|
||||
public MareModule(ILogger<MareModule> logger, IServiceProvider services, DiscordBotServices botServices,
|
||||
IConfigurationService<ServerConfiguration> mareClientConfigurationService,
|
||||
IConfigurationService<ServicesConfiguration> mareServicesConfiguration,
|
||||
GrpcClientFactory grpcClientFactory, IConnectionMultiplexer connectionMultiplexer)
|
||||
{
|
||||
_logger = logger;
|
||||
_services = services;
|
||||
_botServices = botServices;
|
||||
_mareClientConfigurationService = mareClientConfigurationService;
|
||||
_mareServicesConfiguration = mareServicesConfiguration;
|
||||
_grpcClientFactory = grpcClientFactory;
|
||||
_connectionMultiplexer = connectionMultiplexer;
|
||||
}
|
||||
@@ -314,6 +317,29 @@ public class MareModule : InteractionModuleBase
|
||||
Uid = uid ?? string.Empty
|
||||
});
|
||||
|
||||
var discordChannelForMessages = _mareServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
|
||||
if (uid == null && discordChannelForMessages != null)
|
||||
{
|
||||
var discordChannel = await Context.Guild.GetChannelAsync(discordChannelForMessages.Value) as IMessageChannel;
|
||||
if (discordChannel != null)
|
||||
{
|
||||
var embedColor = messageType switch
|
||||
{
|
||||
MareSynchronosShared.Protos.MessageType.Info => Color.Blue,
|
||||
MareSynchronosShared.Protos.MessageType.Warning => new Color(255, 255, 0),
|
||||
MareSynchronosShared.Protos.MessageType.Error => Color.Red,
|
||||
_ => Color.Blue
|
||||
};
|
||||
|
||||
EmbedBuilder eb = new();
|
||||
eb.WithTitle(messageType + " server message");
|
||||
eb.WithColor(embedColor);
|
||||
eb.WithDescription(message);
|
||||
|
||||
await discordChannel.SendMessageAsync(embed: eb.Build());
|
||||
}
|
||||
}
|
||||
|
||||
await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using MareSynchronosServices;
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using MareSynchronosShared.Services;
|
||||
using MareSynchronosShared.Utils;
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@ using MareSynchronosShared.Utils;
|
||||
using Grpc.Net.Client.Configuration;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Services;
|
||||
using Grpc.Net.ClientFactory;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace MareSynchronosServices;
|
||||
@@ -52,18 +49,6 @@ public class Startup
|
||||
ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(options);
|
||||
services.AddSingleton<IConnectionMultiplexer>(connectionMultiplexer);
|
||||
|
||||
services.AddGrpcClient<ConfigurationService.ConfigurationServiceClient>("MainServer", c =>
|
||||
{
|
||||
c.Address = new Uri(mareConfig.GetValue<string>(nameof(ServicesConfiguration.MainServerGrpcAddress)));
|
||||
}).ConfigureChannel(c =>
|
||||
{
|
||||
c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } };
|
||||
c.HttpHandler = new SocketsHttpHandler()
|
||||
{
|
||||
EnableMultipleHttp2Connections = true
|
||||
};
|
||||
});
|
||||
|
||||
services.AddGrpcClient<ClientMessageService.ClientMessageServiceClient>("MessageClient", c =>
|
||||
{
|
||||
c.Address = new Uri(mareConfig.GetValue<string>(nameof(ServicesConfiguration.MainServerGrpcAddress)));
|
||||
@@ -80,19 +65,12 @@ public class Startup
|
||||
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.AddSingleton(Configuration);
|
||||
services.AddSingleton<ServerTokenGenerator>();
|
||||
services.AddSingleton<DiscordBotServices>();
|
||||
services.AddHostedService<DiscordBot>();
|
||||
services.AddSingleton<IConfigurationService<ServicesConfiguration>, MareConfigurationServiceServer<ServicesConfiguration>>();
|
||||
services.AddSingleton<IConfigurationService<ServerConfiguration>>(c => new MareConfigurationServiceClient<ServerConfiguration>(
|
||||
c.GetService<ILogger<MareConfigurationServiceClient<ServerConfiguration>>>(),
|
||||
c.GetService<IOptions<ServerConfiguration>>(),
|
||||
c.GetService<GrpcClientFactory>(),
|
||||
"MainServer"));
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>>(c => new MareConfigurationServiceClient<MareConfigurationAuthBase>(
|
||||
c.GetService<ILogger<MareConfigurationServiceClient<MareConfigurationAuthBase>>>(),
|
||||
c.GetService<IOptions<MareConfigurationAuthBase>>(),
|
||||
c.GetService<GrpcClientFactory>(),
|
||||
"MainServer"));
|
||||
services.AddSingleton<IConfigurationService<ServerConfiguration>, MareConfigurationServiceClient<ServerConfiguration>>();
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
|
||||
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>());
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"MareSynchronos": {
|
||||
"DbContextPoolSize": 1024,
|
||||
"DiscordBotToken": "",
|
||||
"DiscordChannelForMessages": "",
|
||||
"PurgeUnusedAccounts": true,
|
||||
"PurgeUnusedAccountsPeriodInDays": 14,
|
||||
"FailedAuthForTempBan": 5,
|
||||
|
||||
@@ -9,10 +9,6 @@ service FileService {
|
||||
rpc DeleteFiles (DeleteFilesRequest) returns (Empty);
|
||||
}
|
||||
|
||||
service ConfigurationService {
|
||||
rpc GetConfigurationEntry (KeyMessage) returns (ValueMessage);
|
||||
}
|
||||
|
||||
service ClientMessageService {
|
||||
rpc SendClientMessage (ClientMessage) returns (Empty);
|
||||
}
|
||||
@@ -29,15 +25,6 @@ enum MessageType {
|
||||
ERROR = 2;
|
||||
}
|
||||
|
||||
message KeyMessage {
|
||||
string key = 1;
|
||||
string default = 2;
|
||||
}
|
||||
|
||||
message ValueMessage {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message Empty { }
|
||||
|
||||
message MultiUidMessage {
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
using Grpc.Core;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
public abstract class GrpcBaseService : IHostedService, IDisposable
|
||||
{
|
||||
protected GrpcBaseService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private CancellationTokenSource _faultCheckCts = new();
|
||||
private CancellationTokenSource _streamCts = new();
|
||||
private readonly ILogger _logger;
|
||||
protected bool GrpcIsFaulty { get; private set; }
|
||||
|
||||
protected abstract Task StartAsyncInternal(CancellationToken cancellationToken);
|
||||
protected abstract Task StopAsyncInternal(CancellationToken cancellationToken);
|
||||
protected abstract Task OnGrpcRestore();
|
||||
protected abstract Task PreStartStream();
|
||||
protected abstract Task StartStream(CancellationToken ct);
|
||||
protected abstract Task PostStartStream();
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_ = RestartStreams();
|
||||
_ = CheckGrpcFaults(_faultCheckCts.Token);
|
||||
await StartAsyncInternal(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_faultCheckCts.Cancel();
|
||||
_streamCts.Cancel();
|
||||
await StopAsyncInternal(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task RestartStreams()
|
||||
{
|
||||
if (!GrpcIsFaulty)
|
||||
{
|
||||
_streamCts?.Cancel();
|
||||
_streamCts?.Dispose();
|
||||
_streamCts = new();
|
||||
var token = _streamCts.Token;
|
||||
try
|
||||
{
|
||||
await PreStartStream().ConfigureAwait(false);
|
||||
await StartStream(token).ConfigureAwait(false);
|
||||
await PostStartStream().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
SetGrpcFaulty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetGrpcFaulty()
|
||||
{
|
||||
if (!GrpcIsFaulty)
|
||||
{
|
||||
GrpcIsFaulty = true;
|
||||
_logger.LogWarning("GRPC connection is faulty");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckGrpcFaults(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await CheckFaultStateAndRestore().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
SetGrpcFaulty();
|
||||
await Task.Delay(5000, ct).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(250).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckFaultStateAndRestore()
|
||||
{
|
||||
if (GrpcIsFaulty)
|
||||
{
|
||||
GrpcIsFaulty = false;
|
||||
await RestartStreams().ConfigureAwait(false);
|
||||
await OnGrpcRestore().ConfigureAwait(false);
|
||||
_logger.LogInformation("GRPC connection is restored");
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task<T> InvokeOnGrpc<T>(AsyncUnaryCall<T> toExecute)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await toExecute.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
SetGrpcFaulty();
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ExecuteOnGrpc<T>(AsyncUnaryCall<T> toExecute)
|
||||
{
|
||||
try
|
||||
{
|
||||
await toExecute.ConfigureAwait(false);
|
||||
await CheckFaultStateAndRestore().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
SetGrpcFaulty();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_streamCts?.Dispose();
|
||||
_faultCheckCts?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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,61 @@
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
[Route("configuration/[controller]")]
|
||||
[Authorize(Policy = "Internal")]
|
||||
public class MareConfigurationController<T> : Controller where T : class, IMareConfiguration
|
||||
{
|
||||
private readonly ILogger<MareConfigurationController<T>> _logger;
|
||||
private IOptionsMonitor<T> _config;
|
||||
|
||||
public MareConfigurationController(IOptionsMonitor<T> config, ILogger<MareConfigurationController<T>> logger)
|
||||
{
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("GetConfigurationEntry")]
|
||||
[Authorize(Policy = "Internal")]
|
||||
public IActionResult GetConfigurationEntry(string key, string defaultValue)
|
||||
{
|
||||
var result = _config.CurrentValue.SerializeValue(key, defaultValue);
|
||||
_logger.LogInformation("Requested " + key + ", returning:" + result);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable MA0048 // File name must match type name
|
||||
public class MareStaticFilesServerConfigurationController : MareConfigurationController<StaticFilesServerConfiguration>
|
||||
{
|
||||
public MareStaticFilesServerConfigurationController(IOptionsMonitor<StaticFilesServerConfiguration> config, ILogger<MareStaticFilesServerConfigurationController> logger) : base(config, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MareAuthBaseConfigurationController : MareConfigurationController<MareConfigurationAuthBase>
|
||||
{
|
||||
public MareAuthBaseConfigurationController(IOptionsMonitor<MareConfigurationAuthBase> config, ILogger<MareAuthBaseConfigurationController> logger) : base(config, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MareServerConfigurationController : MareConfigurationController<ServerConfiguration>
|
||||
{
|
||||
public MareServerConfigurationController(IOptionsMonitor<ServerConfiguration> config, ILogger<MareServerConfigurationController> logger) : base(config, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MareServicesConfigurationController : MareConfigurationController<ServicesConfiguration>
|
||||
{
|
||||
public MareServicesConfigurationController(IOptionsMonitor<ServicesConfiguration> config, ILogger<MareServicesConfigurationController> logger) : base(config, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
#pragma warning restore MA0048 // File name must match type name
|
||||
@@ -1,43 +1,55 @@
|
||||
using Grpc.Net.ClientFactory;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using static MareSynchronosShared.Protos.ConfigurationService;
|
||||
|
||||
namespace MareSynchronosShared.Services;
|
||||
|
||||
public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationService<T> where T : class, IMareConfiguration
|
||||
{
|
||||
private readonly T _config;
|
||||
private readonly IOptionsMonitor<T> _config;
|
||||
private readonly ConcurrentDictionary<string, object> _cachedRemoteProperties = new(StringComparer.Ordinal);
|
||||
private readonly ILogger<MareConfigurationServiceClient<T>> _logger;
|
||||
private readonly GrpcClientFactory _grpcClientFactory;
|
||||
private readonly string _grpcClientName;
|
||||
private readonly ServerTokenGenerator _serverTokenGenerator;
|
||||
private readonly CancellationTokenSource _updateTaskCts = new();
|
||||
private ConfigurationServiceClient _configurationServiceClient;
|
||||
private bool _initialized = false;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptions<T> config, GrpcClientFactory grpcClientFactory, string grpcClientName)
|
||||
private Uri GetRoute(string key, string value)
|
||||
{
|
||||
_config = config.Value;
|
||||
if (_config.CurrentValue.GetType() == typeof(ServerConfiguration))
|
||||
return new Uri((_config.CurrentValue as ServerConfiguration).MainServerAddress, $"configuration/MareServerConfiguration/{nameof(MareServerConfigurationController.GetConfigurationEntry)}?key={key}&defaultValue={value}");
|
||||
if (_config.CurrentValue.GetType() == typeof(MareConfigurationAuthBase))
|
||||
return new Uri((_config.CurrentValue as MareConfigurationAuthBase).MainServerAddress, $"configuration/MareAuthBaseConfiguration/{nameof(MareAuthBaseConfigurationController.GetConfigurationEntry)}?key={key}&defaultValue={value}");
|
||||
if (_config.CurrentValue.GetType() == typeof(ServicesConfiguration))
|
||||
return new Uri((_config.CurrentValue as ServicesConfiguration).MainServerAddress, $"configuration/MareServicesConfiguration/{nameof(MareServicesConfigurationController.GetConfigurationEntry)}?key={key}&defaultValue={value}");
|
||||
if (_config.CurrentValue.GetType() == typeof(StaticFilesServerConfiguration))
|
||||
return new Uri((_config.CurrentValue as StaticFilesServerConfiguration).MainFileServerAddress, $"configuration/MareStaticFilesServerConfiguration/{nameof(MareStaticFilesServerConfigurationController.GetConfigurationEntry)}?key={key}&defaultValue={value}");
|
||||
|
||||
throw new NotSupportedException("Config is not supported to be gotten remotely");
|
||||
}
|
||||
|
||||
public MareConfigurationServiceClient(ILogger<MareConfigurationServiceClient<T>> logger, IOptionsMonitor<T> config, ServerTokenGenerator serverTokenGenerator)
|
||||
{
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
_grpcClientFactory = grpcClientFactory;
|
||||
_grpcClientName = grpcClientName;
|
||||
_serverTokenGenerator = serverTokenGenerator;
|
||||
_httpClient = new();
|
||||
}
|
||||
|
||||
public bool IsMain => false;
|
||||
|
||||
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
|
||||
{
|
||||
var prop = _config.GetType().GetProperty(key);
|
||||
var prop = _config.CurrentValue.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();
|
||||
@@ -46,7 +58,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
return (T1)remotevalue;
|
||||
}
|
||||
|
||||
var value = prop.GetValue(_config);
|
||||
var value = prop.GetValue(_config.CurrentValue);
|
||||
var defaultPropValue = prop.PropertyType.IsValueType ? Activator.CreateInstance(prop.PropertyType) : null;
|
||||
if (value == defaultPropValue) return defaultValue;
|
||||
return (T1)value;
|
||||
@@ -54,7 +66,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
|
||||
public T1 GetValue<T1>(string key)
|
||||
{
|
||||
var prop = _config.GetType().GetProperty(key);
|
||||
var prop = _config.CurrentValue.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();
|
||||
@@ -63,19 +75,19 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
return (T1)remotevalue;
|
||||
}
|
||||
|
||||
var value = prop.GetValue(_config);
|
||||
var value = prop.GetValue(_config.CurrentValue);
|
||||
return (T1)value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var props = _config.GetType().GetProperties();
|
||||
var props = _config.CurrentValue.GetType().GetProperties();
|
||||
StringBuilder sb = new();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any();
|
||||
var getValueMethod = GetType().GetMethod(nameof(GetValue)).MakeGenericMethod(prop.PropertyType);
|
||||
var value = isRemote ? getValueMethod.Invoke(this, new[] { prop.Name }) : prop.GetValue(_config);
|
||||
var value = isRemote ? getValueMethod.Invoke(this, new[] { prop.Name }) : prop.GetValue(_config.CurrentValue);
|
||||
if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) && !typeof(string).IsAssignableFrom(prop.PropertyType))
|
||||
{
|
||||
var enumVal = (IEnumerable)value;
|
||||
@@ -90,15 +102,18 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private async Task<T1> GetValueFromGrpc<T1>(ConfigurationServiceClient client, string key, object defaultValue)
|
||||
private async Task<T1> GetValueFromRemote<T1>(string key, object defaultValue)
|
||||
{
|
||||
// grab stuff from grpc
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Getting {key} from Grpc", key);
|
||||
var response = await client.GetConfigurationEntryAsync(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }).ConfigureAwait(false);
|
||||
_logger.LogInformation("Grpc Response for {key} = {value}", key, response.Value);
|
||||
return JsonSerializer.Deserialize<T1>(response.Value);
|
||||
_logger.LogInformation("Getting {key} from Http", key);
|
||||
HttpRequestMessage msg = new(HttpMethod.Get, GetRoute(key, Convert.ToString(defaultValue, CultureInfo.InvariantCulture)));
|
||||
msg.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
|
||||
var response = await _httpClient.SendAsync(msg).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
_logger.LogInformation("Http Response for {key} = {value}", key, content);
|
||||
return JsonSerializer.Deserialize<T1>(content);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -111,20 +126,19 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
_logger.LogInformation("Getting Properties from GRPC");
|
||||
_logger.LogInformation("Getting Properties from Remote for " + typeof(T));
|
||||
try
|
||||
{
|
||||
_configurationServiceClient = _grpcClientFactory.CreateClient<ConfigurationServiceClient>(_grpcClientName);
|
||||
var properties = _config.GetType().GetProperties();
|
||||
var properties = _config.CurrentValue.GetType().GetProperties();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()) continue;
|
||||
_logger.LogInformation("Checking Property " + prop.Name);
|
||||
var mi = GetType().GetMethod(nameof(GetValueFromGrpc), BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(prop.PropertyType);
|
||||
var mi = GetType().GetMethod(nameof(GetValueFromRemote), BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(prop.PropertyType);
|
||||
var defaultValue = prop.PropertyType.IsValueType ? Activator.CreateInstance(prop.PropertyType) : null;
|
||||
var task = (Task)mi.Invoke(this, new[] { _configurationServiceClient, prop.Name, defaultValue });
|
||||
var task = (Task)mi.Invoke(this, new[] { prop.Name, defaultValue });
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
var resultProperty = task.GetType().GetProperty("Result");
|
||||
@@ -147,12 +161,12 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Saved properties from GRPC are now:");
|
||||
_logger.LogInformation("Saved properties from HTTP are now:");
|
||||
_logger.LogInformation(ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failure getting or updating properties from GRPC, retrying in 30min");
|
||||
_logger.LogError(ex, "Failure getting or updating properties from HTTP, retrying in 30min");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromMinutes(30), ct).ConfigureAwait(false);
|
||||
@@ -169,6 +183,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_updateTaskCts.Cancel();
|
||||
_httpClient.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -6,31 +6,31 @@ namespace MareSynchronosShared.Services;
|
||||
|
||||
public class MareConfigurationServiceServer<T> : IConfigurationService<T> where T : class, IMareConfiguration
|
||||
{
|
||||
private readonly T _config;
|
||||
private readonly IOptionsMonitor<T> _config;
|
||||
public bool IsMain => true;
|
||||
|
||||
public MareConfigurationServiceServer(IOptions<T> config)
|
||||
public MareConfigurationServiceServer(IOptionsMonitor<T> config)
|
||||
{
|
||||
_config = config.Value;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue)
|
||||
{
|
||||
return _config.GetValueOrDefault<T1>(key, defaultValue);
|
||||
return _config.CurrentValue.GetValueOrDefault<T1>(key, defaultValue);
|
||||
}
|
||||
|
||||
public T1 GetValue<T1>(string key)
|
||||
{
|
||||
return _config.GetValue<T1>(key);
|
||||
return _config.CurrentValue.GetValue<T1>(key);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var props = _config.GetType().GetProperties();
|
||||
var props = _config.CurrentValue.GetType().GetProperties();
|
||||
StringBuilder sb = new();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name} (IsRemote: {prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()}) => {prop.GetValue(_config)}");
|
||||
sb.AppendLine($"{prop.Name} (IsRemote: {prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()}) => {prop.GetValue(_config.CurrentValue)}");
|
||||
}
|
||||
|
||||
sb.AppendLine(_config.ToString());
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class AllowedControllersFeatureProvider : ControllerFeatureProvider
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly Type[] _allowedTypes;
|
||||
|
||||
public AllowedControllersFeatureProvider(params Type[] allowedTypes)
|
||||
{
|
||||
_allowedTypes = allowedTypes;
|
||||
}
|
||||
|
||||
protected override bool IsController(TypeInfo typeInfo)
|
||||
{
|
||||
return base.IsController(typeInfo) && _allowedTypes.Contains(typeInfo.AsType());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MareSynchronosShared.Utils;
|
||||
namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class CdnShardConfiguration
|
||||
{
|
||||
|
||||
@@ -4,21 +4,17 @@ 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();
|
||||
[RemoteConfiguration]
|
||||
public string Jwt { get; set; } = string.Empty;
|
||||
|
||||
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(Jwt)} => {Jwt}");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -7,9 +6,11 @@ namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class MareConfigurationBase : IMareConfiguration
|
||||
{
|
||||
public Uri MainServerAddress { get; set; }
|
||||
public int DbContextPoolSize { get; set; } = 100;
|
||||
public string ShardName { get; set; } = string.Empty;
|
||||
public int MetricsPort { get; set; }
|
||||
public string Jwt { get; set; } = string.Empty;
|
||||
|
||||
public T GetValue<T>(string key)
|
||||
{
|
||||
@@ -39,6 +40,7 @@ public class MareConfigurationBase : IMareConfiguration
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(MainServerAddress)} => {MainServerAddress}");
|
||||
sb.AppendLine($"{nameof(ShardName)} => {ShardName}");
|
||||
sb.AppendLine($"{nameof(DbContextPoolSize)} => {DbContextPoolSize}");
|
||||
return sb.ToString();
|
||||
|
||||
@@ -28,7 +28,6 @@ public class ServerConfiguration : MareConfigurationAuthBase
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(CdnFullUrl)} => {CdnFullUrl}");
|
||||
sb.AppendLine($"{nameof(CdnShardConfiguration)} => {string.Join(", ", CdnShardConfiguration.Select(c => c.ToString()))}");
|
||||
sb.AppendLine($"{nameof(StaticFileServiceAddress)} => {StaticFileServiceAddress}");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using MareSynchronosShared.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
@@ -9,7 +9,7 @@ namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class ServerTokenGenerator
|
||||
{
|
||||
private readonly IConfigurationService<MareConfigurationAuthBase> _configuration;
|
||||
private readonly IOptionsMonitor<MareConfigurationAuthBase> _configuration;
|
||||
private readonly ILogger<ServerTokenGenerator> _logger;
|
||||
|
||||
private Dictionary<string, string> _tokenDictionary { get; set; } = new(StringComparer.Ordinal);
|
||||
@@ -17,7 +17,7 @@ public class ServerTokenGenerator
|
||||
{
|
||||
get
|
||||
{
|
||||
var currentJwt = _configuration.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt));
|
||||
var currentJwt = _configuration.CurrentValue.Jwt;
|
||||
if (_tokenDictionary.TryGetValue(currentJwt, out var token))
|
||||
{
|
||||
return token;
|
||||
@@ -27,7 +27,7 @@ public class ServerTokenGenerator
|
||||
}
|
||||
}
|
||||
|
||||
public ServerTokenGenerator(IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<ServerTokenGenerator> logger)
|
||||
public ServerTokenGenerator(IOptionsMonitor<MareConfigurationAuthBase> configuration, ILogger<ServerTokenGenerator> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
@@ -35,14 +35,14 @@ public class ServerTokenGenerator
|
||||
|
||||
private string GenerateToken()
|
||||
{
|
||||
var signingKey = _configuration.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt));
|
||||
var signingKey = _configuration.CurrentValue.Jwt;
|
||||
var authSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(signingKey));
|
||||
|
||||
var token = new SecurityTokenDescriptor()
|
||||
{
|
||||
Subject = new ClaimsIdentity(new List<Claim>()
|
||||
{
|
||||
new Claim(MareClaimTypes.Uid, _configuration.GetValue<string>(nameof(MareConfigurationBase.ShardName))),
|
||||
new Claim(MareClaimTypes.Uid, _configuration.CurrentValue.ShardName),
|
||||
new Claim(MareClaimTypes.Internal, "true"),
|
||||
}),
|
||||
SigningCredentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256Signature),
|
||||
|
||||
@@ -6,6 +6,7 @@ public class ServicesConfiguration : MareConfigurationBase
|
||||
{
|
||||
public string DiscordBotToken { get; set; } = string.Empty;
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
public ulong? DiscordChannelForMessages { get; set; } = null;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -5,27 +5,24 @@ namespace MareSynchronosStaticFilesServer;
|
||||
|
||||
public class StaticFilesServerConfiguration : MareConfigurationBase
|
||||
{
|
||||
public Uri FileServerGrpcAddress { get; set; } = null;
|
||||
public Uri? MainFileServerAddress { 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 int DownloadQueueSize { get; set; } = 50;
|
||||
public int DownloadTimeoutSeconds { get; set; } = 5;
|
||||
public int DownloadQueueReleaseSeconds { get; set; } = 15;
|
||||
public int DownloadQueueClearLimit { get; set; } = 15000;
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(FileServerGrpcAddress)} => {FileServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(MainFileServerAddress)} => {MainFileServerAddress}");
|
||||
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}");
|
||||
sb.AppendLine($"{nameof(DownloadQueueSize)} => {DownloadQueueSize}");
|
||||
sb.AppendLine($"{nameof(DownloadQueueReleaseSeconds)} => {DownloadQueueReleaseSeconds}");
|
||||
return sb.ToString();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer.Services;
|
||||
using MareSynchronosStaticFilesServer.Utils;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -24,13 +24,21 @@ public class Program
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
public static IHostBuilder CreateHostBuilder(string[] args)
|
||||
{
|
||||
var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddConsole();
|
||||
});
|
||||
var logger = loggerFactory.CreateLogger<Startup>();
|
||||
return Host.CreateDefaultBuilder(args)
|
||||
.UseSystemd()
|
||||
.UseConsoleLifetime()
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseContentRoot(AppContext.BaseDirectory);
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.UseStartup(ctx => new Startup(ctx.Configuration, logger));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public class CachedFileProvider
|
||||
_fileStatisticsService = fileStatisticsService;
|
||||
_metrics = metrics;
|
||||
_generator = generator;
|
||||
_remoteCacheSourceUri = configuration.GetValueOrDefault<Uri>(nameof(StaticFilesServerConfiguration.RemoteCacheSourceUri), null);
|
||||
_remoteCacheSourceUri = configuration.GetValueOrDefault<Uri>(nameof(StaticFilesServerConfiguration.MainFileServerAddress), null);
|
||||
_basePath = configuration.GetValue<string>(nameof(StaticFilesServerConfiguration.CacheDirectory));
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using MareSynchronosShared.Services;
|
||||
using MareSynchronosStaticFilesServer.Utils;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
@@ -19,6 +18,7 @@ public class RequestQueueService : IHostedService
|
||||
private readonly int _queueExpirationSeconds;
|
||||
private readonly SemaphoreSlim _queueSemaphore = new(1);
|
||||
private readonly SemaphoreSlim _queueProcessingSemaphore = new(1);
|
||||
private int _queueLimitForReset;
|
||||
private System.Timers.Timer _queueTimer;
|
||||
private readonly ConcurrentDictionary<Guid, string> _queueRemoval = new();
|
||||
|
||||
@@ -26,6 +26,7 @@ public class RequestQueueService : IHostedService
|
||||
{
|
||||
_userQueueRequests = new UserQueueEntry[configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadQueueSize), 50)];
|
||||
_queueExpirationSeconds = configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadTimeoutSeconds), 5);
|
||||
_queueLimitForReset = configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadQueueClearLimit), 15000);
|
||||
_metrics = metrics;
|
||||
_logger = logger;
|
||||
_hubContext = hubContext;
|
||||
@@ -120,6 +121,12 @@ public class RequestQueueService : IHostedService
|
||||
|
||||
try
|
||||
{
|
||||
if (_queue.Count > _queueLimitForReset)
|
||||
{
|
||||
_queue.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Parallel.For(0, _userQueueRequests.Length, new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 10,
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
using Grpc.Net.Client.Configuration;
|
||||
using Grpc.Net.ClientFactory;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Metrics;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Services;
|
||||
using MareSynchronosShared.Utils;
|
||||
using MareSynchronosStaticFilesServer.Controllers;
|
||||
using MareSynchronosStaticFilesServer.Services;
|
||||
using MareSynchronosStaticFilesServer.Utils;
|
||||
using MessagePack;
|
||||
using MessagePack.Resolvers;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Prometheus;
|
||||
using StackExchange.Redis;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosStaticFilesServer;
|
||||
@@ -26,11 +23,14 @@ namespace MareSynchronosStaticFilesServer;
|
||||
public class Startup
|
||||
{
|
||||
private bool _isMain;
|
||||
public Startup(IConfiguration configuration)
|
||||
private readonly ILogger<Startup> _logger;
|
||||
|
||||
public Startup(IConfiguration configuration, ILogger<Startup> logger)
|
||||
{
|
||||
Configuration = configuration;
|
||||
_logger = logger;
|
||||
var mareSettings = Configuration.GetRequiredSection("MareSynchronos");
|
||||
_isMain = string.IsNullOrEmpty(mareSettings.GetValue(nameof(StaticFilesServerConfiguration.RemoteCacheSourceUri), string.Empty));
|
||||
_isMain = string.IsNullOrEmpty(mareSettings.GetValue(nameof(StaticFilesServerConfiguration.MainFileServerAddress), string.Empty));
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
@@ -83,30 +83,6 @@ public class Startup
|
||||
RetryPolicy = null,
|
||||
};
|
||||
|
||||
services.AddGrpcClient<ConfigurationService.ConfigurationServiceClient>("FileServer", c =>
|
||||
{
|
||||
c.Address = new Uri(mareConfig.GetValue<string>(nameof(StaticFilesServerConfiguration.FileServerGrpcAddress)));
|
||||
}).ConfigureChannel(c =>
|
||||
{
|
||||
c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } };
|
||||
c.HttpHandler = new SocketsHttpHandler()
|
||||
{
|
||||
EnableMultipleHttp2Connections = true,
|
||||
};
|
||||
});
|
||||
|
||||
services.AddGrpcClient<ConfigurationService.ConfigurationServiceClient>("MainServer", c =>
|
||||
{
|
||||
c.Address = new Uri(mareConfig.GetValue<string>(nameof(StaticFilesServerConfiguration.MainServerGrpcAddress)));
|
||||
}).ConfigureChannel(c =>
|
||||
{
|
||||
c.ServiceConfig = new ServiceConfig { MethodConfigs = { noRetryConfig } };
|
||||
c.HttpHandler = new SocketsHttpHandler()
|
||||
{
|
||||
EnableMultipleHttp2Connections = true,
|
||||
};
|
||||
});
|
||||
|
||||
services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
|
||||
.Configure<IConfigurationService<MareConfigurationAuthBase>>((o, s) =>
|
||||
{
|
||||
@@ -144,26 +120,27 @@ public class Startup
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IConfigurationService<StaticFilesServerConfiguration>>(p => new MareConfigurationServiceClient<StaticFilesServerConfiguration>(
|
||||
p.GetRequiredService<ILogger<MareConfigurationServiceClient<StaticFilesServerConfiguration>>>(),
|
||||
p.GetRequiredService<IOptions<StaticFilesServerConfiguration>>(),
|
||||
p.GetRequiredService<GrpcClientFactory>(),
|
||||
"FileServer"));
|
||||
|
||||
services.AddSingleton<IConfigurationService<StaticFilesServerConfiguration>, MareConfigurationServiceClient<StaticFilesServerConfiguration>>();
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<StaticFilesServerConfiguration>)p.GetService<IConfigurationService<StaticFilesServerConfiguration>>());
|
||||
}
|
||||
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>>(p =>
|
||||
new MareConfigurationServiceClient<MareConfigurationAuthBase>(
|
||||
p.GetRequiredService<ILogger<MareConfigurationServiceClient<MareConfigurationAuthBase>>>(),
|
||||
p.GetService<IOptions<MareConfigurationAuthBase>>(),
|
||||
p.GetRequiredService<GrpcClientFactory>(), "MainServer")
|
||||
);
|
||||
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
|
||||
|
||||
services.AddSingleton<ServerTokenGenerator>();
|
||||
services.AddSingleton<RequestQueueService>();
|
||||
services.AddHostedService(p => p.GetService<RequestQueueService>());
|
||||
services.AddControllers();
|
||||
services.AddControllers().ConfigureApplicationPartManager(a =>
|
||||
{
|
||||
a.FeatureProviders.Remove(a.FeatureProviders.OfType<ControllerFeatureProvider>().First());
|
||||
if (_isMain)
|
||||
{
|
||||
a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(MareStaticFilesServerConfigurationController), typeof(CacheController), typeof(RequestController), typeof(ServerFilesController)));
|
||||
}
|
||||
else
|
||||
{
|
||||
a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(CacheController), typeof(RequestController)));
|
||||
}
|
||||
});
|
||||
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
|
||||
|
||||
@@ -198,7 +175,6 @@ public class Startup
|
||||
signalRServiceBuilder.AddStackExchangeRedis(redisConnection, options => { });
|
||||
|
||||
services.AddHealthChecks();
|
||||
services.AddControllers();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
||||
Reference in New Issue
Block a user