Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -0,0 +1,120 @@
// Licensed to StellaOps under the AGPL-3.0-or-later license.
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
using Npgsql;
using StellaOps.ReachGraph.Cache;
using StellaOps.ReachGraph.Hashing;
using StellaOps.ReachGraph.Persistence;
using StellaOps.ReachGraph.Serialization;
using StellaOps.ReachGraph.WebService.Services;
using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new()
{
Title = "ReachGraph Store API",
Version = "v1",
Description = "Content-addressed storage for reachability subgraphs"
});
});
// PostgreSQL
var connectionString = builder.Configuration.GetConnectionString("PostgreSQL")
?? throw new InvalidOperationException("PostgreSQL connection string not configured");
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
var dataSource = dataSourceBuilder.Build();
builder.Services.AddSingleton(dataSource);
// Redis/Valkey
var redisConnectionString = builder.Configuration.GetConnectionString("Redis")
?? "localhost:6379";
var redis = ConnectionMultiplexer.Connect(redisConnectionString);
builder.Services.AddSingleton<IConnectionMultiplexer>(redis);
// Core services
builder.Services.AddSingleton<CanonicalReachGraphSerializer>();
builder.Services.AddSingleton<ReachGraphDigestComputer>();
// Persistence
builder.Services.AddScoped<IReachGraphRepository, PostgresReachGraphRepository>();
// Cache
builder.Services.Configure<ReachGraphCacheOptions>(
builder.Configuration.GetSection("ReachGraphCache"));
builder.Services.AddScoped<IReachGraphCache>(sp =>
{
var redisMultiplexer = sp.GetRequiredService<IConnectionMultiplexer>();
var serializer = sp.GetRequiredService<CanonicalReachGraphSerializer>();
var options = Microsoft.Extensions.Options.Options.Create(
builder.Configuration.GetSection("ReachGraphCache").Get<ReachGraphCacheOptions>()
?? new ReachGraphCacheOptions());
var logger = sp.GetRequiredService<ILogger<ReachGraphValkeyCache>>();
// TODO: Get tenant from request context
return new ReachGraphValkeyCache(redisMultiplexer, serializer, options, logger, "default");
});
// Application services
builder.Services.AddScoped<IReachGraphStoreService, ReachGraphStoreService>();
builder.Services.AddScoped<IReachGraphSliceService, ReachGraphSliceService>();
builder.Services.AddScoped<IReachGraphReplayService, ReachGraphReplayService>();
// Rate limiting
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddPolicy("reachgraph-read", ctx =>
RateLimitPartition.GetFixedWindowLimiter(
ctx.User.FindFirst("tenant")?.Value ?? ctx.Request.Headers["X-Tenant-ID"].FirstOrDefault() ?? "anonymous",
_ => new FixedWindowRateLimiterOptions
{
Window = TimeSpan.FromMinutes(1),
PermitLimit = 100
}));
options.AddPolicy("reachgraph-write", ctx =>
RateLimitPartition.GetFixedWindowLimiter(
ctx.User.FindFirst("tenant")?.Value ?? ctx.Request.Headers["X-Tenant-ID"].FirstOrDefault() ?? "anonymous",
_ => new FixedWindowRateLimiterOptions
{
Window = TimeSpan.FromMinutes(1),
PermitLimit = 20
}));
});
// Response compression
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
});
var app = builder.Build();
// Configure pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseResponseCompression();
app.UseRateLimiter();
app.UseAuthorization();
app.MapControllers();
app.Run();
// Make Program class accessible for integration testing
namespace StellaOps.ReachGraph.WebService
{
public partial class Program { }
}