Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/apps/api/Program.cs
diff options
context:
space:
mode:
authorLeitwolf112026-08-28 23:03:57 +0300
committerLeitwolf112026-08-28 23:03:57 +0300
commit18da54ce240b1bfefce36aa2ec3b44b58bd0dbde (patch)
treece146c2ca1d768f3f6418643b8ba3e6d8abce4df /apps/api/Program.cs
downloadprogmaweb-18da54ce240b1bfefce36aa2ec3b44b58bd0dbde.tar.gz
Initialize Progmaweb 1.0.0 corporate and account platform: add a responsive Next.js corporate site without Visual X# compiler-pipeline content, host-aware account routes, registration and dashboard UX, an ASP.NET Core authentication API with account policies, password hashing, secure sessions, rate limiting and security headers, integration tests on Microsoft Testing Platform, locked dependency graphs, reproducible local orchestration, and separate frontend, backend and security workflows.
Diffstat (limited to 'apps/api/Program.cs')
-rw-r--r--apps/api/Program.cs64
1 files changed, 64 insertions, 0 deletions
diff --git a/apps/api/Program.cs b/apps/api/Program.cs
new file mode 100644
index 0000000..5ca2e17
--- /dev/null
+++ b/apps/api/Program.cs
@@ -0,0 +1,64 @@
+using System.Threading.RateLimiting;
+using Microsoft.AspNetCore.HttpOverrides;
+using Progmasoft.Progmaweb.Api.Accounts;
+using Progmasoft.Progmaweb.Api.Security;
+
+WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
+
+builder.Services.Configure<AccountSessionOptions>(builder.Configuration.GetSection(AccountSessionOptions.SectionName));
+builder.Services.AddSingleton(TimeProvider.System);
+builder.Services.AddSingleton<IAccountStore, InMemoryAccountStore>();
+builder.Services.AddSingleton<AccountService>();
+builder.Services.AddSingleton<SessionService>();
+builder.Services.AddProblemDetails();
+builder.Services.AddHealthChecks();
+builder.Services.AddRateLimiter(options =>
+{
+ options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
+ options.AddPolicy("account-auth", context =>
+ {
+ string partition = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
+ return RateLimitPartition.GetSlidingWindowLimiter(partition, _ => new SlidingWindowRateLimiterOptions
+ {
+ AutoReplenishment = true,
+ PermitLimit = 12,
+ QueueLimit = 0,
+ SegmentsPerWindow = 6,
+ Window = TimeSpan.FromMinutes(3)
+ });
+ });
+});
+
+builder.Services.Configure<ForwardedHeadersOptions>(options =>
+{
+ options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedHost |
+ ForwardedHeaders.XForwardedProto;
+ options.KnownIPNetworks.Clear();
+ options.KnownProxies.Clear();
+});
+
+WebApplication app = builder.Build();
+
+app.UseForwardedHeaders();
+if (!app.Environment.IsDevelopment())
+{
+ app.UseHsts();
+}
+
+app.UseHttpsRedirection();
+app.UseMiddleware<SecurityHeadersMiddleware>();
+app.UseRateLimiter();
+app.UseExceptionHandler();
+
+app.MapHealthChecks("/health");
+app.MapGet("/api/v1/status", () => Results.Ok(new
+{
+ service = "progmaweb-account",
+ status = "online",
+ version = "1.0.0"
+}));
+app.MapAccountEndpoints();
+
+app.Run();
+
+public partial class Program;