diff --git a/Wave/Program.cs b/Wave/Program.cs index 0dc111b..3e7134e 100644 --- a/Wave/Program.cs +++ b/Wave/Program.cs @@ -6,8 +6,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.StaticFiles; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Options; using StackExchange.Redis; using Tomlyn.Extensions.Configuration; @@ -17,31 +15,33 @@ using Wave.Services; using Wave.Utilities; +var logMessages = new List(); + var builder = WebApplication.CreateBuilder(args); builder.Configuration - .AddJsonFile("/configuration/config.json", true, false) - .AddYamlFile("/configuration/config.yml", true, false) - .AddTomlFile("/configuration/config.toml", true, false) - .AddIniFile( "/configuration/config.ini", true, false) - .AddXmlFile( "/configuration/config.xml", true, false) - .AddEnvironmentVariables("WAVE_"); + .AddJsonFile("/configuration/config.json", true, false) + .AddYamlFile("/configuration/config.yml", true, false) + .AddTomlFile("/configuration/config.toml", true, false) + .AddIniFile( "/configuration/config.ini", true, false) + .AddXmlFile( "/configuration/config.xml", true, false) + .AddEnvironmentVariables("WAVE_"); builder.Services.AddRazorComponents().AddInteractiveServerComponents(); builder.Services.AddControllers(options => { - options.OutputFormatters.Add(new SyndicationFeedFormatter()); + options.OutputFormatters.Add(new SyndicationFeedFormatter()); }); builder.Services.AddOutputCache(); #region Data Protection & Redis if (builder.Configuration.GetConnectionString("Redis") is { } redisUri) { - var redis = ConnectionMultiplexer.Connect(redisUri); - builder.Services.AddDataProtection() - .PersistKeysToStackExchangeRedis(redis) - .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { - EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, - ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 - }); + var redis = ConnectionMultiplexer.Connect(redisUri); + builder.Services.AddDataProtection() + .PersistKeysToStackExchangeRedis(redis) + .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { + EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, + ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 + }); builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = redisUri; options.InstanceName = "WaveDistributedCache"; @@ -51,12 +51,12 @@ options.InstanceName = "WaveOutputCache"; }); } else { - builder.Services.AddDataProtection() - .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { - EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, - ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 - }); - Console.WriteLine("No Redis connection string found."); + builder.Services.AddDataProtection() + .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { + EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, + ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 + }); + logMessages.Add("No Redis connection string found."); } #endregion @@ -73,56 +73,56 @@ // Moderators: Can delete Articles / take them Offline // Admins: Can do anything, and assign roles to other users builder.Services.AddAuthorizationBuilder() - .AddPolicy("ArticleEditPermissions", p => p.RequireRole("Author", "Admin")) - .AddPolicy("ArticleReviewPermissions", p => p.RequireRole("Reviewer", "Admin")) - .AddPolicy("ArticleDeletePermissions", p => p.RequireRole("Moderator", "Admin")) - .AddPolicy("CategoryManagePermissions", p => p.RequireRole("Admin")) - .AddPolicy("RoleAssignPermissions", p => p.RequireRole("Admin")) - - .AddPolicy("ArticleEditOrReviewPermissions", p => p.RequireRole("Author", "Reviewer", "Admin")); + .AddPolicy("ArticleEditPermissions", p => p.RequireRole("Author", "Admin")) + .AddPolicy("ArticleReviewPermissions", p => p.RequireRole("Reviewer", "Admin")) + .AddPolicy("ArticleDeletePermissions", p => p.RequireRole("Moderator", "Admin")) + .AddPolicy("CategoryManagePermissions", p => p.RequireRole("Admin")) + .AddPolicy("RoleAssignPermissions", p => p.RequireRole("Admin")) + + .AddPolicy("ArticleEditOrReviewPermissions", p => p.RequireRole("Author", "Reviewer", "Admin")); builder.Services.AddAuthentication(options => { - options.DefaultScheme = IdentityConstants.ApplicationScheme; - options.DefaultSignInScheme = IdentityConstants.ExternalScheme; - }).AddIdentityCookies(); + options.DefaultScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }).AddIdentityCookies(); #endregion #region Identity string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") - ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); + ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); builder.Services.AddDbContextFactory(options => - options.UseNpgsql(connectionString)); + options.UseNpgsql(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) - .AddRoles() - .AddEntityFrameworkStores() - .AddSignInManager() - .AddDefaultTokenProviders() - .AddClaimsPrincipalFactory(); + .AddRoles() + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders() + .AddClaimsPrincipalFactory(); #endregion #region Services builder.Services.AddLocalization(options => { - options.ResourcesPath = "Resources"; + options.ResourcesPath = "Resources"; }); builder.Services.AddScoped(); builder.Services.AddHttpClient(); builder.Services.Configure(builder.Configuration.GetSection(nameof(Customization))); builder.Services.AddCascadingValue("TitlePrefix", - sf => (sf.GetService>()?.Value.AppName ?? "Wave") + " - "); + sf => (sf.GetService>()?.Value.AppName ?? "Wave") + " - "); var smtpConfig = builder.Configuration.GetSection("Email:Smtp"); if (smtpConfig.Exists()) { - builder.Services.Configure(smtpConfig); - builder.Services.AddScoped, SmtpEmailSender>(); + builder.Services.Configure(smtpConfig); + builder.Services.AddScoped, SmtpEmailSender>(); } else { - builder.Services.AddSingleton, IdentityNoOpEmailSender>(); - Console.WriteLine("No Email provider configured."); + builder.Services.AddSingleton, IdentityNoOpEmailSender>(); + logMessages.Add("No Email provider configured."); } #endregion @@ -130,30 +130,30 @@ string[] cultures = ["en-US", "en-GB", "de-DE"]; builder.Services.Configure(options => { - options.ApplyCurrentCultureToResponseHeaders = true; - options.FallBackToParentCultures = true; - options.FallBackToParentUICultures = true; - options.SetDefaultCulture(cultures[0]) - .AddSupportedCultures(cultures) - .AddSupportedUICultures(cultures); + options.ApplyCurrentCultureToResponseHeaders = true; + options.FallBackToParentCultures = true; + options.FallBackToParentUICultures = true; + options.SetDefaultCulture(cultures[0]) + .AddSupportedCultures(cultures) + .AddSupportedUICultures(cultures); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseMigrationsEndPoint(); + app.UseMigrationsEndPoint(); } else { - app.UseExceptionHandler("/Error", createScopeForErrors: true); + app.UseExceptionHandler("/Error", createScopeForErrors: true); } app.UseStaticFiles(new StaticFileOptions { - ContentTypeProvider = new FileExtensionContentTypeProvider { - Mappings = { - [".jxl"] = "image/jxl" - } - } + ContentTypeProvider = new FileExtensionContentTypeProvider { + Mappings = { + [".jxl"] = "image/jxl" + } + } }); app.UseAntiforgery(); @@ -167,20 +167,22 @@ app.UseRequestLocalization(); -{ - using var scope = app.Services.CreateScope(); - using var context = scope.ServiceProvider.GetRequiredService(); - context.Database.Migrate(); +foreach (string message in logMessages) { + app.Logger.LogInformation("{message}", message); +} - var userManager = scope.ServiceProvider.GetRequiredService>(); - - if (userManager.GetUsersInRoleAsync("Admin").Result.Any() is false) { - string admin = Guid.NewGuid().ToString("N")[..16]; - Console.WriteLine( - "There is currently no user in your installation with the admin role, " + - "go to /Admin and use the following password to self promote your account: " + admin); - File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "admin.txt"), admin); - } +{ + using var scope = app.Services.CreateScope(); + using var context = scope.ServiceProvider.GetRequiredService(); + context.Database.Migrate(); + + var userManager = scope.ServiceProvider.GetRequiredService>(); + if (userManager.GetUsersInRoleAsync("Admin").Result.Any() is false) { + string admin = Guid.NewGuid().ToString("N")[..16]; + app.Logger.LogWarning("There is currently no user in your installation with the admin role, " + + "go to /Admin and use the following password to self promote your account: {admin}", admin); + File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "admin.txt"), admin); + } } app.Run();