Initial import into Gitea
This commit is contained in:
105
IntelligenceApi/Program.cs
Normal file
105
IntelligenceApi/Program.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using IntelligenceApi.Engines;
|
||||
using IntelligenceApi.Engines.Franchisee;
|
||||
using IntelligenceApi.Engines.Franchisor;
|
||||
using IntelligenceApi.Engines.General;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// --------------------
|
||||
// Container-friendly HTTP binding
|
||||
// --------------------
|
||||
var port = Environment.GetEnvironmentVariable("PORT") ?? "8081";
|
||||
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
|
||||
|
||||
// --------------------
|
||||
// Services
|
||||
// --------------------
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
// ── Engines ──
|
||||
// General is a singleton — stateless, no DB dependency, safe to share.
|
||||
// Franchisee and Franchisor delegate to General; register as singletons too.
|
||||
// When real data services are added, switch to Scoped.
|
||||
builder.Services.AddSingleton<GeneralEngine>();
|
||||
builder.Services.AddSingleton<FranchiseeEngine>();
|
||||
builder.Services.AddSingleton<FranchisorEngine>();
|
||||
builder.Services.AddSingleton<EngineRouter>();
|
||||
builder.Services.AddSingleton<DemographicsAnalyzer>();
|
||||
|
||||
// --------------------
|
||||
// Security: internal-only access
|
||||
// Simple shared key check — requests must include X-Internal-Key header
|
||||
// matching the INTELLIGENCE_INTERNAL_KEY environment variable.
|
||||
// The Gateway sets this key; the container is not publicly exposed.
|
||||
// --------------------
|
||||
var internalKey = builder.Configuration["INTELLIGENCE_INTERNAL_KEY"]
|
||||
?? Environment.GetEnvironmentVariable("INTELLIGENCE_INTERNAL_KEY");
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Health check (no auth — used by ACA liveness probe)
|
||||
app.MapGet("/health", () => Results.Ok(new
|
||||
{
|
||||
ok = true,
|
||||
service = "IntelligenceApi",
|
||||
timestamp = DateTimeOffset.UtcNow
|
||||
}));
|
||||
|
||||
app.MapGet("/", () => Results.Ok(new
|
||||
{
|
||||
service = "IntelligenceApi",
|
||||
version = "1.0.0",
|
||||
status = "Spend distribution engine running"
|
||||
}));
|
||||
|
||||
// Internal key middleware — validates all /api/* requests
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var path = context.Request.Path.Value ?? "";
|
||||
|
||||
// Pass health and root through unauthenticated
|
||||
if (path == "/" || path.StartsWith("/health", StringComparison.OrdinalIgnoreCase)
|
||||
|| path.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
// In development, skip key check if not configured
|
||||
if (string.IsNullOrWhiteSpace(internalKey))
|
||||
{
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 503;
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
ok = false,
|
||||
error = "Service not configured (missing INTELLIGENCE_INTERNAL_KEY)"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.Request.Headers.TryGetValue("X-Internal-Key", out var key)
|
||||
|| key.FirstOrDefault() != internalKey)
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsJsonAsync(new { ok = false, error = "Unauthorized" });
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
|
||||
Console.WriteLine($"[IntelligenceApi] Starting on port {port}");
|
||||
Console.WriteLine($"[IntelligenceApi] Internal key configured: {!string.IsNullOrWhiteSpace(internalKey)}");
|
||||
|
||||
app.Run();
|
||||
Reference in New Issue
Block a user