feat(api): configure API application startup

This commit is contained in:
2024-12-27 12:00:00 -05:00
parent d4c5f257af
commit 4db3e56811

View File

@@ -0,0 +1,108 @@
using System.Text;
using FluentMigrator.Runner;
using Hangfire;
using Hangfire.Redis.StackExchange;
using IncidentOps.Api.Auth;
using IncidentOps.Infrastructure;
using IncidentOps.Infrastructure.Auth;
using IncidentOps.Infrastructure.Migrations;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
// Add controllers
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();
// Configure JWT settings
var jwtSettings = new JwtSettings
{
Issuer = builder.Configuration["Jwt:Issuer"] ?? "incidentops",
Audience = builder.Configuration["Jwt:Audience"] ?? "incidentops",
SigningKey = builder.Configuration["Jwt:SigningKey"] ?? throw new InvalidOperationException("JWT signing key not configured"),
AccessTokenExpirationMinutes = builder.Configuration.GetValue<int>("Jwt:AccessTokenExpirationMinutes", 15),
RefreshTokenExpirationDays = builder.Configuration.GetValue<int>("Jwt:RefreshTokenExpirationDays", 7)
};
// Configure Infrastructure
var connectionString = builder.Configuration.GetConnectionString("Postgres")
?? throw new InvalidOperationException("Postgres connection string not configured");
builder.Services.AddInfrastructure(connectionString, jwtSettings);
// Configure FluentMigrator
builder.Services.AddFluentMigratorCore()
.ConfigureRunner(rb => rb
.AddPostgres()
.WithGlobalConnectionString(connectionString)
.ScanIn(typeof(Migration0001_InitialSchema).Assembly).For.Migrations())
.AddLogging(lb => lb.AddFluentMigratorConsole());
// Configure JWT Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.Issuer,
ValidAudience = jwtSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SigningKey))
};
});
// Configure Authorization
builder.Services.AddSingleton<IAuthorizationHandler, RoleRequirementHandler>();
builder.Services.AddAuthorizationBuilder()
.AddPolicy("Viewer", policy => policy.Requirements.Add(new RoleRequirement(IncidentOps.Domain.Enums.OrgRole.Viewer)))
.AddPolicy("Member", policy => policy.Requirements.Add(new RoleRequirement(IncidentOps.Domain.Enums.OrgRole.Member)))
.AddPolicy("Admin", policy => policy.Requirements.Add(new RoleRequirement(IncidentOps.Domain.Enums.OrgRole.Admin)));
// Configure Hangfire (client only - server runs in Worker)
var redisConnectionString = builder.Configuration["Redis:ConnectionString"]
?? throw new InvalidOperationException("Redis connection string not configured");
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseRedisStorage(ConnectionMultiplexer.Connect(redisConnectionString)));
// Add CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins(builder.Configuration.GetSection("Cors:Origins").Get<string[]>() ?? ["http://localhost:3000"])
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
var app = builder.Build();
// Run migrations
using (var scope = app.Services.CreateScope())
{
var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
runner.MigrateUp();
}
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();