using System.Threading.RateLimiting; using Serilog; using SPMS.API.Extensions; using SPMS.Application; using SPMS.Domain.Common; using SPMS.Infrastructure; var builder = WebApplication.CreateBuilder(new WebApplicationOptions { WebRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_WEBROOT") ?? "wwwroot" }); // ===== 1. Serilog ===== builder.Host.UseSerilog((context, config) => config.ReadFrom.Configuration(context.Configuration)); // ===== 2. Services (DI) ===== builder.Services.AddApplication(); builder.Services.AddInfrastructure(builder.Configuration); // ===== 3. Presentation ===== builder.Services.AddControllers(); builder.Services.AddSwaggerDocumentation(); builder.Services.AddJwtAuthentication(builder.Configuration); builder.Services.AddAuthorizationPolicies(); // ===== 4. Rate Limiting ===== builder.Services.AddRateLimiter(options => { options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; options.OnRejected = async (context, cancellationToken) => { context.HttpContext.Response.ContentType = "application/json"; var response = ApiResponse.Fail( ErrorCodes.LimitExceeded, "요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요."); await context.HttpContext.Response.WriteAsJsonAsync(response, cancellationToken); }; options.GlobalLimiter = PartitionedRateLimiter.Create(httpContext => RateLimitPartition.GetFixedWindowLimiter( partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown", factory: _ => new FixedWindowRateLimiterOptions { PermitLimit = 100, Window = TimeSpan.FromMinutes(1) })); }); var app = builder.Build(); // ===== 5. Middleware Pipeline ===== app.UseMiddlewarePipeline(); app.Run();