using Microsoft.EntityFrameworkCore; using Infra.Data.EfCore.MySql; using Hangfire; using Hangfire.MySql; using BusinessModels.Clientes; using Hangfire.Dashboard; using NuvemFiscal.Sdk.Model; var builder = WebApplication.CreateBuilder(args); // Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // DbContext (MySQL) var connStr = builder.Configuration.GetConnectionString("Default"); builder.Services.AddDbContext(opt => opt.UseMySql(connStr, ServerVersion.AutoDetect(connStr))); // Hangfire + MySQL builder.Services.AddHangfire(cfg => { cfg .SetDataCompatibilityLevel(CompatibilityLevel.Version_180) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UseStorage(new MySqlStorage( builder.Configuration.GetConnectionString("Default"), new MySqlStorageOptions { TablesPrefix = "hangfire_", TransactionIsolationLevel = System.Transactions.IsolationLevel.ReadCommitted, QueuePollInterval = TimeSpan.FromSeconds(15), PrepareSchemaIfNecessary = true // cria as tabelas no primeiro run })); }); // Sobe o servidor de processamento dentro da própria API builder.Services.AddHangfireServer(); // DI de Application/Adapters/Infra (services.AddScoped<...>) // builder.Services.AddScoped(); // exemplo builder.Services.AddNuvemFiscalSdk(opt => { var s = builder.Configuration.GetSection("NuvemFiscal"); opt.BaseApiUrl = s["BaseApiUrl"]!; opt.ClientId = s["ClientId"]!; opt.ClientSecret = s["ClientSecret"]!; opt.Scope = s.GetValue("Scope") ?? "nfcom"; }); // Serviços da sua API... builder.Services.AddControllers(); var app = builder.Build(); app.UseRouting(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHangfireDashboard("/hangfire", new DashboardOptions { Authorization = new[] { new AllowAllDashboardAuthorizationFilter() } }); // Dashboard do Hangfire (dev: aberto; prod: proteja com auth/policy ou IP) app.MapControllers(); app.MapPost("/nfcom/emitir", async (INfcomSdkService svc, NfcomPedidoEmissao payload, CancellationToken ct) => { var r = await svc.EmitirAsync(payload, ct); return Results.Ok(r); }); app.MapGet("/nfcom/{id}", async (INfcomSdkService svc, string id, CancellationToken ct) => { var r = await svc.ConsultarAsync(id, ct); return Results.Ok(r); }); app.MapGet("/nfcom/{id}/xml", async (INfcomSdkService svc, string id, CancellationToken ct) => { var bytes = await svc.BaixarXmlAsync(id, ct); return Results.File(bytes.Content, "application/xml", $"{id}.xml"); }); // Dispara uma vez (fila imediata) BackgroundJob.Enqueue(() => Console.WriteLine($"Hello, Hangfire! {DateTime.Now}")); // Agenda para daqui 1 minuto BackgroundJob.Schedule(() => Console.WriteLine("Rodou depois de 1 min"), TimeSpan.FromMinutes(1)); // Recorrente (a cada minuto) RecurringJob.AddOrUpdate("ping-db", () => Console.WriteLine("Ping periódico"), Cron.Minutely); app.MapGet("/health", () => Results.Ok("ok")); app.MapGet("/api/next-clientes/{codigo:int}", async (int codigo, AppDbContext db, CancellationToken ct) => { var cliente = await db.Set() .AsNoTracking() .FirstOrDefaultAsync(x => x.Codigo == codigo, ct); return cliente is null ? Results.NotFound(new { message = "NextCliente não encontrado.", codigo }) : Results.Ok(cliente); }) .WithName("GetNextClienteByCodigo") .WithTags("NextClientes") .WithOpenApi(); using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); await db.Database.MigrateAsync(); } app.Run(); public class AllowAllDashboardAuthorizationFilter : IDashboardAuthorizationFilter { public bool Authorize(DashboardContext context) => true; }