138 lines
4.3 KiB
C#
138 lines
4.3 KiB
C#
// -----------------------------------------------------------------------------
|
|
// Program.cs
|
|
// Sprint: SPRINT_20260125_002_Attestor_trust_automation
|
|
// Task: PROXY-002 - Implement tile-proxy service
|
|
// Description: Tile proxy web service entry point
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using Microsoft.Extensions.Options;
|
|
using Serilog;
|
|
using StellaOps.Attestor.TileProxy;
|
|
using StellaOps.Attestor.TileProxy.Endpoints;
|
|
using StellaOps.Attestor.TileProxy.Jobs;
|
|
using StellaOps.Attestor.TileProxy.Services;
|
|
|
|
const string ConfigurationSection = "tile_proxy";
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Configure logging
|
|
builder.Host.UseSerilog((context, config) =>
|
|
{
|
|
config
|
|
.ReadFrom.Configuration(context.Configuration)
|
|
.Enrich.FromLogContext()
|
|
.WriteTo.Console(
|
|
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}");
|
|
});
|
|
|
|
// Load configuration
|
|
builder.Configuration
|
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
|
|
.AddEnvironmentVariables("TILE_PROXY__");
|
|
|
|
// Configure options
|
|
builder.Services.Configure<TileProxyOptions>(builder.Configuration.GetSection(ConfigurationSection));
|
|
|
|
// Validate options
|
|
builder.Services.AddSingleton<IValidateOptions<TileProxyOptions>, TileProxyOptionsValidator>();
|
|
|
|
// Register services
|
|
builder.Services.AddSingleton<ContentAddressedTileStore>();
|
|
builder.Services.AddSingleton<TileProxyService>();
|
|
|
|
// Register sync job as hosted service
|
|
builder.Services.AddHostedService<TileSyncJob>();
|
|
|
|
// Configure HTTP client for upstream
|
|
builder.Services.AddHttpClient<TileProxyService>((sp, client) =>
|
|
{
|
|
var options = sp.GetRequiredService<IOptions<TileProxyOptions>>().Value;
|
|
client.BaseAddress = new Uri(options.UpstreamUrl);
|
|
client.Timeout = TimeSpan.FromSeconds(options.Request.TimeoutSeconds);
|
|
client.DefaultRequestHeaders.Add("User-Agent", "StellaOps-TileProxy/1.0");
|
|
});
|
|
|
|
// Add OpenAPI
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Validate options on startup
|
|
var optionsValidator = app.Services.GetRequiredService<IValidateOptions<TileProxyOptions>>();
|
|
var options = app.Services.GetRequiredService<IOptions<TileProxyOptions>>().Value;
|
|
var validationResult = optionsValidator.Validate(null, options);
|
|
if (validationResult.Failed)
|
|
{
|
|
throw new InvalidOperationException($"Configuration validation failed: {validationResult.FailureMessage}");
|
|
}
|
|
|
|
// Configure pipeline
|
|
app.UseSerilogRequestLogging();
|
|
|
|
// Map endpoints
|
|
app.MapTileProxyEndpoints();
|
|
|
|
// Startup message
|
|
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
|
logger.LogInformation(
|
|
"Tile Proxy starting - Upstream: {Upstream}, Cache: {CachePath}",
|
|
options.UpstreamUrl,
|
|
options.Cache.BasePath);
|
|
|
|
app.Run();
|
|
|
|
/// <summary>
|
|
/// Options validator for tile proxy configuration.
|
|
/// </summary>
|
|
public sealed class TileProxyOptionsValidator : IValidateOptions<TileProxyOptions>
|
|
{
|
|
public ValidateOptionsResult Validate(string? name, TileProxyOptions options)
|
|
{
|
|
var errors = new List<string>();
|
|
|
|
if (string.IsNullOrWhiteSpace(options.UpstreamUrl))
|
|
{
|
|
errors.Add("UpstreamUrl is required");
|
|
}
|
|
else if (!Uri.TryCreate(options.UpstreamUrl, UriKind.Absolute, out _))
|
|
{
|
|
errors.Add("UpstreamUrl must be a valid absolute URI");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(options.Origin))
|
|
{
|
|
errors.Add("Origin is required");
|
|
}
|
|
|
|
if (options.Cache.MaxSizeGb < 0)
|
|
{
|
|
errors.Add("Cache.MaxSizeGb cannot be negative");
|
|
}
|
|
|
|
if (options.Cache.CheckpointTtlMinutes < 1)
|
|
{
|
|
errors.Add("Cache.CheckpointTtlMinutes must be at least 1");
|
|
}
|
|
|
|
if (options.Request.TimeoutSeconds < 1)
|
|
{
|
|
errors.Add("Request.TimeoutSeconds must be at least 1");
|
|
}
|
|
|
|
if (options.Tuf.Enabled && string.IsNullOrWhiteSpace(options.Tuf.Url))
|
|
{
|
|
errors.Add("Tuf.Url is required when TUF is enabled");
|
|
}
|
|
|
|
return errors.Count > 0
|
|
? ValidateOptionsResult.Fail(errors)
|
|
: ValidateOptionsResult.Success;
|
|
}
|
|
}
|
|
|
|
public partial class Program
|
|
{
|
|
}
|