using System.Text; using Amazon.S3; using Amazon.SecretsManager; using Amazon.SQS; using FluentValidation; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using ProposalSystem.Api.Middleware; using ProposalSystem.Api.Services; using ProposalSystem.Application.Interfaces; using ProposalSystem.Application.Validators; using ProposalSystem.Infrastructure.Data; using ProposalSystem.Infrastructure.Services; var builder = WebApplication.CreateBuilder(args); // Dev mode flag (read early for conditional setup) var devMode = builder.Configuration.GetValue("Auth:DevMode"); // AWS SDK clients (skip in dev mode — no real AWS credentials needed) if (!devMode) { builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); builder.Services.AddAWSService(); builder.Services.AddAWSService(); builder.Services.AddAWSService(); } // Database var dbSecretArn = builder.Configuration["DB_SECRET_ARN"]; if (!string.IsNullOrEmpty(dbSecretArn)) { var smClient = new AmazonSecretsManagerClient(); var connectionString = SecretsManagerConnectionString.ResolveAsync(smClient, dbSecretArn).GetAwaiter().GetResult(); builder.Services.AddDbContext(options => options.UseNpgsql(connectionString)); } else { builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))); } // Internal API key (for Lambda-to-API calls) var internalApiKeySecretArn = builder.Configuration["INTERNAL_API_KEY_SECRET_ARN"]; if (!string.IsNullOrEmpty(internalApiKeySecretArn)) { var smClient = new AmazonSecretsManagerClient(); var secretResponse = smClient.GetSecretValueAsync(new Amazon.SecretsManager.Model.GetSecretValueRequest { SecretId = internalApiKeySecretArn, }).GetAwaiter().GetResult(); builder.Configuration["INTERNAL_API_KEY"] = secretResponse.SecretString; } // Authentication var cognitoAuthority = builder.Configuration["Auth:Authority"]; if (!string.IsNullOrEmpty(cognitoAuthority)) { builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = cognitoAuthority; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateIssuer = true, ValidateAudience = false, ValidateLifetime = true, RoleClaimType = "cognito:groups", }; }); } else if (devMode) { var devSigningKey = builder.Configuration["Auth:DevSigningKey"]!; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(devSigningKey)), ValidateIssuer = true, ValidIssuer = "proposal-system-dev", ValidateAudience = true, ValidAudience = "proposal-system-dev", ValidateLifetime = true, RoleClaimType = "cognito:groups", }; }); } else { builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(); } builder.Services.AddAuthorization(); // Services builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); if (devMode) builder.Services.AddScoped(); else builder.Services.AddScoped(); builder.Services.AddScoped(); var jobsQueueUrl = builder.Configuration["JOBS_QUEUE_URL"] ?? ""; if (string.IsNullOrEmpty(jobsQueueUrl)) { builder.Services.AddScoped(); } else { builder.Services.AddScoped(sp => { var sqsClient = sp.GetRequiredService(); return new SqsJobPublisher(sqsClient, jobsQueueUrl); }); } // HTTP client for Cognito token exchange builder.Services.AddHttpClient(); // Validation builder.Services.AddValidatorsFromAssemblyContaining(); // Controllers builder.Services.AddControllers(options => { options.Filters.Add(); }); // Middleware builder.Services.AddTransient(); // Health checks builder.Services.AddHealthChecks() .AddDbContextCheck(); // CORS builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins( "https://proposals.seahaven.com", "http://localhost:5173") .AllowAnyMethod() .AllowAnyHeader(); }); }); // Lambda hosting builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi); var app = builder.Build(); app.UseMiddleware(); app.UseCors(); app.UseMiddleware(); app.UseAuthentication(); app.UseAuthorization(); app.Use(async (context, next) => { if (context.User.Identity?.IsAuthenticated == true) { var userService = context.RequestServices.GetRequiredService(); await userService.ResolveAsync(); } await next(); }); app.MapControllers(); app.MapHealthChecks("/api/health"); app.Run();