using System.Reflection; using ControlCenter.Data; using ControlCenter.Hubs; using ControlCenter.Repositories; using ControlCenter.Services; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // ── API Services ─────────────────────────────────────────── builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new() { Title = "Control Center API", Version = "v1", Description = "OpenClaw Control Center — Command Hub backend with SignalR real-time updates" }); // 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); } }); // ── Database ──────────────────────────────────────────────── // PostgreSQL via EF Core with Npgsql. // Connection string from appsettings.json or environment variable. builder.Services.AddDbContext(options => options.UseNpgsql( builder.Configuration.GetConnectionString("ControlCenterDb") ?? throw new InvalidOperationException( "Connection string 'ControlCenterDb' not found. " + "Add it to appsettings.json or set the environment variable."))); // ── Repositories ──────────────────────────────────────────── builder.Services.AddScoped(); // ── CORS (kiosk + remote browser) ───────────────────────── // The Control Center frontend runs on a different origin than the backend. // SignalR requires credentials for WebSocket transport, so we use // specific origins rather than AllowAnyOrigin. var corsOrigins = builder.Configuration.GetSection("Cors:AllowedOrigins") .Get() ?? new[] { "http://localhost:4200", "http://localhost:5000" }; builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins(corsOrigins) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); // Required for SignalR WebSocket }); }); // ── SignalR (real-time agent status updates) ─────────────── builder.Services.AddSignalR(); // ── Gateway Bridge Service ──────────────────────────────── // Background service that connects to the OpenClaw Gateway WebSocket // and bridges events to the AgentStatus SignalR hub. builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); var app = builder.Build(); // ── Middleware ────────────────────────────────────────────── if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseCors(); app.UseAuthorization(); app.MapControllers(); // ── Hub Endpoints ─────────────────────────────────────────── // Agent status hub at /hubs/agent-status (matches the design spec) app.MapHub("/hubs/agent-status"); app.Run();