128 lines
5.3 KiB
C#
128 lines
5.3 KiB
C#
using System.Text.Json.Serialization;
|
||
using ERPCore.Infra.Auth;
|
||
using ERPCore.Infra.Auth.AuthHex;
|
||
using ERPCore.Infra.Persistence;
|
||
using ERPCore.Infra.UoW;
|
||
using ERPCore.Repositories;
|
||
using ERPCore.Repositories.Interfaces;
|
||
using ERPCore.Services;
|
||
using ERPCore.Services.Auth;
|
||
using ERPCore.Services.Interfaces;
|
||
using ERPCore.Services.Stock;
|
||
using ERPCore.System.Errors;
|
||
using Microsoft.AspNetCore.Authentication;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.OpenApi;
|
||
using Serilog;
|
||
|
||
var builder = WebApplication.CreateBuilder(args);
|
||
|
||
// Serilog (file sink) — reads levels from configuration, plus a rolling daily file.
|
||
builder.Host.UseSerilog((ctx, cfg) => cfg
|
||
.ReadFrom.Configuration(ctx.Configuration)
|
||
.WriteTo.File("logs/erpcore-.log", rollingInterval: RollingInterval.Day));
|
||
|
||
// Controllers + JSON: serialize enums as their string names (docs/11 §8, camelCase).
|
||
builder.Services.AddControllers()
|
||
.AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
|
||
|
||
// EF Core + PostgreSQL
|
||
builder.Services.AddDbContext<ErpDbContext>(o =>
|
||
o.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||
|
||
// ProblemDetails (RFC 7807) + domain-exception mapping
|
||
builder.Services.AddProblemDetails();
|
||
builder.Services.AddExceptionHandler<DomainExceptionHandler>();
|
||
|
||
// Auth: validate external AuthHex RS256 tokens + ERP door policy (docs/10 A.4)
|
||
builder.Services.AddErpJwtAuth(builder.Configuration);
|
||
|
||
// AuthController proxy → AuthHex (docs/11 §2.0)
|
||
builder.Services.AddHttpClient<IAuthHexClient, AuthHexClient>(c =>
|
||
{
|
||
var baseUrl = builder.Configuration["AuthHex:BaseUrl"]
|
||
?? throw new InvalidOperationException("AuthHex:BaseUrl is not configured.");
|
||
c.BaseAddress = new Uri(baseUrl);
|
||
});
|
||
builder.Services.AddScoped<IAuthUserService, AuthUserService>();
|
||
builder.Services.AddScoped<IAuthRecoveryService, AuthRecoveryService>();
|
||
builder.Services.AddScoped<IAuthAltService, AuthAltService>();
|
||
|
||
// Current-user (audit actor). AuthHex has no sub/nameid → a claims transformation
|
||
// JIT-provisions a local shadow user and injects the local `int` id as `nameid`.
|
||
builder.Services.AddHttpContextAccessor();
|
||
builder.Services.AddScoped<ICurrentUser, CurrentUser>();
|
||
builder.Services.AddScoped<IClaimsTransformation, ShadowUserClaimsTransformation>();
|
||
|
||
// Unit of work + generic repository base
|
||
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||
|
||
// Master-data services (docs/11 §2)
|
||
builder.Services.AddScoped<IItemService, ItemService>();
|
||
builder.Services.AddScoped<IUomService, UomService>();
|
||
builder.Services.AddScoped<ICategoryService, CategoryService>();
|
||
builder.Services.AddScoped<IBrandService, BrandService>();
|
||
builder.Services.AddScoped<IItemTypeService, ItemTypeService>();
|
||
builder.Services.AddScoped<IProductConfigService, ProductConfigService>();
|
||
builder.Services.AddScoped<IVendorService, VendorService>();
|
||
builder.Services.AddScoped<IWarehouseService, WarehouseService>();
|
||
|
||
// RBAC / sidebar: Role (shadow of AuthHex) + Permission assignment + user management
|
||
builder.Services.AddScoped<IRoleService, RoleService>();
|
||
builder.Services.AddScoped<IUserManagementService, UserManagementService>();
|
||
|
||
// Cross-cutting + procurement services (docs/11 §3)
|
||
builder.Services.AddScoped<INumberSequenceService, NumberSequenceService>();
|
||
builder.Services.AddScoped<IRequisitionService, RequisitionService>();
|
||
builder.Services.AddScoped<IRfqService, RfqService>();
|
||
builder.Services.AddScoped<IPurchaseOrderService, PurchaseOrderService>();
|
||
|
||
// Stock core + goods receipt (docs/11 §4–5)
|
||
builder.Services.AddScoped<IFifoCostingService, FifoCostingService>();
|
||
builder.Services.AddScoped<IStockService, StockService>();
|
||
builder.Services.AddScoped<IGrnService, GrnService>();
|
||
|
||
// Stock transactions + reference data (docs/11 §5–6)
|
||
builder.Services.AddScoped<IStockMutator, StockMutator>();
|
||
builder.Services.AddScoped<IReasonCodeService, ReasonCodeService>();
|
||
builder.Services.AddScoped<IAdjustmentService, AdjustmentService>();
|
||
builder.Services.AddScoped<ITransferService, TransferService>();
|
||
builder.Services.AddScoped<ICountService, CountService>();
|
||
builder.Services.AddScoped<IReorderService, ReorderService>();
|
||
builder.Services.AddScoped<IPurchaseReturnService, PurchaseReturnService>();
|
||
|
||
// Cross-cutting: audit trail + GL-ready journal read access (docs/11 §6 / FR-X-02, FR-STK-13)
|
||
builder.Services.AddScoped<IAuditService, AuditService>();
|
||
|
||
// Health checks (EF Core DB)
|
||
builder.Services.AddHealthChecks().AddDbContextCheck<ErpDbContext>();
|
||
|
||
// Swagger / OpenAPI (Swashbuckle v10 → OpenAPI 3.1)
|
||
builder.Services.AddEndpointsApiExplorer();
|
||
builder.Services.AddSwaggerGen(o => o.SwaggerDoc("v1", new OpenApiInfo { Title = "ERPCore API", Version = "v1" }));
|
||
|
||
var app = builder.Build();
|
||
|
||
// Seed configurable reference data (reason codes) idempotently at startup.
|
||
using (var scope = app.Services.CreateScope())
|
||
{
|
||
var db = scope.ServiceProvider.GetRequiredService<ErpDbContext>();
|
||
await DataSeeder.SeedAsync(db);
|
||
}
|
||
|
||
app.UseSerilogRequestLogging();
|
||
|
||
if (app.Environment.IsDevelopment())
|
||
{
|
||
app.UseSwagger();
|
||
app.UseSwaggerUI();
|
||
}
|
||
|
||
app.UseExceptionHandler(); // -> DomainExceptionHandler / ProblemDetails
|
||
app.UseAuthentication();
|
||
app.UseAuthorization();
|
||
app.MapControllers();
|
||
app.MapHealthChecks("/health");
|
||
app.Run();
|