All checks were successful
Dev Build / build-test (pull_request) Successful in 3m11s
145 lines
6.5 KiB
C#
145 lines
6.5 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>();
|
|
|
|
// ── Low Stock Detection ────────────────────────────────────
|
|
builder.Services.AddSingleton<ILowStockDetector, LowStockDetector>();
|
|
|
|
// ── Usage Logging ───────────────────────────────────────────
|
|
builder.Services.AddScoped<IUsageLogService, UsageLogService>();
|
|
|
|
// ── 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}";
|
|
} |