Program.cs
3,202 bytes
| 1 | using Asp.Versioning; |
|---|---|
| 2 | using Asp.Versioning.ApiExplorer; |
| 3 | using Microsoft.Extensions.Options; |
| 4 | using SplitApp.Modules.Users.Api.Controllers; |
| 5 | using SplitApp.Modules.Users.Infrastructure; |
| 6 | using SplitApp.Shared.Contracts.Users; |
| 7 | using SplitApp.Shared.Messaging; |
| 8 | using SplitApp.Shared.Messaging.Integration.Users; |
| 9 | using SplitApp.UsersService.Hosting; |
| 10 | using SplitApp.UsersService.Messaging; |
| 11 | using Swashbuckle.AspNetCore.SwaggerGen; |
| 12 | |
| 13 | var builder = WebApplication.CreateBuilder(args); |
| 14 | |
| 15 | builder.Services |
| 16 | .AddControllers() |
| 17 | .AddApplicationPart(typeof(AccountController).Assembly); |
| 18 | |
| 19 | builder.Services.AddApiVersioning(options => |
| 20 | { |
| 21 | options.ReportApiVersions = true; |
| 22 | options.DefaultApiVersion = new ApiVersion(1, 0); |
| 23 | options.AssumeDefaultVersionWhenUnspecified = true; |
| 24 | options.ApiVersionReader = new UrlSegmentApiVersionReader(); |
| 25 | }).AddApiExplorer(options => |
| 26 | { |
| 27 | options.GroupNameFormat = "'v'VVV"; |
| 28 | options.SubstituteApiVersionInUrl = true; |
| 29 | }); |
| 30 | |
| 31 | builder.Services.AddEndpointsApiExplorer(); |
| 32 | builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); |
| 33 | builder.Services.AddSwaggerGen(); |
| 34 | |
| 35 | // Users module (Identity + JWT + EF + repos) — reused verbatim from the modular monolith. |
| 36 | builder.Services.AddUsersModule(builder.Configuration); |
| 37 | |
| 38 | builder.Services.AddAuthorization(); |
| 39 | |
| 40 | // CORS — same wide-open policy as the WebApp, so external SPA frontends (Vite/Vue) |
| 41 | // can call /api/v1/identity/* directly without proxy gymnastics. The login response |
| 42 | // returns the JWT as JSON; SPAs store it in localStorage and send it on subsequent |
| 43 | // requests via `Authorization: Bearer ...`, so cross-subdomain cookie issues don't apply. |
| 44 | builder.Services.AddCors(options => |
| 45 | { |
| 46 | options.AddPolicy("CorsAllowAll", policy => |
| 47 | { |
| 48 | policy |
| 49 | .AllowAnyOrigin() |
| 50 | .AllowAnyHeader() |
| 51 | .AllowAnyMethod() |
| 52 | .WithExposedHeaders("X-Version", "X-Version-Created-At"); |
| 53 | }); |
| 54 | }); |
| 55 | |
| 56 | // Messaging: this process advertises serviceName "users-service" in queue names. |
| 57 | builder.Services.AddMessaging(builder.Configuration, serviceName: "users-service"); |
| 58 | builder.Services.AddIntegrationRequestHandler<GetUserByIdRequest, UserDto?, GetUserByIdRequestHandler>(); |
| 59 | builder.Services.AddIntegrationRequestHandler<GetUsersByIdsRequest, UserDto[], GetUsersByIdsRequestHandler>(); |
| 60 | |
| 61 | builder.Services.AddSingleton<SeededHealthState>(); |
| 62 | |
| 63 | var app = builder.Build(); |
| 64 | |
| 65 | // Run migrations + seed roles/users. The compose healthcheck polls /health until this flips. |
| 66 | app.UseUsersModule(); |
| 67 | app.Services.GetRequiredService<SeededHealthState>().MarkSeeded(); |
| 68 | |
| 69 | if (app.Environment.IsDevelopment()) |
| 70 | { |
| 71 | app.UseDeveloperExceptionPage(); |
| 72 | } |
| 73 | |
| 74 | app.UseSwagger(); |
| 75 | app.UseSwaggerUI(options => |
| 76 | { |
| 77 | var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>(); |
| 78 | foreach (var description in provider.ApiVersionDescriptions) |
| 79 | { |
| 80 | options.SwaggerEndpoint( |
| 81 | $"/swagger/{description.GroupName}/swagger.json", |
| 82 | description.GroupName.ToUpperInvariant()); |
| 83 | } |
| 84 | }); |
| 85 | |
| 86 | app.UseRouting(); |
| 87 | app.UseCors("CorsAllowAll"); |
| 88 | app.UseAuthentication(); |
| 89 | app.UseAuthorization(); |
| 90 | app.MapControllers(); |
| 91 | |
| 92 | app.Run(); |
| 93 | |
| 94 | public partial class Program; |
| 95 | |