- Rename all TrueCV references to RealCV across the codebase - Add new transparent RealCV logo - Switch from JetBrains Mono to Inter font for better number clarity - Update solution, project files, and namespaces 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
147 lines
5.8 KiB
C#
147 lines
5.8 KiB
C#
using Hangfire;
|
|
using Hangfire.SqlServer;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Polly;
|
|
using Polly.Extensions.Http;
|
|
using Stripe;
|
|
using RealCV.Application.Interfaces;
|
|
using RealCV.Infrastructure.Configuration;
|
|
using RealCV.Infrastructure.Data;
|
|
using RealCV.Infrastructure.ExternalApis;
|
|
using RealCV.Infrastructure.Jobs;
|
|
using RealCV.Infrastructure.Services;
|
|
|
|
namespace RealCV.Infrastructure;
|
|
|
|
public static class DependencyInjection
|
|
{
|
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
// Configure DbContext with SQL Server
|
|
// AddDbContextFactory enables thread-safe parallel operations
|
|
services.AddDbContextFactory<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(
|
|
configuration.GetConnectionString("DefaultConnection"),
|
|
sqlOptions =>
|
|
{
|
|
sqlOptions.EnableRetryOnFailure(
|
|
maxRetryCount: 3,
|
|
maxRetryDelay: TimeSpan.FromSeconds(30),
|
|
errorNumbersToAdd: null);
|
|
}));
|
|
|
|
// Also register DbContext for scoped injection (non-parallel scenarios)
|
|
services.AddDbContext<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(
|
|
configuration.GetConnectionString("DefaultConnection"),
|
|
sqlOptions =>
|
|
{
|
|
sqlOptions.EnableRetryOnFailure(
|
|
maxRetryCount: 3,
|
|
maxRetryDelay: TimeSpan.FromSeconds(30),
|
|
errorNumbersToAdd: null);
|
|
}));
|
|
|
|
// Configure Hangfire with SQL Server storage
|
|
services.AddHangfire(config => config
|
|
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
|
.UseSimpleAssemblyNameTypeSerializer()
|
|
.UseRecommendedSerializerSettings()
|
|
.UseSqlServerStorage(
|
|
configuration.GetConnectionString("HangfireConnection"),
|
|
new SqlServerStorageOptions
|
|
{
|
|
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
|
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
|
QueuePollInterval = TimeSpan.Zero,
|
|
UseRecommendedIsolationLevel = true,
|
|
DisableGlobalLocks = true
|
|
}));
|
|
|
|
services.AddHangfireServer();
|
|
|
|
// Configure options
|
|
services.Configure<CompaniesHouseSettings>(
|
|
configuration.GetSection(CompaniesHouseSettings.SectionName));
|
|
|
|
services.Configure<AnthropicSettings>(
|
|
configuration.GetSection(AnthropicSettings.SectionName));
|
|
|
|
services.Configure<AzureBlobSettings>(
|
|
configuration.GetSection(AzureBlobSettings.SectionName));
|
|
|
|
services.Configure<LocalStorageSettings>(
|
|
configuration.GetSection(LocalStorageSettings.SectionName));
|
|
|
|
services.Configure<StripeSettings>(
|
|
configuration.GetSection(StripeSettings.SectionName));
|
|
|
|
// Configure Stripe API key
|
|
var stripeSettings = configuration.GetSection(StripeSettings.SectionName).Get<StripeSettings>();
|
|
if (!string.IsNullOrEmpty(stripeSettings?.SecretKey))
|
|
{
|
|
StripeConfiguration.ApiKey = stripeSettings.SecretKey;
|
|
}
|
|
|
|
// Configure HttpClient for CompaniesHouseClient with retry policy
|
|
services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
|
|
{
|
|
var settings = configuration
|
|
.GetSection(CompaniesHouseSettings.SectionName)
|
|
.Get<CompaniesHouseSettings>();
|
|
|
|
if (settings is not null)
|
|
{
|
|
client.BaseAddress = new Uri(settings.BaseUrl);
|
|
}
|
|
})
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Register services
|
|
services.AddScoped<ICVParserService, CVParserService>();
|
|
services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
|
|
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
|
|
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
|
|
services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
|
|
services.AddScoped<ICVCheckService, CVCheckService>();
|
|
services.AddScoped<IUserContextService, UserContextService>();
|
|
services.AddScoped<IAuditService, AuditService>();
|
|
services.AddScoped<IStripeService, StripeService>();
|
|
services.AddScoped<ISubscriptionService, Services.SubscriptionService>();
|
|
|
|
// Register file storage - use local storage if configured, otherwise Azure
|
|
var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
|
|
if (useLocalStorage)
|
|
{
|
|
services.AddScoped<IFileStorageService, LocalFileStorageService>();
|
|
}
|
|
else
|
|
{
|
|
services.AddScoped<IFileStorageService, FileStorageService>();
|
|
}
|
|
|
|
// Register Hangfire jobs
|
|
services.AddTransient<ProcessCVCheckJob>();
|
|
services.AddTransient<ResetMonthlyUsageJob>();
|
|
|
|
return services;
|
|
}
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
|
{
|
|
return HttpPolicyExtensions
|
|
.HandleTransientHttpError()
|
|
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
|
|
.WaitAndRetryAsync(
|
|
retryCount: 3,
|
|
sleepDurationProvider: retryAttempt =>
|
|
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
|
onRetry: (outcome, timespan, retryAttempt, context) =>
|
|
{
|
|
// Logging could be added here via ILogger if injected
|
|
});
|
|
}
|
|
}
|