Files
the-living-world/src/TheLivingWorld.Api/Program.cs
T

76 lines
3.1 KiB
C#

using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;
using TheLivingWorld.Api.Endpoints;
using TheLivingWorld.Api.Generation;
using TheLivingWorld.Api.Simulation;
using TheLivingWorld.Api.Storage;
using TheLivingWorld.Core.Contracts;
using TheLivingWorld.Core.Export;
using TheLivingWorld.Osm;
const string WebClientCorsPolicy = "web-client";
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.Services.ConfigureHttpJsonOptions(options => MapJson.Apply(options.SerializerOptions));
builder.Services.Configure<WorldStorageOptions>(
builder.Configuration.GetSection(WorldStorageOptions.SectionName));
builder.Services.Configure<SimulationOptions>(
builder.Configuration.GetSection(SimulationOptions.SectionName));
// Relative paths in configuration are meant to sit next to the app, not next to whatever the working
// directory happens to be when it is launched.
builder.Services.PostConfigure<WorldStorageOptions>(options =>
options.RootDirectory = RootPath(options.RootDirectory));
#pragma warning disable EXTEXP0001 // RemoveAllResilienceHandlers is still marked experimental.
builder.Services.AddOsmImport(builder.Configuration)
// Overpass answers in minutes, not seconds; the resilience pipeline that AddServiceDefaults installs on
// every client would abandon the request long before that. OverpassClient retries across mirrors itself.
.RemoveAllResilienceHandlers();
#pragma warning restore EXTEXP0001
builder.Services.PostConfigure<OsmOptions>(options =>
options.CacheDirectory = RootPath(options.CacheDirectory));
builder.Services.AddSingleton<WorldStore>();
builder.Services.AddSingleton<ChunkExporter>();
builder.Services.AddSingleton<WorldSimulationHost>();
builder.Services.AddHostedService(provider => provider.GetRequiredService<WorldSimulationHost>());
builder.Services.AddSingleton<WorldGenerationService>();
// Geometry payloads are highly repetitive JSON and compress by roughly an order of magnitude.
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["application/json"]);
});
builder.Services.Configure<BrotliCompressionProviderOptions>(o => o.Level = CompressionLevel.Fastest);
builder.Services.Configure<GzipCompressionProviderOptions>(o => o.Level = CompressionLevel.Fastest);
// The Vite dev server proxies /api during `aspire run`, but the client can also be pointed at the API
// directly, which crosses an origin.
builder.Services.AddCors(options => options.AddPolicy(WebClientCorsPolicy, policy => policy
.SetIsOriginAllowed(origin => new Uri(origin).IsLoopback)
.AllowAnyHeader()
.AllowAnyMethod()));
var app = builder.Build();
app.UseResponseCompression();
app.UseCors(WebClientCorsPolicy);
app.MapDefaultEndpoints();
app.MapWorldEndpoints();
app.Run();
string RootPath(string path) =>
Path.IsPathRooted(path) ? path : Path.GetFullPath(Path.Combine(builder.Environment.ContentRootPath, path));