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