using Finbuckle.MultiTenant; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using WorkClub.Api.Auth; using WorkClub.Api.Endpoints.Tasks; using WorkClub.Api.Middleware; using WorkClub.Api.Services; using WorkClub.Application.Interfaces; using WorkClub.Infrastructure.Data; using WorkClub.Infrastructure.Data.Interceptors; using WorkClub.Infrastructure.Services; using WorkClub.Infrastructure.Seed; var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); builder.Services.AddMultiTenant() .WithHeaderStrategy("X-Tenant-Id") .WithClaimStrategy("tenant_id") .WithInMemoryStore(options => { options.IsCaseSensitive = false; }); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = builder.Configuration["Keycloak:Authority"]; options.Audience = builder.Configuration["Keycloak:Audience"]; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true }; }); builder.Services.AddScoped(); builder.Services.AddAuthorizationBuilder() .AddPolicy("RequireAdmin", policy => policy.RequireRole("Admin")) .AddPolicy("RequireManager", policy => policy.RequireRole("Admin", "Manager")) .AddPolicy("RequireMember", policy => policy.RequireRole("Admin", "Manager", "Member")) .AddPolicy("RequireViewer", policy => policy.RequireAuthenticatedUser()); builder.Services.AddDbContext((sp, options) => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")) .AddInterceptors( sp.GetRequiredService(), sp.GetRequiredService())); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); if (!string.IsNullOrEmpty(connectionString)) { builder.Services.AddHealthChecks() .AddNpgSql(connectionString); } else { builder.Services.AddHealthChecks(); } var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); using var scope = app.Services.CreateScope(); var seedService = scope.ServiceProvider.GetRequiredService(); await seedService.SeedAsync(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseMultiTenant(); app.UseMiddleware(); app.UseAuthorization(); app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions { Predicate = _ => false }); app.MapHealthChecks("/health/ready"); app.MapHealthChecks("/health/startup"); var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }) .WithName("GetWeatherForecast"); app.MapGet("/api/test", () => Results.Ok(new { message = "Test endpoint" })) .RequireAuthorization(); app.MapTaskEndpoints(); app.Run(); record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } public partial class Program { }