76 lines
3.1 KiB
C#
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));
|