using HyperBooru.Server.Components; using HyperBooru.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http.Json; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Security.Cryptography; using System.Text.Json.Serialization; namespace HyperBooru.Server; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddHttpContextAccessor(); builder.Services.AddControllers().AddJsonOptions(o => { var converter = new JsonStringEnumConverter(); o.JsonSerializerOptions.Converters.Add(converter); }); builder.Services.Configure(o => { o.SerializerOptions.TypeInfoResolverChain.Insert(0, new ExceptionJsonResolver()); }); builder.Services.AddRazorComponents() .AddInteractiveWebAssemblyComponents(); // Add our custom services builder.Services.AddSingleton(); builder.Services.AddDbContextFactory(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddHostedService(); // Ensure session keys are stored in a persistent location on all platforms builder.Services.AddDataProtection() .PersistKeysToFileSystem(new( builder.Services.BuildServiceProvider() .GetRequiredService() .KeyPath)); // Load our persistently-stored JWT signing key builder.Services.AddSingleton(sp => { var keyPath = Path.Combine( sp.GetRequiredService().KeyPath, "jwt_key"); var protector = sp.GetRequiredService() .CreateProtector("jwt-signing-key"); try { var unprotected = protector.Unprotect(File.ReadAllBytes(keyPath)); var rsa = RSA.Create(); rsa.ImportRSAPrivateKey(unprotected, out var _); return rsa; } catch { var rsa = RSA.Create(4096); var privKey = rsa.ExportRSAPrivateKey(); File.WriteAllBytes(keyPath, protector.Protect(privKey)); return rsa; } }); // Configure JWT token-based authentication builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { var rsa = builder.Services.BuildServiceProvider().GetRequiredService(); o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, ValidateIssuerSigningKey = true, IssuerSigningKey = new RsaSecurityKey(rsa) }; }); // Configure custom authorization policies builder.Services.AddAuthorization(o => { o.AddPolicy(AuthorizationPolicy.NsfwPolicy, p => { p.RequireClaim("nsfw", "true"); }); }); var app = builder.Build(); // Ensure database is created and it's schema is up to date using var scope = app.Services.CreateScope(); using var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); if(app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); } else { app.UseExceptionHandler("/Error"); } app.UseAuthentication(); app.UseAuthorization(); app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseHsts(); app.UseHttpsRedirection(); app.MapStaticAssets(); app.UseMiddleware(); app.UseAntiforgery(); app.MapControllers().DisableAntiforgery(); app.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Client._Imports).Assembly); app.Run(); } }