using Microsoft.EntityFrameworkCore; using Asp.Versioning; using FluentValidation; using Hellang.Middleware.ProblemDetails; using MembershipService.API.Application.Behaviors; using MembershipService.Infrastructure; using Microsoft.OpenApi.Models; using Serilog; // EN: Configure Serilog early / VI: Cấu hình Serilog sớm Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateBootstrapLogger(); try { Log.Information("Starting Membership Service API / Khởi động Membership Service API"); var builder = WebApplication.CreateBuilder(args); // EN: Configure Serilog / VI: Cấu hình Serilog builder.Host.UseSerilog((context, services, configuration) => configuration .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Console()); // EN: Add Infrastructure services / VI: Thêm Infrastructure services builder.Services.AddInfrastructure(builder.Configuration); // EN: Add MediatR with behaviors / VI: Thêm MediatR với behaviors builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblyContaining(); cfg.AddOpenBehavior(typeof(LoggingBehavior<,>)); cfg.AddOpenBehavior(typeof(ValidatorBehavior<,>)); cfg.AddOpenBehavior(typeof(TransactionBehavior<,>)); }); // EN: Add FluentValidation / VI: Thêm FluentValidation builder.Services.AddValidatorsFromAssemblyContaining(); // EN: Add API versioning / VI: Thêm API versioning builder.Services.AddApiVersioning(options => { options.DefaultApiVersion = new ApiVersion(1, 0); options.AssumeDefaultVersionWhenUnspecified = true; options.ReportApiVersions = true; options.ApiVersionReader = ApiVersionReader.Combine( new UrlSegmentApiVersionReader(), new HeaderApiVersionReader("X-Api-Version")); }) .AddApiExplorer(options => { options.GroupNameFormat = "'v'VVV"; options.SubstituteApiVersionInUrl = true; }); // EN: Add controllers / VI: Thêm controllers builder.Services.AddControllers(); // EN: Add ProblemDetails middleware (RFC 7807) / VI: Thêm ProblemDetails middleware builder.Services.AddProblemDetails(options => { options.IncludeExceptionDetails = (ctx, ex) => builder.Environment.IsDevelopment(); }); // EN: Add Swagger / VI: Thêm Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "Membership Service API", Version = "v1", Description = """ Membership Service API - Manage member profiles and membership levels ## Features - Member profile management - Membership levels (Free/Basic/Premium) - User preferences """, Contact = new OpenApiContact { Name = "GoodGo Team", Email = "support@goodgo.com", Url = new Uri("https://github.com/goodgo") }, License = new OpenApiLicense { Name = "MIT License", Url = new Uri("https://opensource.org/licenses/MIT") } }); // EN: Include XML comments for better documentation // VI: Include XML comments để documentation tốt hơn var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename); if (File.Exists(xmlPath)) { options.IncludeXmlComments(xmlPath, includeControllerXmlComments: true); } // EN: Add JWT Bearer security definition / VI: Thêm JWT Bearer security definition options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'" }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty() } }); // EN: Enable annotations / VI: Bật annotations options.EnableAnnotations(); }); // EN: Add Authentication / VI: Thêm Authentication builder.Services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = builder.Configuration["Jwt:Authority"] ?? "http://localhost:5001"; options.Audience = builder.Configuration["Jwt:Audience"] ?? "membership-service"; options.RequireHttpsMetadata = false; }); builder.Services.AddAuthorization(); // EN: Add health checks / VI: Thêm health checks builder.Services.AddHealthChecks() .AddNpgSql( builder.Configuration.GetConnectionString("DefaultConnection") ?? builder.Configuration["DATABASE_URL"] ?? "", name: "postgresql", tags: ["db", "postgresql"]); // EN: Add CORS / VI: Thêm CORS builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); // EN: Configure middleware pipeline / VI: Cấu hình middleware pipeline app.UseSerilogRequestLogging(); app.UseProblemDetails(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Membership Service API v1"); c.RoutePrefix = "swagger"; }); } app.UseCors(); app.UseRouting(); // EN: Add Authentication & Authorization middleware / VI: Thêm Authentication & Authorization middleware app.UseAuthentication(); app.UseAuthorization(); // EN: Map health check endpoints / VI: Map health check endpoints app.MapHealthChecks("/health"); app.MapHealthChecks("/health/live", new() { Predicate = _ => false // EN: Just checks app is running / VI: Chỉ kiểm tra app đang chạy }); app.MapHealthChecks("/health/ready"); // EN: Map controllers / VI: Map controllers app.MapControllers(); // EN: Run the application / VI: Chạy ứng dụng // EN: Auto-apply EF Core migrations on startup // VI: Tự động áp dụng EF Core migrations khi khởi động try { using (var scope = app.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); await dbContext.Database.MigrateAsync(); } } catch (Exception ex) { // EN: Log migration errors but don't crash the app (e.g. PendingModelChangesWarning in EF Core 10) // VI: Log lỗi migration nhưng không crash app Console.WriteLine($"Warning: EF Core migration issue: {ex.Message}"); } app.Run(); } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly / Ứng dụng kết thúc bất ngờ"); throw; } finally { Log.CloseAndFlush(); } // EN: Make Program class accessible for integration tests // VI: Làm cho class Program có thể truy cập cho integration tests public partial class Program { }