Add ReceiptParseQueue (Channel-based singleton) and ReceiptParseWorkerService (BackgroundService) for sequential receipt parsing. Replaces fire-and-forget Task.Run with a proper queue. ReceiptManager now enqueues uploaded receipts and supports bulk upload via UploadManyUnmappedReceiptsAsync. Worker recovers pending items on startup. Register IAIToolExecutor and IAIVisionClientResolver in DI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.5 KiB
C#
130 lines
4.5 KiB
C#
using System.Globalization;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using MoneyMap.Data;
|
|
using MoneyMap.Services;
|
|
using MoneyMap.Services.AITools;
|
|
|
|
// Set default culture to en-US for currency formatting ($)
|
|
var culture = new CultureInfo("en-US");
|
|
CultureInfo.DefaultThreadCurrentCulture = culture;
|
|
CultureInfo.DefaultThreadCurrentUICulture = culture;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddDbContext<MoneyMapContext>(options =>
|
|
options.UseSqlServer(builder.Configuration.GetConnectionString("MoneyMapDb")));
|
|
|
|
// Add memory cache for services like TransactionCategorizer
|
|
builder.Services.AddMemoryCache();
|
|
|
|
// Add session support
|
|
builder.Services.AddDistributedMemoryCache();
|
|
builder.Services.AddSession(options =>
|
|
{
|
|
options.IdleTimeout = TimeSpan.FromMinutes(30);
|
|
options.Cookie.HttpOnly = true;
|
|
options.Cookie.IsEssential = true;
|
|
options.IOTimeout = TimeSpan.FromMinutes(5); // Increase timeout for large data
|
|
});
|
|
|
|
// Use session-based TempData provider to avoid cookie size limits
|
|
builder.Services.AddRazorPages()
|
|
.AddSessionStateTempDataProvider();
|
|
|
|
// Core transaction and import services
|
|
builder.Services.AddScoped<ITransactionImporter, TransactionImporter>();
|
|
builder.Services.AddScoped<ICardResolver, CardResolver>();
|
|
builder.Services.AddScoped<ITransactionCategorizer, TransactionCategorizer>();
|
|
builder.Services.AddScoped<ITransactionService, TransactionService>();
|
|
builder.Services.AddScoped<ITransactionStatisticsService, TransactionStatisticsService>();
|
|
|
|
// Entity management services
|
|
builder.Services.AddScoped<IAccountService, AccountService>();
|
|
builder.Services.AddScoped<ICardService, CardService>();
|
|
builder.Services.AddScoped<IMerchantService, MerchantService>();
|
|
builder.Services.AddScoped<IBudgetService, BudgetService>();
|
|
|
|
// Receipt services
|
|
builder.Services.AddScoped<IReceiptMatchingService, ReceiptMatchingService>();
|
|
|
|
// Reference data services
|
|
builder.Services.AddScoped<IReferenceDataService, ReferenceDataService>();
|
|
|
|
// Dashboard services
|
|
builder.Services.AddScoped<IDashboardService, DashboardService>();
|
|
builder.Services.AddScoped<IDashboardStatsCalculator, DashboardStatsCalculator>();
|
|
builder.Services.AddScoped<ITopCategoriesProvider, TopCategoriesProvider>();
|
|
builder.Services.AddScoped<IRecentTransactionsProvider, RecentTransactionsProvider>();
|
|
builder.Services.AddScoped<ISpendTrendsProvider, SpendTrendsProvider>();
|
|
|
|
// Receipt services
|
|
builder.Services.AddScoped<IReceiptManager, ReceiptManager>();
|
|
builder.Services.AddScoped<IReceiptAutoMapper, ReceiptAutoMapper>();
|
|
builder.Services.AddScoped<IPdfToImageConverter, PdfToImageConverter>();
|
|
|
|
// Receipt parse queue and background worker
|
|
builder.Services.AddSingleton<IReceiptParseQueue, ReceiptParseQueue>();
|
|
builder.Services.AddHostedService<ReceiptParseWorkerService>();
|
|
|
|
// AI vision clients and tool-use support
|
|
builder.Services.AddHttpClient<OpenAIVisionClient>();
|
|
builder.Services.AddHttpClient<ClaudeVisionClient>();
|
|
builder.Services.AddHttpClient<OllamaVisionClient>();
|
|
builder.Services.AddHttpClient<LlamaCppVisionClient>();
|
|
builder.Services.AddScoped<IAIVisionClientResolver, AIVisionClientResolver>();
|
|
builder.Services.AddScoped<IAIToolExecutor, AIToolExecutor>();
|
|
builder.Services.AddScoped<IReceiptParser, AIReceiptParser>();
|
|
|
|
// AI categorization service
|
|
builder.Services.AddHttpClient<ITransactionAICategorizer, TransactionAICategorizer>();
|
|
|
|
// Model warmup service - preloads the configured AI model on startup
|
|
builder.Services.AddHostedService<ModelWarmupService>();
|
|
|
|
// Financial audit API service
|
|
builder.Services.AddScoped<IFinancialAuditService, FinancialAuditService>();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Seed default category mappings on startup
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
var categorizer = scope.ServiceProvider.GetRequiredService<ITransactionCategorizer>();
|
|
await categorizer.SeedDefaultMappingsAsync();
|
|
}
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/Error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
app.UseStaticFiles();
|
|
|
|
app.UseRouting();
|
|
|
|
app.UseSession();
|
|
|
|
app.UseAuthorization();
|
|
|
|
app.MapRazorPages();
|
|
|
|
// Financial Audit API endpoint
|
|
app.MapGet("/api/audit", async (
|
|
IFinancialAuditService auditService,
|
|
DateTime? startDate,
|
|
DateTime? endDate,
|
|
bool includeTransactions = false) =>
|
|
{
|
|
var end = endDate ?? DateTime.Today;
|
|
var start = startDate ?? end.AddDays(-90);
|
|
|
|
var result = await auditService.GenerateAuditAsync(start, end, includeTransactions);
|
|
return Results.Ok(result);
|
|
})
|
|
.WithName("GetFinancialAudit");
|
|
|
|
app.Run();
|