Files
Extrudex/backend/Program.cs
dex-bot 8b2a29881d
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m7s
Dev Build / deploy-dev (pull_request) Has been skipped
Dev Build / notify-success (pull_request) Has been skipped
Dev Build / notify-failure (pull_request) Has been skipped
feat(CUB-8): Create background service for Moonraker mapping
2026-04-27 20:40:23 -04:00

139 lines
6.1 KiB
C#

using System.Reflection;
using Extrudex.API.Filters;
using Extrudex.API.Hubs;
using Extrudex.API.Jobs;
using Extrudex.Domain.Interfaces;
using Extrudex.Infrastructure.Configuration;
using Extrudex.Infrastructure.Data;
using Extrudex.Infrastructure.Services;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// ── Database ───────────────────────────────────────────────
// Connection string resolution (highest priority first):
// 1. EXTRUDEX_DB_CONNECTION_STRING env var (Docker / production)
// 2. Individual env vars: EXTRUDEX_DB_HOST, EXTRUDEX_DB_PORT, etc.
// 3. appsettings.json ConnectionStrings:ExtrudexDb
// 4. Hardcoded default for local dev
var connectionString = Environment.GetEnvironmentVariable("EXTRUDEX_DB_CONNECTION_STRING")
?? BuildConnectionStringFromEnvVars()
?? builder.Configuration.GetConnectionString("ExtrudexDb")
?? "Host=localhost;Port=5432;Database=extrudex;Username=extrudex;Password=changeme";
builder.Services.AddDbContext<ExtrudexDbContext>(options =>
options.UseNpgsql(connectionString));
// ── API Services ───────────────────────────────────────────
builder.Services.AddControllers(options =>
{
options.Filters.AddService<FluentValidationFilter>();
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new()
{
Title = "Extrudex API",
Version = "v1",
Description = "Filament inventory and print tracking system"
});
// Include XML doc comments in Swagger output
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
});
// ── QR Code Generation ──────────────────────────────────────
builder.Services.AddSingleton<IQrCodeService, QrCodeService>();
// ── Cost Per Print Calculation ─────────────────────────────
builder.Services.AddScoped<ICostPerPrintService, CostPerPrintService>();
// ── FluentValidation ──────────────────────────────────────
// Registers all validators from the API assembly into DI.
builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
// Register the FluentValidation action filter so validators run automatically
// on all API controller actions before the action executes.
builder.Services.AddScoped<FluentValidationFilter>();
// ── CORS (kiosk + remote browser) ─────────────────────────
// AllowAnyOrigin disallows credentials by spec; this is fine for
// REST API calls. SignalR WebSockets negotiate without credentials
// by default, so no special CORS policy is needed. If browser clients
// require credentials (cookies, auth headers), replace AllowAnyOrigin
// with .WithOrigins(...) and add .AllowCredentials().
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// ── SignalR (real-time printer updates) ────────────────────
builder.Services.AddSignalR();
// ── Filament Usage Sync (Background Job) ──────────────────
builder.Services.Configure<FilamentUsageSyncOptions>(
builder.Configuration.GetSection(FilamentUsageSyncOptions.SectionName));
builder.Services.AddHttpClient<IMoonrakerClient, MoonrakerClient>(client =>
{
client.DefaultRequestHeaders.Add("User-Agent", "Extrudex/1.0");
});
builder.Services.AddScoped<IFilamentUsageSyncService, FilamentUsageSyncService>();
builder.Services.AddHostedService<FilamentUsageSyncJob>();
// ── Moonraker Printer Sync (Background Service) ──────────
builder.Services.Configure<MoonrakerPrinterSyncOptions>(
builder.Configuration.GetSection(MoonrakerPrinterSyncOptions.SectionName));
builder.Services.AddScoped<IMoonrakerPrinterSyncService, MoonrakerPrinterSyncService>();
builder.Services.AddHostedService<MoonrakerPrinterSyncJob>();
// ── Health Checks ───────────────────────────────────────────
builder.Services.AddHealthChecks()
.AddNpgSql(connectionString);
var app = builder.Build();
// ── Middleware ──────────────────────────────────────────────
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
app.UseAuthorization();
app.MapControllers();
// ── Hub Endpoints ───────────────────────────────────────────
app.MapHub<PrinterHub>("/hubs/printer");
// ── Health Check Endpoint ──────────────────────────────────
app.MapHealthChecks("/health");
app.Run();
// Helper: builds a connection string from individual env vars.
// Returns null if EXTRUDEX_DB_HOST is not set.
static string? BuildConnectionStringFromEnvVars()
{
var host = Environment.GetEnvironmentVariable("EXTRUDEX_DB_HOST");
if (string.IsNullOrEmpty(host)) return null;
var port = Environment.GetEnvironmentVariable("EXTRUDEX_DB_PORT") ?? "5432";
var database = Environment.GetEnvironmentVariable("EXTRUDEX_DB_NAME") ?? "extrudex";
var username = Environment.GetEnvironmentVariable("EXTRUDEX_DB_USER") ?? "extrudex";
var password = Environment.GetEnvironmentVariable("EXTRUDEX_DB_PASSWORD") ?? "changeme";
return $"Host={host};Port={port};Database={database};Username={username};Password={password}";
}