145 lines
5.6 KiB
C#
145 lines
5.6 KiB
C#
using System.Text;
|
|
using FastEndpoints;
|
|
using FastEndpoints.Swagger;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using Npgsql;
|
|
using SpaceGame.Api.Universe.Bootstrap;
|
|
|
|
const string StartupScenarioPath = "scenarios/minimal.json";
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddCors((options) =>
|
|
{
|
|
options.AddDefaultPolicy((policy) =>
|
|
{
|
|
policy
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod()
|
|
.AllowAnyOrigin();
|
|
});
|
|
});
|
|
builder.Services
|
|
.AddOptions<StaticDataOptions>()
|
|
.Bind(builder.Configuration.GetSection("StaticData"))
|
|
.Validate(options => !string.IsNullOrWhiteSpace(options.DataRoot), "StaticData:DataRoot must be configured.")
|
|
.PostConfigure(options =>
|
|
{
|
|
if (Path.IsPathRooted(options.DataRoot))
|
|
{
|
|
options.DataRoot = Path.GetFullPath(options.DataRoot);
|
|
return;
|
|
}
|
|
|
|
var candidatePaths = new[]
|
|
{
|
|
Path.GetFullPath(options.DataRoot),
|
|
Path.GetFullPath(Path.Combine(builder.Environment.ContentRootPath, options.DataRoot)),
|
|
Path.GetFullPath(Path.Combine(builder.Environment.ContentRootPath, "..", "..", options.DataRoot)),
|
|
};
|
|
|
|
var resolvedPath = candidatePaths.FirstOrDefault(Directory.Exists);
|
|
if (resolvedPath is null)
|
|
{
|
|
throw new InvalidOperationException($"StaticData:DataRoot '{options.DataRoot}' could not be resolved to an existing directory.");
|
|
}
|
|
|
|
options.DataRoot = resolvedPath;
|
|
})
|
|
.ValidateOnStart();
|
|
builder.Services.Configure<BalanceOptions>(builder.Configuration.GetSection("Balance"));
|
|
builder.Services.Configure<OrbitalSimulationOptions>(builder.Configuration.GetSection("OrbitalSimulation"));
|
|
builder.Services
|
|
.AddOptions<AuthOptions>()
|
|
.Bind(builder.Configuration.GetSection("Auth"))
|
|
.Validate(options => !string.IsNullOrWhiteSpace(options.ConnectionString), "Auth:ConnectionString must be configured.")
|
|
.ValidateOnStart();
|
|
builder.Services
|
|
.AddOptions<JwtOptions>()
|
|
.Bind(builder.Configuration.GetSection("Jwt"))
|
|
.Validate(options => !string.IsNullOrWhiteSpace(options.SigningKey), "Jwt:SigningKey must be configured.")
|
|
.ValidateOnStart();
|
|
|
|
var jwtOptions = builder.Configuration.GetSection("Jwt").Get<JwtOptions>() ?? new JwtOptions();
|
|
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey));
|
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = true,
|
|
ValidateAudience = true,
|
|
ValidateIssuerSigningKey = true,
|
|
ValidateLifetime = true,
|
|
ValidIssuer = jwtOptions.Issuer,
|
|
ValidAudience = jwtOptions.Audience,
|
|
IssuerSigningKey = signingKey,
|
|
ClockSkew = TimeSpan.FromSeconds(30),
|
|
};
|
|
});
|
|
builder.Services
|
|
.AddAuthorizationBuilder()
|
|
.AddPolicy(AuthPolicyNames.AdminAccess, policy =>
|
|
{
|
|
policy.RequireAuthenticatedUser();
|
|
policy.RequireRole(AuthRoleNames.Admin);
|
|
})
|
|
.AddPolicy(AuthPolicyNames.GmAccess, policy =>
|
|
{
|
|
policy.RequireAuthenticatedUser();
|
|
policy.RequireRole(AuthRoleNames.Gm);
|
|
});
|
|
builder.Services.AddHttpContextAccessor();
|
|
|
|
builder.Services.AddSingleton<IBalanceService, BalanceService>();
|
|
builder.Services.AddSingleton<AppVersionService>();
|
|
builder.Services.AddSingleton<IPlayerStateStore, PlayerStateStore>();
|
|
builder.Services.AddSingleton<PlayerFactionProjectionService>();
|
|
builder.Services.AddSingleton<LocalPasswordHasher>();
|
|
builder.Services.AddSingleton<RefreshTokenFactory>();
|
|
builder.Services.AddSingleton<ITokenService, JwtTokenService>();
|
|
builder.Services.AddSingleton<IPasswordResetDelivery, DevPasswordResetDelivery>();
|
|
builder.Services.AddSingleton<IPlayerIdentityResolver, HttpContextPlayerIdentityResolver>();
|
|
builder.Services.AddSingleton((serviceProvider) =>
|
|
{
|
|
var authOptions = serviceProvider.GetRequiredService<Microsoft.Extensions.Options.IOptions<AuthOptions>>();
|
|
return new NpgsqlDataSourceBuilder(authOptions.Value.ConnectionString).Build();
|
|
});
|
|
builder.Services.AddSingleton<IAuthRepository, PostgresAuthRepository>();
|
|
builder.Services.AddSingleton<AuthService>();
|
|
builder.Services.AddSingleton<AuthSchemaInitializer>();
|
|
builder.Services.AddSingleton<DevAuthSeeder>();
|
|
builder.Services.AddTransient<SystemGenerationService>();
|
|
builder.Services.AddTransient<SpatialBuilder>();
|
|
builder.Services.AddTransient<WorldSeedingService>();
|
|
builder.Services.AddTransient<ScenarioValidationService>();
|
|
builder.Services.AddTransient<ScenarioContentBuilder>();
|
|
builder.Services.AddTransient<ScenarioLoader>();
|
|
builder.Services.AddTransient<WorldTopologyBuilder>();
|
|
builder.Services.AddTransient<WorldRuntimeAssembler>();
|
|
builder.Services.AddTransient<WorldBuilder>();
|
|
builder.Services.AddSingleton<IStaticDataProvider, StaticDataProvider>();
|
|
builder.Services.AddSingleton<WorldService>();
|
|
builder.Services.AddSingleton<TelemetryService>();
|
|
builder.Services.AddHostedService<SimulationHostedService>();
|
|
|
|
builder.Services.AddFastEndpoints();
|
|
builder.Services.SwaggerDocument();
|
|
|
|
var app = builder.Build();
|
|
await app.Services.GetRequiredService<AuthSchemaInitializer>().EnsureSchemaAsync(CancellationToken.None);
|
|
if (builder.Environment.IsDevelopment())
|
|
{
|
|
await app.Services.GetRequiredService<DevAuthSeeder>().SeedAsync(CancellationToken.None);
|
|
app.Services.GetRequiredService<WorldService>().LoadFromScenario(StartupScenarioPath);
|
|
}
|
|
|
|
app.UseCors();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
app.UseFastEndpoints();
|
|
app.UseSwaggerGen();
|
|
|
|
app.Run();
|