using System.Reflection; using Api.Services; using Api.Services.Contracts; using Microsoft.EntityFrameworkCore; using Models.Settings; using MyAi.Models.Data; using MyAi.Models.Services; using Refit; using Serilog; using Shared.Models.Settings; using StartupHelpers; StartupExtensions.LoadDotEnvFile(); const string ServiceName = "api"; var appVersion = StartupExtensions.GetApplicationVersion(Assembly.GetExecutingAssembly()); try { var builder = WebApplication.CreateBuilder(args); builder.ConfigureJsonSerilog(ServiceName, appVersion); Log.Information("Starting {Service} version {AppVersion}", ServiceName, appVersion); builder.AddAzureKeyVaultIfConfigured(); builder.Services.AddControllers(); builder.Services.Configure(builder.Configuration.GetSection("Google")); builder.Services.Configure(builder.Configuration.GetSection("Contact")); builder.Services.Configure(builder.Configuration.GetSection("Subscribe")); builder.Services.Configure(builder.Configuration.GetSection("Smtp")); builder.Services.Configure(builder.Configuration.GetSection("Captcha")); builder.Services.Configure(builder.Configuration.GetSection("FileStorage")); builder.Services.Configure(builder.Configuration.GetSection("JobSearch")); builder.Services.AddDbContext(options => { var connectionString = builder.Services.GetConfiguredDbConnectionString(builder.Configuration); options.UseSqlServer(connectionString, sql => { sql.MigrationsAssembly("myai-models"); sql.MigrationsHistoryTable(MyAiDbContext.MigrationTableName, MyAiDbContext.SchemaName); }); }); builder.Services.AddSingleton(); builder.Services.AddHttpClient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); static void ConfigureCvMatcherApiClient(IServiceProvider sp, HttpClient client) { var config = sp.GetRequiredService(); var baseUrl = config["CvMatcherApi:BaseUrl"] ?? string.Empty; if (!string.IsNullOrWhiteSpace(baseUrl)) client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/"); var key = config["CvMatcherApi:InternalApiKey"]; if (!string.IsNullOrWhiteSpace(key) && !client.DefaultRequestHeaders.Contains("X-Internal-Api-Key")) client.DefaultRequestHeaders.Add("X-Internal-Api-Key", key); } builder.Services.AddRefitClient() .ConfigureHttpClient(ConfigureCvMatcherApiClient); builder.Services.AddRefitClient() .ConfigureHttpClient(ConfigureCvMatcherApiClient); builder.Services.AddSwaggerWithXmlComments(Assembly.GetExecutingAssembly(), "API"); builder.Services.ConfigureCaddyForwardedHeaders(); builder.Services.AddFrontendCorsFromConfiguration(builder.Configuration); builder.Services.AddPublicApiRateLimiting(builder.Configuration); var app = builder.Build(); app.LogStartupDiagnostics(ServiceName); app.UseForwardedHeaders(); app.UseDefaultSerilogRequestLogging(includeProxyHeaders: true); app.UseSwaggerInDevelopment("API", "API"); app.UseHttpsRedirection(); app.UseAuthorization(); app.UseRouting(); app.UseCors("FrontendOnly"); app.UseRateLimiter(); app.MapControllers(); Log.Information("Running EF Core migrations if any"); using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); } Log.Information("{Service} startup complete. Listening for requests...", ServiceName); app.Run(); } catch (Exception ex) { Log.Fatal(ex, "{Service} terminated unexpectedly", ServiceName); } finally { Log.Information("Shutting down {Service}", ServiceName); Log.CloseAndFlush(); }