110 lines
5.3 KiB
C#
110 lines
5.3 KiB
C#
using Serilog;
|
|
using Tiku.Application;
|
|
using Tiku.Infrastructure;
|
|
using Tiku.Infrastructure.Messaging;
|
|
using Tiku.Infrastructure.Security;
|
|
using Tiku.Api.Caching;
|
|
using Microsoft.AspNetCore.ResponseCompression;
|
|
using System.IO.Compression;
|
|
using Tiku.Api.Observability;
|
|
using Tiku.Application.Tenancy;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
|
|
namespace Tiku.Api.Configuration;
|
|
|
|
public static class DependencyInjection
|
|
{
|
|
public static WebApplicationBuilder AddApiServices(this WebApplicationBuilder builder)
|
|
{
|
|
builder.Services.AddSerilog((services, configuration) => configuration
|
|
.ReadFrom.Configuration(builder.Configuration)
|
|
.ReadFrom.Services(services)
|
|
.Enrich.FromLogContext(),
|
|
preserveStaticLogger: true);
|
|
|
|
builder.Services.AddApiPresentation();
|
|
builder.Services.AddHealthChecks();
|
|
builder.Services.AddApiObservability(builder.Configuration, builder.Environment);
|
|
builder.Services.AddSingleton<OutboxBacklogSnapshot>();
|
|
builder.Services.AddHostedService<OutboxBacklogMonitor>();
|
|
builder.Services.AddApplication();
|
|
builder.Services.AddNetworkConfiguration(builder.Configuration, builder.Environment);
|
|
builder.Services.AddApiRateLimiting(builder.Configuration);
|
|
|
|
var connectionString = Options.OptionsValidation.ResolveDatabaseConnectionString(
|
|
builder.Configuration,
|
|
builder.Environment.IsDevelopment());
|
|
builder.Services.AddInfrastructure(connectionString);
|
|
var redisConnectionString = builder.Configuration.GetConnectionString("Redis") ?? builder.Configuration["REDIS_URL"];
|
|
builder.Services.AddOptions<RedisSecurityConnectionOptions>()
|
|
.Configure(options => options.ConnectionString = redisConnectionString ?? string.Empty)
|
|
.Validate(
|
|
options => !builder.Environment.IsProduction() || !string.IsNullOrWhiteSpace(options.ConnectionString),
|
|
"Redis is required in Production.")
|
|
.ValidateOnStart();
|
|
if (!string.IsNullOrWhiteSpace(redisConnectionString))
|
|
{
|
|
builder.Services.AddRedisSecurity(redisConnectionString, builder.Environment.EnvironmentName);
|
|
}
|
|
else if (builder.Environment.IsProduction())
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Redis is required in Production. Configure ConnectionStrings:Redis or REDIS_URL.");
|
|
}
|
|
|
|
builder.Services.AddOutputCache(options =>
|
|
{
|
|
options.AddPolicy("TenantPublic", new TenantPublicOutputCachePolicy());
|
|
options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(60);
|
|
});
|
|
builder.Services.RemoveAll<ITenantPublicCacheInvalidator>();
|
|
builder.Services.AddSingleton<ITenantPublicCacheInvalidator, TenantPublicCacheInvalidator>();
|
|
if (builder.Environment.IsProduction() && !string.IsNullOrWhiteSpace(redisConnectionString))
|
|
{
|
|
builder.Services.AddStackExchangeRedisOutputCache(options =>
|
|
{
|
|
options.Configuration = redisConnectionString;
|
|
options.InstanceName = $"tiku:{builder.Environment.EnvironmentName.ToLowerInvariant()}:output:";
|
|
});
|
|
}
|
|
builder.Services.AddResponseCompression(options =>
|
|
{
|
|
options.EnableForHttps = true;
|
|
options.Providers.Add<BrotliCompressionProvider>();
|
|
options.Providers.Add<GzipCompressionProvider>();
|
|
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["application/json"]);
|
|
});
|
|
builder.Services.Configure<BrotliCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
|
|
builder.Services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
|
|
var messaging = builder.Configuration.GetSection("RabbitMq").Get<MessagingOptions>() ?? new MessagingOptions();
|
|
builder.Services.AddOptions<MessagingOptions>()
|
|
.Bind(builder.Configuration.GetSection("RabbitMq"))
|
|
.Validate(
|
|
options => !builder.Environment.IsProduction() ||
|
|
(options.IsConfigured &&
|
|
!string.IsNullOrWhiteSpace(options.Username) &&
|
|
!string.IsNullOrWhiteSpace(options.Password)),
|
|
"Production RabbitMQ requires a valid Host, Username and Password.")
|
|
.Validate(
|
|
options => options.OutboxBacklogAlertCount > 0 && options.OutboxOldestMessageAlertSeconds > 0,
|
|
"RabbitMQ outbox alert thresholds must be positive.")
|
|
.ValidateOnStart();
|
|
builder.Services.AddSingleton(messaging);
|
|
if (messaging.IsConfigured)
|
|
{
|
|
messaging.ConfigureConsumers = false;
|
|
builder.Services.AddReliableMessaging(messaging);
|
|
}
|
|
else if (builder.Environment.IsProduction())
|
|
{
|
|
throw new InvalidOperationException("RabbitMQ is required in Production. Configure RabbitMq:Host.");
|
|
}
|
|
|
|
builder.Services.AddApiDataProtection(builder.Configuration, builder.Environment);
|
|
builder.Services.AddExternalServiceOptions(builder.Configuration, builder.Environment);
|
|
builder.Services.AddApiAuthenticationAndAuthorization(builder.Configuration, builder.Environment);
|
|
|
|
return builder;
|
|
}
|
|
}
|