using ColaFlow.API.Extensions; using ColaFlow.API.Handlers; using ColaFlow.API.Hubs; using ColaFlow.API.Middleware; using ColaFlow.API.Services; using ColaFlow.Modules.Identity.Application; using ColaFlow.Modules.Identity.Infrastructure; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Scalar.AspNetCore; using System.Text; var builder = WebApplication.CreateBuilder(args); // Register ProjectManagement Module builder.Services.AddProjectManagementModule(builder.Configuration, builder.Environment); // Register Identity Module builder.Services.AddIdentityApplication(); builder.Services.AddIdentityInfrastructure(builder.Configuration, builder.Environment); // Add Response Caching builder.Services.AddResponseCaching(); builder.Services.AddMemoryCache(); // Add Response Compression (Gzip and Brotli) builder.Services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add(); options.Providers.Add(); }); builder.Services.Configure(options => { options.Level = System.IO.Compression.CompressionLevel.Fastest; }); builder.Services.Configure(options => { options.Level = System.IO.Compression.CompressionLevel.Fastest; }); // Add controllers builder.Services.AddControllers(); // Configure exception handling (IExceptionHandler - .NET 8+) builder.Services.AddExceptionHandler(); builder.Services.AddProblemDetails(); // Configure Authentication builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("JWT SecretKey not configured"))) }; // Configure SignalR to use JWT from query string (for WebSocket upgrade) options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; // If the request is for SignalR hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs")) { // Read the token from query string context.Token = accessToken; } return Task.CompletedTask; } }; }); // Configure Authorization Policies for RBAC builder.Services.AddAuthorization(options => { // Tenant Owner only options.AddPolicy("RequireTenantOwner", policy => policy.RequireRole("TenantOwner")); // Tenant Owner or Tenant Admin options.AddPolicy("RequireTenantAdmin", policy => policy.RequireRole("TenantOwner", "TenantAdmin")); // Tenant Owner, Tenant Admin, or Tenant Member (excludes Guest and AIAgent) options.AddPolicy("RequireTenantMember", policy => policy.RequireRole("TenantOwner", "TenantAdmin", "TenantMember")); // Human users only (excludes AIAgent) options.AddPolicy("RequireHumanUser", policy => policy.RequireAssertion(context => !context.User.IsInRole("AIAgent"))); // AI Agent only (for MCP integration testing) options.AddPolicy("RequireAIAgent", policy => policy.RequireRole("AIAgent")); }); // Configure CORS for frontend (SignalR requires AllowCredentials) builder.Services.AddCors(options => { options.AddPolicy("AllowFrontend", policy => { policy.WithOrigins("http://localhost:3000", "https://localhost:3000") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); // Required for SignalR }); }); // Configure SignalR builder.Services.AddSignalR(options => { // Enable detailed errors (development only) options.EnableDetailedErrors = builder.Environment.IsDevelopment(); // Client timeout settings options.ClientTimeoutInterval = TimeSpan.FromSeconds(60); options.HandshakeTimeout = TimeSpan.FromSeconds(15); // Keep alive interval options.KeepAliveInterval = TimeSpan.FromSeconds(15); }); // Register Realtime Notification Service builder.Services.AddScoped(); // Register Project Notification Service Adapter (for ProjectManagement module) builder.Services.AddScoped(); // Configure OpenAPI/Scalar builder.Services.AddOpenApi(); var app = builder.Build(); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.MapScalarApiReference(); } // Performance logging (should be early to measure total request time) app.UsePerformanceLogging(); // Global exception handler (should be first in pipeline) app.UseExceptionHandler(); // Enable Response Compression (should be early in pipeline) app.UseResponseCompression(); // Enable CORS app.UseCors("AllowFrontend"); app.UseHttpsRedirection(); // Enable Response Caching (after HTTPS redirection) app.UseResponseCaching(); // Authentication & Authorization app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); // Map SignalR Hubs (after UseAuthorization) app.MapHub("/hubs/project"); app.MapHub("/hubs/notification"); app.Run(); // Make the implicit Program class public for integration tests public partial class Program { }