♻️ Add services to container in Pass
This commit is contained in:
@ -33,6 +33,8 @@
|
||||
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.1"/>
|
||||
<PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="9.0.1"/>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0"/>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,23 +1,40 @@
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Startup;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Configure Kestrel and server options
|
||||
builder.ConfigureAppKestrel();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
// Add metrics and telemetry
|
||||
builder.Services.AddAppMetrics();
|
||||
|
||||
// Add application services
|
||||
builder.Services.AddAppServices(builder.Configuration);
|
||||
builder.Services.AddAppRateLimiting();
|
||||
builder.Services.AddAppAuthentication();
|
||||
builder.Services.AddAppSwagger();
|
||||
|
||||
// Add flush handlers and websocket handlers
|
||||
builder.Services.AddAppFlushHandlers();
|
||||
|
||||
// Add business services
|
||||
builder.Services.AddAppBusinessServices(builder.Configuration);
|
||||
|
||||
// Add scheduled jobs
|
||||
builder.Services.AddAppScheduledJobs();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
// Run database migrations
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
// Configure application middleware pipeline
|
||||
app.ConfigureAppMiddleware(builder.Configuration);
|
||||
|
||||
app.Run();
|
66
DysonNetwork.Pass/Startup/ApplicationConfiguration.cs
Normal file
66
DysonNetwork.Pass/Startup/ApplicationConfiguration.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System.Net;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Prometheus;
|
||||
|
||||
namespace DysonNetwork.Pass.Startup;
|
||||
|
||||
public static class ApplicationConfiguration
|
||||
{
|
||||
public static WebApplication ConfigureAppMiddleware(this WebApplication app, IConfiguration configuration)
|
||||
{
|
||||
app.MapMetrics();
|
||||
app.MapOpenApi();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseRequestLocalization();
|
||||
|
||||
ConfigureForwardedHeaders(app, configuration);
|
||||
|
||||
app.UseCors(opts =>
|
||||
opts.SetIsOriginAllowed(_ => true)
|
||||
.WithExposedHeaders("*")
|
||||
.WithHeaders()
|
||||
.AllowCredentials()
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
);
|
||||
|
||||
app.UseWebSockets();
|
||||
app.UseRateLimiter();
|
||||
app.UseHttpsRedirection();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseMiddleware<PermissionMiddleware>();
|
||||
|
||||
app.MapControllers().RequireRateLimiting("fixed");
|
||||
app.MapStaticAssets().RequireRateLimiting("fixed");
|
||||
app.MapRazorPages().RequireRateLimiting("fixed");
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static void ConfigureForwardedHeaders(WebApplication app, IConfiguration configuration)
|
||||
{
|
||||
var knownProxiesSection = configuration.GetSection("KnownProxies");
|
||||
var forwardedHeadersOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All };
|
||||
|
||||
if (knownProxiesSection.Exists())
|
||||
{
|
||||
var proxyAddresses = knownProxiesSection.Get<string[]>();
|
||||
if (proxyAddresses != null)
|
||||
foreach (var proxy in proxyAddresses)
|
||||
if (IPAddress.TryParse(proxy, out var ipAddress))
|
||||
forwardedHeadersOptions.KnownProxies.Add(ipAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardedHeadersOptions.KnownProxies.Add(IPAddress.Any);
|
||||
forwardedHeadersOptions.KnownProxies.Add(IPAddress.IPv6Any);
|
||||
}
|
||||
|
||||
app.UseForwardedHeaders(forwardedHeadersOptions);
|
||||
}
|
||||
}
|
17
DysonNetwork.Pass/Startup/KestrelConfiguration.cs
Normal file
17
DysonNetwork.Pass/Startup/KestrelConfiguration.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace DysonNetwork.Pass.Startup;
|
||||
|
||||
public static class KestrelConfiguration
|
||||
{
|
||||
public static WebApplicationBuilder ConfigureAppKestrel(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Host.UseContentRoot(Directory.GetCurrentDirectory());
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = 50 * 1024 * 1024;
|
||||
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
|
||||
options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
40
DysonNetwork.Pass/Startup/MetricsConfiguration.cs
Normal file
40
DysonNetwork.Pass/Startup/MetricsConfiguration.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
using Prometheus;
|
||||
using Prometheus.SystemMetrics;
|
||||
|
||||
namespace DysonNetwork.Pass.Startup;
|
||||
|
||||
public static class MetricsConfiguration
|
||||
{
|
||||
public static IServiceCollection AddAppMetrics(this IServiceCollection services)
|
||||
{
|
||||
// Prometheus
|
||||
services.UseHttpClientMetrics();
|
||||
services.AddHealthChecks();
|
||||
services.AddSystemMetrics();
|
||||
services.AddPrometheusEntityFrameworkMetrics();
|
||||
services.AddPrometheusAspNetCoreMetrics();
|
||||
services.AddPrometheusHttpClientMetrics();
|
||||
|
||||
// OpenTelemetry
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(tracing =>
|
||||
{
|
||||
tracing
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
.AddOtlpExporter();
|
||||
})
|
||||
.WithMetrics(metrics =>
|
||||
{
|
||||
metrics
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
.AddRuntimeInstrumentation()
|
||||
.AddOtlpExporter();
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
54
DysonNetwork.Pass/Startup/ScheduledJobsConfiguration.cs
Normal file
54
DysonNetwork.Pass/Startup/ScheduledJobsConfiguration.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using DysonNetwork.Pass.Handlers;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
using Quartz;
|
||||
|
||||
namespace DysonNetwork.Pass.Startup;
|
||||
|
||||
public static class ScheduledJobsConfiguration
|
||||
{
|
||||
public static IServiceCollection AddAppScheduledJobs(this IServiceCollection services)
|
||||
{
|
||||
services.AddQuartz(q =>
|
||||
{
|
||||
var appDatabaseRecyclingJob = new JobKey("AppDatabaseRecycling");
|
||||
q.AddJob<AppDatabaseRecyclingJob>(opts => opts.WithIdentity(appDatabaseRecyclingJob));
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(appDatabaseRecyclingJob)
|
||||
.WithIdentity("AppDatabaseRecyclingTrigger")
|
||||
.WithCronSchedule("0 0 0 * * ?"));
|
||||
|
||||
var actionLogFlushJob = new JobKey("ActionLogFlush");
|
||||
q.AddJob<ActionLogFlushJob>(opts => opts.WithIdentity(actionLogFlushJob));
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(actionLogFlushJob)
|
||||
.WithIdentity("ActionLogFlushTrigger")
|
||||
.WithSimpleSchedule(o => o
|
||||
.WithIntervalInMinutes(5)
|
||||
.RepeatForever())
|
||||
);
|
||||
|
||||
var lastActiveFlushJob = new JobKey("LastActiveFlush");
|
||||
q.AddJob<LastActiveFlushJob>(opts => opts.WithIdentity(lastActiveFlushJob));
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(lastActiveFlushJob)
|
||||
.WithIdentity("LastActiveFlushTrigger")
|
||||
.WithSimpleSchedule(o => o
|
||||
.WithIntervalInMinutes(5)
|
||||
.RepeatForever())
|
||||
);
|
||||
|
||||
var subscriptionRenewalJob = new JobKey("SubscriptionRenewal");
|
||||
q.AddJob<SubscriptionRenewalJob>(opts => opts.WithIdentity(subscriptionRenewalJob));
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(subscriptionRenewalJob)
|
||||
.WithIdentity("SubscriptionRenewalTrigger")
|
||||
.WithSimpleSchedule(o => o
|
||||
.WithIntervalInMinutes(30)
|
||||
.RepeatForever())
|
||||
);
|
||||
});
|
||||
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
195
DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs
Normal file
195
DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using System.Globalization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Auth;
|
||||
using DysonNetwork.Pass.Auth.OpenId;
|
||||
using DysonNetwork.Pass.Email;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.SystemTextJson;
|
||||
using StackExchange.Redis;
|
||||
using System.Text.Json;
|
||||
using System.Threading.RateLimiting;
|
||||
using DysonNetwork.Pass.Auth.OidcProvider.Options;
|
||||
using DysonNetwork.Pass.Auth.OidcProvider.Services;
|
||||
using DysonNetwork.Pass.Handlers;
|
||||
using DysonNetwork.Pass.Wallet.PaymentHandlers;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Geo;
|
||||
|
||||
namespace DysonNetwork.Pass.Startup;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddAppServices(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
services.AddDbContext<AppDatabase>();
|
||||
services.AddSingleton<IConnectionMultiplexer>(_ =>
|
||||
{
|
||||
var connection = configuration.GetConnectionString("FastRetrieve")!;
|
||||
return ConnectionMultiplexer.Connect(connection);
|
||||
});
|
||||
services.AddSingleton<IClock>(SystemClock.Instance);
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddSingleton<ICacheService, CacheServiceRedis>();
|
||||
|
||||
services.AddHttpClient();
|
||||
|
||||
// Register OIDC services
|
||||
services.AddScoped<OidcService, GoogleOidcService>();
|
||||
services.AddScoped<OidcService, AppleOidcService>();
|
||||
services.AddScoped<OidcService, GitHubOidcService>();
|
||||
services.AddScoped<OidcService, MicrosoftOidcService>();
|
||||
services.AddScoped<OidcService, DiscordOidcService>();
|
||||
services.AddScoped<OidcService, AfdianOidcService>();
|
||||
services.AddScoped<GoogleOidcService>();
|
||||
services.AddScoped<AppleOidcService>();
|
||||
services.AddScoped<GitHubOidcService>();
|
||||
services.AddScoped<MicrosoftOidcService>();
|
||||
services.AddScoped<DiscordOidcService>();
|
||||
services.AddScoped<AfdianOidcService>();
|
||||
|
||||
services.AddControllers().AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;
|
||||
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower;
|
||||
|
||||
options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||
}).AddDataAnnotationsLocalization(options =>
|
||||
{
|
||||
options.DataAnnotationLocalizerProvider = (type, factory) =>
|
||||
factory.Create(typeof(SharedResource));
|
||||
});
|
||||
services.AddRazorPages();
|
||||
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
var supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("zh-Hans"),
|
||||
};
|
||||
|
||||
options.SupportedCultures = supportedCultures;
|
||||
options.SupportedUICultures = supportedCultures;
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAppRateLimiting(this IServiceCollection services)
|
||||
{
|
||||
services.AddRateLimiter(o => o.AddFixedWindowLimiter(policyName: "fixed", opts =>
|
||||
{
|
||||
opts.Window = TimeSpan.FromMinutes(1);
|
||||
opts.PermitLimit = 120;
|
||||
opts.QueueLimit = 2;
|
||||
opts.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
||||
}));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAppAuthentication(this IServiceCollection services)
|
||||
{
|
||||
services.AddCors();
|
||||
services.AddAuthorization();
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = AuthConstants.SchemeName;
|
||||
options.DefaultChallengeScheme = AuthConstants.SchemeName;
|
||||
})
|
||||
.AddScheme<DysonTokenAuthOptions, DysonTokenAuthHandler>(AuthConstants.SchemeName, _ => { });
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAppSwagger(this IServiceCollection services)
|
||||
{
|
||||
services.AddEndpointsApiExplorer();
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Version = "v1",
|
||||
Title = "Solar Network API",
|
||||
Description = "An open-source social network",
|
||||
TermsOfService = new Uri("https://solsynth.dev/terms"),
|
||||
License = new OpenApiLicense
|
||||
{
|
||||
Name = "APGLv3",
|
||||
Url = new Uri("https://www.gnu.org/licenses/agpl-3.0.html")
|
||||
}
|
||||
});
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
In = ParameterLocation.Header,
|
||||
Description = "Please enter a valid token",
|
||||
Name = "Authorization",
|
||||
Type = SecuritySchemeType.Http,
|
||||
BearerFormat = "JWT",
|
||||
Scheme = "Bearer"
|
||||
});
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
[]
|
||||
}
|
||||
});
|
||||
});
|
||||
services.AddOpenApi();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAppFlushHandlers(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<FlushBufferService>();
|
||||
services.AddScoped<ActionLogFlushHandler>();
|
||||
services.AddScoped<LastActiveFlushHandler>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAppBusinessServices(this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
services.AddScoped<CompactTokenService>();
|
||||
services.AddScoped<RazorViewRenderer>();
|
||||
services.Configure<GeoIpOptions>(configuration.GetSection("GeoIP"));
|
||||
services.AddScoped<GeoIpService>();
|
||||
services.AddScoped<EmailService>();
|
||||
services.AddScoped<PermissionService>();
|
||||
services.AddScoped<ActionLogService>();
|
||||
services.AddScoped<AccountService>();
|
||||
services.AddScoped<AccountEventService>();
|
||||
services.AddScoped<ActionLogService>();
|
||||
services.AddScoped<RelationshipService>();
|
||||
services.AddScoped<MagicSpellService>();
|
||||
services.AddScoped<NotificationService>();
|
||||
services.AddScoped<AuthService>();
|
||||
services.AddScoped<AccountUsernameService>();
|
||||
services.AddScoped<WalletService>();
|
||||
services.AddScoped<SubscriptionService>();
|
||||
services.AddScoped<PaymentService>();
|
||||
services.AddScoped<AfdianPaymentHandler>();
|
||||
|
||||
services.Configure<OidcProviderOptions>(configuration.GetSection("OidcProvider"));
|
||||
services.AddScoped<OidcProviderService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
@ -71,8 +71,8 @@
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.41" />
|
||||
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="11.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.3" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" Version="9.0.6" />
|
||||
<PackageReference Include="tusdotnet" Version="2.8.1" />
|
||||
</ItemGroup>
|
||||
|
Reference in New Issue
Block a user