- reload configs on change

- remove grpc config service replace with http
- add messaging to channel
This commit is contained in:
rootdarkarchon
2023-02-25 12:20:59 +01:00
parent 3490c5c8d8
commit aa03d400d4
38 changed files with 304 additions and 377 deletions

View File

@@ -82,6 +82,7 @@ services:
restart: on-failure restart: on-failure
environment: environment:
MareSynchronos__DiscordBotToken: "${DEV_MARE_DISCORDTOKEN}" MareSynchronos__DiscordBotToken: "${DEV_MARE_DISCORDTOKEN}"
MareSynchronos__DiscordChannelForMessages: "${DEV_MARE_DISCORDCHANNEL}"
volumes: volumes:
- ../config/standalone/services-standalone.json:/opt/MareSynchronosServices/appsettings.json - ../config/standalone/services-standalone.json:/opt/MareSynchronosServices/appsettings.json
- ../log/services-standalone/:/opt/MareSynchronosServices/logs/:rw - ../log/services-standalone/:/opt/MareSynchronosServices/logs/:rw

View File

@@ -28,6 +28,7 @@ services:
- 6000:6000/tcp - 6000:6000/tcp
environment: environment:
MareSynchronos__CdnFullUrl: "${DEV_MARE_CDNURL}" MareSynchronos__CdnFullUrl: "${DEV_MARE_CDNURL}"
DOTNET_USE_POLLING_FILE_WATCHER: 1
volumes: volumes:
- ../config/standalone/server-standalone.json:/opt/MareSynchronosServer/appsettings.json - ../config/standalone/server-standalone.json:/opt/MareSynchronosServer/appsettings.json
- ../log/server-standalone/:/opt/MareSynchronosServer/logs/:rw - ../log/server-standalone/:/opt/MareSynchronosServer/logs/:rw
@@ -46,11 +47,15 @@ services:
restart: on-failure restart: on-failure
environment: environment:
MareSynchronos__DiscordBotToken: "${DEV_MARE_DISCORDTOKEN}" MareSynchronos__DiscordBotToken: "${DEV_MARE_DISCORDTOKEN}"
MareSynchronos__DiscordChannelForMessages: "${DEV_MARE_DISCORDCHANNEL}"
DOTNET_USE_POLLING_FILE_WATCHER: 1
volumes: volumes:
- ../config/standalone/services-standalone.json:/opt/MareSynchronosServices/appsettings.json - ../config/standalone/services-standalone.json:/opt/MareSynchronosServices/appsettings.json
- ../log/services-standalone/:/opt/MareSynchronosServices/logs/:rw - ../log/services-standalone/:/opt/MareSynchronosServices/logs/:rw
- postgres_socket:/var/run/postgresql/:rw - postgres_socket:/var/run/postgresql/:rw
depends_on: depends_on:
mare-server:
condition: service_healthy
postgres: postgres:
condition: service_healthy condition: service_healthy
@@ -59,6 +64,8 @@ services:
ports: ports:
- 6200:6200/tcp - 6200:6200/tcp
restart: on-failure restart: on-failure
environment:
DOTNET_USE_POLLING_FILE_WATCHER: 1
volumes: volumes:
- ../config/standalone/files-standalone.json:/opt/MareSynchronosStaticFilesServer/appsettings.json - ../config/standalone/files-standalone.json:/opt/MareSynchronosStaticFilesServer/appsettings.json
- ../log/files-standalone/:/opt/MareSynchronosStaticFilesServer/logs/:rw - ../log/files-standalone/:/opt/MareSynchronosStaticFilesServer/logs/:rw

View File

@@ -28,17 +28,17 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Files Shard 1", "ShardName": "Files Shard 1",
"MetricsPort": 6250, "MetricsPort": 6250,
"FileServerGrpcAddress": "http://mare-files:6205",
"ForcedDeletionOfFilesAfterHours": 2, "ForcedDeletionOfFilesAfterHours": 2,
"CacheSizeHardLimitInGiB": 5, "CacheSizeHardLimitInGiB": 5,
"UnusedFileRetentionPeriodInDays": 14, "UnusedFileRetentionPeriodInDays": 14,
"CacheDirectory": "/marecache/", "CacheDirectory": "/marecache/",
"RemoteCacheSourceUri": "http://mare-files:6200/",
"MainServerGrpcAddress": "http://mare-server:6005",
"DownloadTimeoutSeconds": 30, "DownloadTimeoutSeconds": 30,
"DownloadQueueSize": 50, "DownloadQueueSize": 50,
"DownloadQueueReleaseSeconds": 15, "DownloadQueueReleaseSeconds": 15,
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
"MainServerAddress": "http://mare-server:6000",
"MainFileServerAddress": "http://mare-files:6200"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -28,17 +28,17 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Files Shard 2", "ShardName": "Files Shard 2",
"MetricsPort": 6250, "MetricsPort": 6250,
"FileServerGrpcAddress": "http://mare-files:6205",
"ForcedDeletionOfFilesAfterHours": 2, "ForcedDeletionOfFilesAfterHours": 2,
"CacheSizeHardLimitInGiB": 5, "CacheSizeHardLimitInGiB": 5,
"UnusedFileRetentionPeriodInDays": 14, "UnusedFileRetentionPeriodInDays": 14,
"CacheDirectory": "/marecache/", "CacheDirectory": "/marecache/",
"RemoteCacheSourceUri": "http://mare-files:6200/",
"MainServerGrpcAddress": "http://mare-server:6005",
"DownloadTimeoutSeconds": 30, "DownloadTimeoutSeconds": 30,
"DownloadQueueSize": 50, "DownloadQueueSize": 50,
"DownloadQueueReleaseSeconds": 15, "DownloadQueueReleaseSeconds": 15,
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
"MainServerAddress": "http://mare-server:6000",
"MainFileServerAddress": "http://mare-files:6200"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -34,8 +34,10 @@
"UnusedFileRetentionPeriodInDays": 14, "UnusedFileRetentionPeriodInDays": 14,
"CacheDirectory": "/marecache/", "CacheDirectory": "/marecache/",
"RemoteCacheSourceUri": "", "RemoteCacheSourceUri": "",
"MainServerGrpcAddress": "http://mare-server:6005", "RedisConnectionString": "redis,password=secretredispassword",
"RedisConnectionString": "redis,password=secretredispassword" "Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
"MainServerAddress": "http://mare-server:6000",
"MainFileServerAddress": ""
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -28,8 +28,9 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Shard 1", "ShardName": "Shard 1",
"MetricsPort": 6050, "MetricsPort": 6050,
"MainServerGrpcAddress": "http://mare-server:6005", "MainServerAddress": "http://mare-server:6000",
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -28,8 +28,9 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Shard 2", "ShardName": "Shard 2",
"MetricsPort": 6050, "MetricsPort": 6050,
"MainServerGrpcAddress": "http://mare-server:6005", "MainServerAddress": "http://mare-server:6000",
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -34,8 +34,10 @@
"UnusedFileRetentionPeriodInDays": 14, "UnusedFileRetentionPeriodInDays": 14,
"CacheDirectory": "/marecache/", "CacheDirectory": "/marecache/",
"RemoteCacheSourceUri": "", "RemoteCacheSourceUri": "",
"MainServerGrpcAddress": "http://mare-server:6005", "MainServerAddress": "http://mare-server:6000/",
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword",
"MainFileServerAddress": "",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": { "Kestrel": {

View File

@@ -28,7 +28,7 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Main", "ShardName": "Main",
"MetricsPort": 6050, "MetricsPort": 6050,
"MainServerGrpcAddress": "", "MainServerAddress": "",
"FailedAuthForTempBan": 5, "FailedAuthForTempBan": 5,
"TempBanDurationInMinutes": 5, "TempBanDurationInMinutes": 5,
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring", "Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",

View File

@@ -28,8 +28,11 @@
"DbContextPoolSize": 512, "DbContextPoolSize": 512,
"ShardName": "Services", "ShardName": "Services",
"MetricsPort": 6150, "MetricsPort": 6150,
"MainServerGrpcAddress": "http://mare-server:6005", "MainServerAddress": "http://mare-server:6000/",
"MainServerGrpcAddress": "http://mare-server:6005/",
"DiscordBotToken": "", "DiscordBotToken": "",
"DiscordChannelForMessages": "",
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
"RedisConnectionString": "redis,password=secretredispassword" "RedisConnectionString": "redis,password=secretredispassword"
}, },
"AllowedHosts": "*", "AllowedHosts": "*",

View File

@@ -2,7 +2,6 @@
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Data.Extensions;
using MareSynchronos.API.Dto.Group;
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronosServer.Utils; using MareSynchronosServer.Utils;
using MareSynchronosShared.Metrics; using MareSynchronosShared.Metrics;

View File

@@ -55,8 +55,15 @@ public class Program
} }
} }
public static IHostBuilder CreateHostBuilder(string[] args) => public static IHostBuilder CreateHostBuilder(string[] args)
Host.CreateDefaultBuilder(args) {
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.ClearProviders();
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<Startup>();
return Host.CreateDefaultBuilder(args)
.UseSystemd() .UseSystemd()
.UseConsoleLifetime() .UseConsoleLifetime()
.ConfigureWebHostDefaults(webBuilder => .ConfigureWebHostDefaults(webBuilder =>
@@ -67,6 +74,7 @@ public class Program
builder.AddConfiguration(ctx.Configuration.GetSection("Logging")); builder.AddConfiguration(ctx.Configuration.GetSection("Logging"));
builder.AddFile(o => o.RootPath = AppContext.BaseDirectory); builder.AddFile(o => o.RootPath = AppContext.BaseDirectory);
}); });
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup(ctx => new Startup(ctx.Configuration, logger));
}); });
}
} }

View File

@@ -13,8 +13,6 @@ using MareSynchronosServer.RequirementHandlers;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using Prometheus; using Prometheus;
using Microsoft.Extensions.Options;
using Grpc.Net.ClientFactory;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Text; using System.Text;
@@ -26,14 +24,19 @@ using StackExchange.Redis.Extensions.System.Text.Json;
using MareSynchronos.API.SignalR; using MareSynchronos.API.SignalR;
using MessagePack; using MessagePack;
using MessagePack.Resolvers; using MessagePack.Resolvers;
using Microsoft.AspNetCore.Mvc.Controllers;
using MareSynchronosServer.Controllers;
namespace MareSynchronosServer; namespace MareSynchronosServer;
public class Startup public class Startup
{ {
public Startup(IConfiguration configuration) private readonly ILogger<Startup> _logger;
public Startup(IConfiguration configuration, ILogger<Startup> logger)
{ {
Configuration = configuration; Configuration = configuration;
_logger = logger;
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
@@ -68,16 +71,27 @@ public class Startup
ConfigureMareServices(services, mareConfig); ConfigureMareServices(services, mareConfig);
services.AddHealthChecks(); 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<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
services.Configure<MareConfigurationBase>(mareConfig); services.Configure<MareConfigurationBase>(Configuration.GetRequiredSection("MareSynchronos"));
services.Configure<MareConfigurationAuthBase>(mareConfig); services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));
services.AddSingleton<ServerTokenGenerator>(); services.AddSingleton<ServerTokenGenerator>();
services.AddSingleton<SystemInfoService>(); services.AddSingleton<SystemInfoService>();
@@ -217,6 +231,7 @@ public class Startup
{ {
policy.AddRequirements(new UserRequirement(UserRequirements.Identified | UserRequirements.Moderator | UserRequirements.Administrator)); 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) if (!isMainServer)
{ {
var noRetryConfig = new MethodConfig services.AddSingleton<IConfigurationService<ServerConfiguration>, MareConfigurationServiceClient<ServerConfiguration>>();
{ services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
Names = { MethodName.Default },
RetryPolicy = null,
};
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<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>());
services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>()); services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
} }
@@ -358,12 +348,18 @@ public class Startup
if (config.IsMain) if (config.IsMain)
{ {
endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous();
endpoints.MapGrpcService<GrpcClientMessageService>().AllowAnonymous(); endpoints.MapGrpcService<GrpcClientMessageService>().AllowAnonymous();
} }
endpoints.MapHealthChecks("/health").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);
}
}); });
} }
} }

View File

@@ -29,18 +29,21 @@ public class MareModule : InteractionModuleBase
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly DiscordBotServices _botServices; private readonly DiscordBotServices _botServices;
private readonly IConfigurationService<ServerConfiguration> _mareClientConfigurationService; private readonly IConfigurationService<ServerConfiguration> _mareClientConfigurationService;
private readonly IConfigurationService<ServicesConfiguration> _mareServicesConfiguration;
private readonly GrpcClientFactory _grpcClientFactory; private readonly GrpcClientFactory _grpcClientFactory;
private readonly IConnectionMultiplexer _connectionMultiplexer; private readonly IConnectionMultiplexer _connectionMultiplexer;
private Random random = new(); private Random random = new();
public MareModule(ILogger<MareModule> logger, IServiceProvider services, DiscordBotServices botServices, public MareModule(ILogger<MareModule> logger, IServiceProvider services, DiscordBotServices botServices,
IConfigurationService<ServerConfiguration> mareClientConfigurationService, IConfigurationService<ServerConfiguration> mareClientConfigurationService,
IConfigurationService<ServicesConfiguration> mareServicesConfiguration,
GrpcClientFactory grpcClientFactory, IConnectionMultiplexer connectionMultiplexer) GrpcClientFactory grpcClientFactory, IConnectionMultiplexer connectionMultiplexer)
{ {
_logger = logger; _logger = logger;
_services = services; _services = services;
_botServices = botServices; _botServices = botServices;
_mareClientConfigurationService = mareClientConfigurationService; _mareClientConfigurationService = mareClientConfigurationService;
_mareServicesConfiguration = mareServicesConfiguration;
_grpcClientFactory = grpcClientFactory; _grpcClientFactory = grpcClientFactory;
_connectionMultiplexer = connectionMultiplexer; _connectionMultiplexer = connectionMultiplexer;
} }
@@ -314,6 +317,29 @@ public class MareModule : InteractionModuleBase
Uid = uid ?? string.Empty 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); await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -1,6 +1,5 @@
using MareSynchronosServices; using MareSynchronosServices;
using MareSynchronosShared.Data; using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;

View File

@@ -7,9 +7,6 @@ using MareSynchronosShared.Utils;
using Grpc.Net.Client.Configuration; using Grpc.Net.Client.Configuration;
using MareSynchronosShared.Protos; using MareSynchronosShared.Protos;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using Grpc.Net.ClientFactory;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis; using StackExchange.Redis;
namespace MareSynchronosServices; namespace MareSynchronosServices;
@@ -52,18 +49,6 @@ public class Startup
ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(options); ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(options);
services.AddSingleton<IConnectionMultiplexer>(connectionMultiplexer); 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 => services.AddGrpcClient<ClientMessageService.ClientMessageServiceClient>("MessageClient", c =>
{ {
c.Address = new Uri(mareConfig.GetValue<string>(nameof(ServicesConfiguration.MainServerGrpcAddress))); 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<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos")); services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));
services.AddSingleton(Configuration); services.AddSingleton(Configuration);
services.AddSingleton<ServerTokenGenerator>();
services.AddSingleton<DiscordBotServices>(); services.AddSingleton<DiscordBotServices>();
services.AddHostedService<DiscordBot>(); services.AddHostedService<DiscordBot>();
services.AddSingleton<IConfigurationService<ServicesConfiguration>, MareConfigurationServiceServer<ServicesConfiguration>>(); services.AddSingleton<IConfigurationService<ServicesConfiguration>, MareConfigurationServiceServer<ServicesConfiguration>>();
services.AddSingleton<IConfigurationService<ServerConfiguration>>(c => new MareConfigurationServiceClient<ServerConfiguration>( services.AddSingleton<IConfigurationService<ServerConfiguration>, MareConfigurationServiceClient<ServerConfiguration>>();
c.GetService<ILogger<MareConfigurationServiceClient<ServerConfiguration>>>(), services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
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<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>()); services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
services.AddHostedService(p => (MareConfigurationServiceClient<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>()); services.AddHostedService(p => (MareConfigurationServiceClient<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>());

View File

@@ -19,6 +19,7 @@
"MareSynchronos": { "MareSynchronos": {
"DbContextPoolSize": 1024, "DbContextPoolSize": 1024,
"DiscordBotToken": "", "DiscordBotToken": "",
"DiscordChannelForMessages": "",
"PurgeUnusedAccounts": true, "PurgeUnusedAccounts": true,
"PurgeUnusedAccountsPeriodInDays": 14, "PurgeUnusedAccountsPeriodInDays": 14,
"FailedAuthForTempBan": 5, "FailedAuthForTempBan": 5,

View File

@@ -9,10 +9,6 @@ service FileService {
rpc DeleteFiles (DeleteFilesRequest) returns (Empty); rpc DeleteFiles (DeleteFilesRequest) returns (Empty);
} }
service ConfigurationService {
rpc GetConfigurationEntry (KeyMessage) returns (ValueMessage);
}
service ClientMessageService { service ClientMessageService {
rpc SendClientMessage (ClientMessage) returns (Empty); rpc SendClientMessage (ClientMessage) returns (Empty);
} }
@@ -29,15 +25,6 @@ enum MessageType {
ERROR = 2; ERROR = 2;
} }
message KeyMessage {
string key = 1;
string default = 2;
}
message ValueMessage {
string value = 1;
}
message Empty { } message Empty { }
message MultiUidMessage { message MultiUidMessage {

View File

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

View File

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

View File

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

View File

@@ -1,43 +1,55 @@
using Grpc.Net.ClientFactory; using MareSynchronosShared.Utils;
using MareSynchronosShared.Protos; using MareSynchronosStaticFilesServer;
using MareSynchronosShared.Utils;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Net.Http.Headers;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using static MareSynchronosShared.Protos.ConfigurationService;
namespace MareSynchronosShared.Services; namespace MareSynchronosShared.Services;
public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationService<T> where T : class, IMareConfiguration 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 ConcurrentDictionary<string, object> _cachedRemoteProperties = new(StringComparer.Ordinal);
private readonly ILogger<MareConfigurationServiceClient<T>> _logger; private readonly ILogger<MareConfigurationServiceClient<T>> _logger;
private readonly GrpcClientFactory _grpcClientFactory; private readonly ServerTokenGenerator _serverTokenGenerator;
private readonly string _grpcClientName;
private readonly CancellationTokenSource _updateTaskCts = new(); private readonly CancellationTokenSource _updateTaskCts = new();
private ConfigurationServiceClient _configurationServiceClient;
private bool _initialized = false; 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; _logger = logger;
_grpcClientFactory = grpcClientFactory; _serverTokenGenerator = serverTokenGenerator;
_grpcClientName = grpcClientName; _httpClient = new();
} }
public bool IsMain => false; public bool IsMain => false;
public T1 GetValueOrDefault<T1>(string key, T1 defaultValue) 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 == null) return defaultValue;
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}"); 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(); bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
@@ -46,7 +58,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
return (T1)remotevalue; return (T1)remotevalue;
} }
var value = prop.GetValue(_config); var value = prop.GetValue(_config.CurrentValue);
var defaultPropValue = prop.PropertyType.IsValueType ? Activator.CreateInstance(prop.PropertyType) : null; var defaultPropValue = prop.PropertyType.IsValueType ? Activator.CreateInstance(prop.PropertyType) : null;
if (value == defaultPropValue) return defaultValue; if (value == defaultPropValue) return defaultValue;
return (T1)value; return (T1)value;
@@ -54,7 +66,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
public T1 GetValue<T1>(string key) 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 == null) throw new KeyNotFoundException(key);
if (prop.PropertyType != typeof(T1)) throw new InvalidCastException($"Invalid Cast: Property {key} is {prop.PropertyType}, wanted: {typeof(T1)}"); 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(); bool isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), inherit: true).Any();
@@ -63,19 +75,19 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
return (T1)remotevalue; return (T1)remotevalue;
} }
var value = prop.GetValue(_config); var value = prop.GetValue(_config.CurrentValue);
return (T1)value; return (T1)value;
} }
public override string ToString() public override string ToString()
{ {
var props = _config.GetType().GetProperties(); var props = _config.CurrentValue.GetType().GetProperties();
StringBuilder sb = new(); StringBuilder sb = new();
foreach (var prop in props) foreach (var prop in props)
{ {
var isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any(); var isRemote = prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any();
var getValueMethod = GetType().GetMethod(nameof(GetValue)).MakeGenericMethod(prop.PropertyType); 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)) if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) && !typeof(string).IsAssignableFrom(prop.PropertyType))
{ {
var enumVal = (IEnumerable)value; var enumVal = (IEnumerable)value;
@@ -90,15 +102,18 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
return sb.ToString(); 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 try
{ {
_logger.LogInformation("Getting {key} from Grpc", key); _logger.LogInformation("Getting {key} from Http", key);
var response = await client.GetConfigurationEntryAsync(new KeyMessage { Key = key, Default = Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }).ConfigureAwait(false); HttpRequestMessage msg = new(HttpMethod.Get, GetRoute(key, Convert.ToString(defaultValue, CultureInfo.InvariantCulture)));
_logger.LogInformation("Grpc Response for {key} = {value}", key, response.Value); msg.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
return JsonSerializer.Deserialize<T1>(response.Value); 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) catch (Exception ex)
{ {
@@ -111,20 +126,19 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
{ {
while (!ct.IsCancellationRequested) while (!ct.IsCancellationRequested)
{ {
_logger.LogInformation("Getting Properties from GRPC"); _logger.LogInformation("Getting Properties from Remote for " + typeof(T));
try try
{ {
_configurationServiceClient = _grpcClientFactory.CreateClient<ConfigurationServiceClient>(_grpcClientName); var properties = _config.CurrentValue.GetType().GetProperties();
var properties = _config.GetType().GetProperties();
foreach (var prop in properties) foreach (var prop in properties)
{ {
try try
{ {
if (!prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()) continue; if (!prop.GetCustomAttributes(typeof(RemoteConfigurationAttribute), true).Any()) continue;
_logger.LogInformation("Checking Property " + prop.Name); _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 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); await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result"); var resultProperty = task.GetType().GetProperty("Result");
@@ -147,12 +161,12 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
_initialized = true; _initialized = true;
} }
_logger.LogInformation("Saved properties from GRPC are now:"); _logger.LogInformation("Saved properties from HTTP are now:");
_logger.LogInformation(ToString()); _logger.LogInformation(ToString());
} }
catch (Exception ex) 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); await Task.Delay(TimeSpan.FromMinutes(30), ct).ConfigureAwait(false);
@@ -169,6 +183,7 @@ public class MareConfigurationServiceClient<T> : IHostedService, IConfigurationS
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
_updateTaskCts.Cancel(); _updateTaskCts.Cancel();
_httpClient.Dispose();
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@@ -6,31 +6,31 @@ namespace MareSynchronosShared.Services;
public class MareConfigurationServiceServer<T> : IConfigurationService<T> where T : class, IMareConfiguration public class MareConfigurationServiceServer<T> : IConfigurationService<T> where T : class, IMareConfiguration
{ {
private readonly T _config; private readonly IOptionsMonitor<T> _config;
public bool IsMain => true; 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) 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) public T1 GetValue<T1>(string key)
{ {
return _config.GetValue<T1>(key); return _config.CurrentValue.GetValue<T1>(key);
} }
public override string ToString() public override string ToString()
{ {
var props = _config.GetType().GetProperties(); var props = _config.CurrentValue.GetType().GetProperties();
StringBuilder sb = new(); StringBuilder sb = new();
foreach (var prop in props) 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()); sb.AppendLine(_config.ToString());

View File

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

View File

@@ -1,7 +1,4 @@
using System.Text.Json.Serialization; namespace MareSynchronosShared.Utils;
using System.Text.RegularExpressions;
namespace MareSynchronosShared.Utils;
public class CdnShardConfiguration public class CdnShardConfiguration
{ {

View File

@@ -4,21 +4,17 @@ namespace MareSynchronosShared.Utils;
public class MareConfigurationAuthBase : MareConfigurationBase public class MareConfigurationAuthBase : MareConfigurationBase
{ {
public Uri MainServerGrpcAddress { get; set; } = null;
[RemoteConfiguration] [RemoteConfiguration]
public int FailedAuthForTempBan { get; set; } = 5; public int FailedAuthForTempBan { get; set; } = 5;
[RemoteConfiguration] [RemoteConfiguration]
public int TempBanDurationInMinutes { get; set; } = 5; public int TempBanDurationInMinutes { get; set; } = 5;
[RemoteConfiguration] [RemoteConfiguration]
public List<string> WhitelistedIps { get; set; } = new(); public List<string> WhitelistedIps { get; set; } = new();
[RemoteConfiguration]
public string Jwt { get; set; } = string.Empty;
public override string ToString() public override string ToString()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine(base.ToString()); sb.AppendLine(base.ToString());
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}"); sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}");
sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}"); sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}");
sb.AppendLine($"{nameof(Jwt)} => {Jwt}"); sb.AppendLine($"{nameof(Jwt)} => {Jwt}");

View File

@@ -1,5 +1,4 @@
using System.Globalization; using System.Reflection;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@@ -7,9 +6,11 @@ namespace MareSynchronosShared.Utils;
public class MareConfigurationBase : IMareConfiguration public class MareConfigurationBase : IMareConfiguration
{ {
public Uri MainServerAddress { get; set; }
public int DbContextPoolSize { get; set; } = 100; public int DbContextPoolSize { get; set; } = 100;
public string ShardName { get; set; } = string.Empty; public string ShardName { get; set; } = string.Empty;
public int MetricsPort { get; set; } public int MetricsPort { get; set; }
public string Jwt { get; set; } = string.Empty;
public T GetValue<T>(string key) public T GetValue<T>(string key)
{ {
@@ -39,6 +40,7 @@ public class MareConfigurationBase : IMareConfiguration
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine(base.ToString()); sb.AppendLine(base.ToString());
sb.AppendLine($"{nameof(MainServerAddress)} => {MainServerAddress}");
sb.AppendLine($"{nameof(ShardName)} => {ShardName}"); sb.AppendLine($"{nameof(ShardName)} => {ShardName}");
sb.AppendLine($"{nameof(DbContextPoolSize)} => {DbContextPoolSize}"); sb.AppendLine($"{nameof(DbContextPoolSize)} => {DbContextPoolSize}");
return sb.ToString(); return sb.ToString();

View File

@@ -28,7 +28,6 @@ public class ServerConfiguration : MareConfigurationAuthBase
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine(base.ToString()); sb.AppendLine(base.ToString());
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
sb.AppendLine($"{nameof(CdnFullUrl)} => {CdnFullUrl}"); sb.AppendLine($"{nameof(CdnFullUrl)} => {CdnFullUrl}");
sb.AppendLine($"{nameof(CdnShardConfiguration)} => {string.Join(", ", CdnShardConfiguration.Select(c => c.ToString()))}"); sb.AppendLine($"{nameof(CdnShardConfiguration)} => {string.Join(", ", CdnShardConfiguration.Select(c => c.ToString()))}");
sb.AppendLine($"{nameof(StaticFileServiceAddress)} => {StaticFileServiceAddress}"); sb.AppendLine($"{nameof(StaticFileServiceAddress)} => {StaticFileServiceAddress}");

View File

@@ -1,5 +1,5 @@
using MareSynchronosShared.Services; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
@@ -9,7 +9,7 @@ namespace MareSynchronosShared.Utils;
public class ServerTokenGenerator public class ServerTokenGenerator
{ {
private readonly IConfigurationService<MareConfigurationAuthBase> _configuration; private readonly IOptionsMonitor<MareConfigurationAuthBase> _configuration;
private readonly ILogger<ServerTokenGenerator> _logger; private readonly ILogger<ServerTokenGenerator> _logger;
private Dictionary<string, string> _tokenDictionary { get; set; } = new(StringComparer.Ordinal); private Dictionary<string, string> _tokenDictionary { get; set; } = new(StringComparer.Ordinal);
@@ -17,7 +17,7 @@ public class ServerTokenGenerator
{ {
get get
{ {
var currentJwt = _configuration.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt)); var currentJwt = _configuration.CurrentValue.Jwt;
if (_tokenDictionary.TryGetValue(currentJwt, out var token)) if (_tokenDictionary.TryGetValue(currentJwt, out var token))
{ {
return 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; _configuration = configuration;
_logger = logger; _logger = logger;
@@ -35,14 +35,14 @@ public class ServerTokenGenerator
private string GenerateToken() 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 authSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(signingKey));
var token = new SecurityTokenDescriptor() var token = new SecurityTokenDescriptor()
{ {
Subject = new ClaimsIdentity(new List<Claim>() 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"), new Claim(MareClaimTypes.Internal, "true"),
}), }),
SigningCredentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256Signature), SigningCredentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256Signature),

View File

@@ -6,6 +6,7 @@ public class ServicesConfiguration : MareConfigurationBase
{ {
public string DiscordBotToken { get; set; } = string.Empty; public string DiscordBotToken { get; set; } = string.Empty;
public Uri MainServerGrpcAddress { get; set; } = null; public Uri MainServerGrpcAddress { get; set; } = null;
public ulong? DiscordChannelForMessages { get; set; } = null;
public override string ToString() public override string ToString()
{ {

View File

@@ -5,27 +5,24 @@ namespace MareSynchronosStaticFilesServer;
public class StaticFilesServerConfiguration : MareConfigurationBase public class StaticFilesServerConfiguration : MareConfigurationBase
{ {
public Uri FileServerGrpcAddress { get; set; } = null; public Uri? MainFileServerAddress { get; set; } = null;
public int ForcedDeletionOfFilesAfterHours { get; set; } = -1; public int ForcedDeletionOfFilesAfterHours { get; set; } = -1;
public double CacheSizeHardLimitInGiB { get; set; } = -1; public double CacheSizeHardLimitInGiB { get; set; } = -1;
public int UnusedFileRetentionPeriodInDays { get; set; } = 14; public int UnusedFileRetentionPeriodInDays { get; set; } = 14;
public string CacheDirectory { get; set; } 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 DownloadQueueSize { get; set; } = 50;
public int DownloadTimeoutSeconds { get; set; } = 5; public int DownloadTimeoutSeconds { get; set; } = 5;
public int DownloadQueueReleaseSeconds { get; set; } = 15; public int DownloadQueueReleaseSeconds { get; set; } = 15;
public int DownloadQueueClearLimit { get; set; } = 15000;
public override string ToString() public override string ToString()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine(base.ToString()); sb.AppendLine(base.ToString());
sb.AppendLine($"{nameof(FileServerGrpcAddress)} => {FileServerGrpcAddress}"); sb.AppendLine($"{nameof(MainFileServerAddress)} => {MainFileServerAddress}");
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
sb.AppendLine($"{nameof(ForcedDeletionOfFilesAfterHours)} => {ForcedDeletionOfFilesAfterHours}"); sb.AppendLine($"{nameof(ForcedDeletionOfFilesAfterHours)} => {ForcedDeletionOfFilesAfterHours}");
sb.AppendLine($"{nameof(CacheSizeHardLimitInGiB)} => {CacheSizeHardLimitInGiB}"); sb.AppendLine($"{nameof(CacheSizeHardLimitInGiB)} => {CacheSizeHardLimitInGiB}");
sb.AppendLine($"{nameof(UnusedFileRetentionPeriodInDays)} => {UnusedFileRetentionPeriodInDays}"); sb.AppendLine($"{nameof(UnusedFileRetentionPeriodInDays)} => {UnusedFileRetentionPeriodInDays}");
sb.AppendLine($"{nameof(CacheDirectory)} => {CacheDirectory}"); sb.AppendLine($"{nameof(CacheDirectory)} => {CacheDirectory}");
sb.AppendLine($"{nameof(RemoteCacheSourceUri)} => {RemoteCacheSourceUri}");
sb.AppendLine($"{nameof(DownloadQueueSize)} => {DownloadQueueSize}"); sb.AppendLine($"{nameof(DownloadQueueSize)} => {DownloadQueueSize}");
sb.AppendLine($"{nameof(DownloadQueueReleaseSeconds)} => {DownloadQueueReleaseSeconds}"); sb.AppendLine($"{nameof(DownloadQueueReleaseSeconds)} => {DownloadQueueReleaseSeconds}");
return sb.ToString(); return sb.ToString();

View File

@@ -1,5 +1,4 @@
using MareSynchronos.API.Routes; using MareSynchronos.API.Routes;
using MareSynchronosShared.Utils;
using MareSynchronosStaticFilesServer.Services; using MareSynchronosStaticFilesServer.Services;
using MareSynchronosStaticFilesServer.Utils; using MareSynchronosStaticFilesServer.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@@ -1,5 +1,4 @@
using MareSynchronos.API.Routes; using MareSynchronos.API.Routes;
using MareSynchronosShared.Utils;
using MareSynchronosStaticFilesServer.Services; using MareSynchronosStaticFilesServer.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@@ -1,5 +1,4 @@
using MareSynchronos.API.Routes; using MareSynchronos.API.Routes;
using MareSynchronosShared.Utils;
using MareSynchronosStaticFilesServer.Services; using MareSynchronosStaticFilesServer.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@@ -24,13 +24,21 @@ public class Program
host.Run(); host.Run();
} }
public static IHostBuilder CreateHostBuilder(string[] args) => public static IHostBuilder CreateHostBuilder(string[] args)
Host.CreateDefaultBuilder(args) {
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.ClearProviders();
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<Startup>();
return Host.CreateDefaultBuilder(args)
.UseSystemd() .UseSystemd()
.UseConsoleLifetime() .UseConsoleLifetime()
.ConfigureWebHostDefaults(webBuilder => .ConfigureWebHostDefaults(webBuilder =>
{ {
webBuilder.UseContentRoot(AppContext.BaseDirectory); webBuilder.UseContentRoot(AppContext.BaseDirectory);
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup(ctx => new Startup(ctx.Configuration, logger));
}); });
}
} }

View File

@@ -26,7 +26,7 @@ public class CachedFileProvider
_fileStatisticsService = fileStatisticsService; _fileStatisticsService = fileStatisticsService;
_metrics = metrics; _metrics = metrics;
_generator = generator; _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)); _basePath = configuration.GetValue<string>(nameof(StaticFilesServerConfiguration.CacheDirectory));
_httpClient = new HttpClient(); _httpClient = new HttpClient();
} }

View File

@@ -1,5 +1,4 @@
using MareSynchronos.API.Routes; using MareSynchronosShared.Metrics;
using MareSynchronosShared.Metrics;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosStaticFilesServer.Utils; using MareSynchronosStaticFilesServer.Utils;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
@@ -19,6 +18,7 @@ public class RequestQueueService : IHostedService
private readonly int _queueExpirationSeconds; private readonly int _queueExpirationSeconds;
private readonly SemaphoreSlim _queueSemaphore = new(1); private readonly SemaphoreSlim _queueSemaphore = new(1);
private readonly SemaphoreSlim _queueProcessingSemaphore = new(1); private readonly SemaphoreSlim _queueProcessingSemaphore = new(1);
private int _queueLimitForReset;
private System.Timers.Timer _queueTimer; private System.Timers.Timer _queueTimer;
private readonly ConcurrentDictionary<Guid, string> _queueRemoval = new(); private readonly ConcurrentDictionary<Guid, string> _queueRemoval = new();
@@ -26,6 +26,7 @@ public class RequestQueueService : IHostedService
{ {
_userQueueRequests = new UserQueueEntry[configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadQueueSize), 50)]; _userQueueRequests = new UserQueueEntry[configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadQueueSize), 50)];
_queueExpirationSeconds = configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadTimeoutSeconds), 5); _queueExpirationSeconds = configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadTimeoutSeconds), 5);
_queueLimitForReset = configurationService.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DownloadQueueClearLimit), 15000);
_metrics = metrics; _metrics = metrics;
_logger = logger; _logger = logger;
_hubContext = hubContext; _hubContext = hubContext;
@@ -120,6 +121,12 @@ public class RequestQueueService : IHostedService
try try
{ {
if (_queue.Count > _queueLimitForReset)
{
_queue.Clear();
return;
}
Parallel.For(0, _userQueueRequests.Length, new ParallelOptions() Parallel.For(0, _userQueueRequests.Length, new ParallelOptions()
{ {
MaxDegreeOfParallelism = 10, MaxDegreeOfParallelism = 10,

View File

@@ -1,24 +1,21 @@
using Grpc.Net.Client.Configuration; using Grpc.Net.Client.Configuration;
using Grpc.Net.ClientFactory;
using MareSynchronos.API;
using MareSynchronosShared.Data; using MareSynchronosShared.Data;
using MareSynchronosShared.Metrics; using MareSynchronosShared.Metrics;
using MareSynchronosShared.Protos;
using MareSynchronosShared.Services; using MareSynchronosShared.Services;
using MareSynchronosShared.Utils; using MareSynchronosShared.Utils;
using MareSynchronosStaticFilesServer.Controllers;
using MareSynchronosStaticFilesServer.Services; using MareSynchronosStaticFilesServer.Services;
using MareSynchronosStaticFilesServer.Utils; using MareSynchronosStaticFilesServer.Utils;
using MessagePack; using MessagePack;
using MessagePack.Resolvers; using MessagePack.Resolvers;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Prometheus; using Prometheus;
using StackExchange.Redis;
using System.Text; using System.Text;
namespace MareSynchronosStaticFilesServer; namespace MareSynchronosStaticFilesServer;
@@ -26,11 +23,14 @@ namespace MareSynchronosStaticFilesServer;
public class Startup public class Startup
{ {
private bool _isMain; private bool _isMain;
public Startup(IConfiguration configuration) private readonly ILogger<Startup> _logger;
public Startup(IConfiguration configuration, ILogger<Startup> logger)
{ {
Configuration = configuration; Configuration = configuration;
_logger = logger;
var mareSettings = Configuration.GetRequiredSection("MareSynchronos"); 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; } public IConfiguration Configuration { get; }
@@ -83,30 +83,6 @@ public class Startup
RetryPolicy = null, 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) services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
.Configure<IConfigurationService<MareConfigurationAuthBase>>((o, s) => .Configure<IConfigurationService<MareConfigurationAuthBase>>((o, s) =>
{ {
@@ -144,26 +120,27 @@ public class Startup
} }
else else
{ {
services.AddSingleton<IConfigurationService<StaticFilesServerConfiguration>>(p => new MareConfigurationServiceClient<StaticFilesServerConfiguration>( services.AddSingleton<IConfigurationService<StaticFilesServerConfiguration>, MareConfigurationServiceClient<StaticFilesServerConfiguration>>();
p.GetRequiredService<ILogger<MareConfigurationServiceClient<StaticFilesServerConfiguration>>>(),
p.GetRequiredService<IOptions<StaticFilesServerConfiguration>>(),
p.GetRequiredService<GrpcClientFactory>(),
"FileServer"));
services.AddHostedService(p => (MareConfigurationServiceClient<StaticFilesServerConfiguration>)p.GetService<IConfigurationService<StaticFilesServerConfiguration>>()); services.AddHostedService(p => (MareConfigurationServiceClient<StaticFilesServerConfiguration>)p.GetService<IConfigurationService<StaticFilesServerConfiguration>>());
} }
services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>>(p => services.AddSingleton<IConfigurationService<MareConfigurationAuthBase>, MareConfigurationServiceClient<MareConfigurationAuthBase>>();
new MareConfigurationServiceClient<MareConfigurationAuthBase>(
p.GetRequiredService<ILogger<MareConfigurationServiceClient<MareConfigurationAuthBase>>>(),
p.GetService<IOptions<MareConfigurationAuthBase>>(),
p.GetRequiredService<GrpcClientFactory>(), "MainServer")
);
services.AddSingleton<ServerTokenGenerator>(); services.AddSingleton<ServerTokenGenerator>();
services.AddSingleton<RequestQueueService>(); services.AddSingleton<RequestQueueService>();
services.AddHostedService(p => p.GetService<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>>()); services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
@@ -198,7 +175,6 @@ public class Startup
signalRServiceBuilder.AddStackExchangeRedis(redisConnection, options => { }); signalRServiceBuilder.AddStackExchangeRedis(redisConnection, options => { });
services.AddHealthChecks(); services.AddHealthChecks();
services.AddControllers();
} }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)