OPC # 0001: Extract OPC into standalone repo
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
using ControlPlane.Api.Consumers;
|
||||
using ControlPlane.Api.Endpoints;
|
||||
using ControlPlane.Api.Services;
|
||||
using ControlPlane.Core.Models;
|
||||
using ControlPlane.Core.Services;
|
||||
using MassTransit;
|
||||
using Npgsql;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.AddServiceDefaults();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.ConfigureHttpJsonOptions(o =>
|
||||
o.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()));
|
||||
|
||||
// In-memory job store - swap for EF Core post-MVP
|
||||
builder.Services.AddSingleton<Dictionary<Guid, ProvisioningJob>>();
|
||||
|
||||
// Tenant registry - reads ClientAssets/{subdomain}.xml files
|
||||
builder.Services.AddSingleton<TenantRegistryService>();
|
||||
|
||||
// SSE event bus - ProgressConsumer writes here, SSE endpoint reads
|
||||
builder.Services.AddSingleton<SseEventBus>();
|
||||
|
||||
// Build + release pipeline services
|
||||
builder.Services.AddSingleton<BuildHistoryService>();
|
||||
builder.Services.AddSingleton<ImageBuildService>();
|
||||
builder.Services.AddSingleton<ReleaseService>();
|
||||
builder.Services.AddSingleton<ProjectBuildService>();
|
||||
builder.Services.AddSingleton<PromotionService>();
|
||||
|
||||
// OPC persistence (raw Npgsql)
|
||||
var opcConnStr = builder.Configuration.GetConnectionString("opcdb");
|
||||
if (!string.IsNullOrWhiteSpace(opcConnStr))
|
||||
builder.Services.AddSingleton(NpgsqlDataSource.Create(opcConnStr));
|
||||
else
|
||||
builder.Services.AddSingleton(NpgsqlDataSource.Create("Host=localhost;Database=opcdb;Username=postgres;Password=controlplane-dev"));
|
||||
builder.Services.AddScoped<OpcService>();
|
||||
|
||||
// Named HttpClient for OpenRouter AI assist proxy
|
||||
builder.Services.AddHttpClient("openrouter");
|
||||
|
||||
// Gitea integration
|
||||
builder.Services.AddHttpClient("gitea").ConfigurePrimaryHttpMessageHandler(() =>
|
||||
new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator });
|
||||
builder.Services.AddScoped<GiteaService>();
|
||||
|
||||
builder.Services.AddMassTransit(x =>
|
||||
{
|
||||
x.SetKebabCaseEndpointNameFormatter();
|
||||
|
||||
// Receives ProvisioningProgressEvent from Worker and pushes to SSE
|
||||
x.AddConsumer<ProvisioningProgressConsumer>();
|
||||
|
||||
x.UsingRabbitMq((ctx, cfg) =>
|
||||
{
|
||||
var connStr = builder.Configuration.GetConnectionString("rabbitmq");
|
||||
if (!string.IsNullOrWhiteSpace(connStr))
|
||||
cfg.Host(new Uri(connStr));
|
||||
cfg.ConfigureEndpoints(ctx);
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapDefaultEndpoints();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.MapOpenApi();
|
||||
|
||||
app.MapProvisioningEndpoints();
|
||||
app.MapTenantLogEndpoints();
|
||||
app.MapImageBuildEndpoints();
|
||||
app.MapReleaseEndpoints();
|
||||
app.MapProjectBuildEndpoints();
|
||||
app.MapGitEndpoints();
|
||||
app.MapPromotionEndpoints();
|
||||
app.MapOpcEndpoints();
|
||||
app.MapGiteaEndpoints();
|
||||
app.MapInfraEndpoints();
|
||||
|
||||
// Ensure OPC tables exist (idempotent — IF NOT EXISTS)
|
||||
var ds = app.Services.GetRequiredService<NpgsqlDataSource>();
|
||||
await using (var cmd = ds.CreateCommand("""
|
||||
CREATE TABLE IF NOT EXISTS opc (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
number VARCHAR(20) NOT NULL UNIQUE,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
type VARCHAR(50) NOT NULL DEFAULT 'General',
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'New',
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'Medium',
|
||||
assignee VARCHAR(200) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS opc_note (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
opc_id UUID NOT NULL REFERENCES opc(id) ON DELETE CASCADE,
|
||||
author VARCHAR(200) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS opc_artifact (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
opc_id UUID NOT NULL REFERENCES opc(id) ON DELETE CASCADE,
|
||||
artifact_type VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(500) NOT NULL DEFAULT '',
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS opc_pinned_commit (
|
||||
opc_id UUID NOT NULL REFERENCES opc(id) ON DELETE CASCADE,
|
||||
hash VARCHAR(40) NOT NULL,
|
||||
short_hash VARCHAR(10) NOT NULL DEFAULT '',
|
||||
subject VARCHAR(1000) NOT NULL DEFAULT '',
|
||||
author VARCHAR(200) NOT NULL DEFAULT '',
|
||||
pinned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
pinned_by VARCHAR(200) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (opc_id, hash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_opc_number ON opc(number);
|
||||
CREATE INDEX IF NOT EXISTS ix_opc_note_opc_id ON opc_note(opc_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_opc_artifact_opc_id ON opc_artifact(opc_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_opc_artifact_type ON opc_artifact(opc_id, artifact_type);
|
||||
CREATE INDEX IF NOT EXISTS ix_opc_pinned_commit_opc_id ON opc_pinned_commit(opc_id);
|
||||
"""))
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
app.Run();
|
||||
Reference in New Issue
Block a user