From 72411aa1a124f68fd824ad58df8a90ce969c3b8e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 8 Mar 2021 10:03:36 +0100 Subject: [PATCH] Add nice error page for boot, like in v8 --- .../Models/UnattendedSettings.cs | 6 ++++ .../Runtime/CoreRuntime.cs | 12 +++---- src/Umbraco.Infrastructure/RuntimeState.cs | 13 +++++--- .../ApplicationBuilderExtensions.cs | 1 + .../Middleware/BootFailedMiddleware.cs | 32 +++++++++++++++++-- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs index f8779d817c..26c3ee72c4 100644 --- a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs @@ -19,6 +19,12 @@ namespace Umbraco.Cms.Core.Configuration.Models /// public bool InstallUnattended { get; set; } = false; + /// + /// Gets or sets a value indicating whether unattended upgrades are enabled. + /// + public bool UpgradeUnattended { get; set; } = false; + + /// /// Gets or sets a value to use for creating a user with a name for Unattended Installs /// diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index dab1fe662b..e86da6d002 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -14,7 +14,6 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Migrations.Upgrade; using Umbraco.Cms.Infrastructure.Persistence; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Infrastructure.Runtime { @@ -96,7 +95,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime if (State.Level <= RuntimeLevel.BootFailed) { - throw new InvalidOperationException($"Cannot start the runtime if the runtime level is less than or equal to {RuntimeLevel.BootFailed}"); + return; // The exception will be rethrown by BootFailedMiddelware } IApplicationShutdownRegistry hostingEnvironmentLifetime = _applicationShutdownRegistry; @@ -110,8 +109,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken); - // if level is Updrade and reason is UpgradeMigrations, that means we need to perform an unattended upgrade - if (State.Reason == RuntimeLevelReason.UpgradeMigrations && State.Level == RuntimeLevel.Upgrade) + // if level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade + if (State.Reason == RuntimeLevelReason.UpgradeMigrations && State.Level == RuntimeLevel.Run) { // do the upgrade DoUnattendedUpgrade(); @@ -181,11 +180,12 @@ namespace Umbraco.Cms.Infrastructure.Runtime _databaseFactory.ConfigureForUpgrade(); } } - catch + catch (Exception ex) { State.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException); timer?.Fail(); - throw; + _logger.LogError(ex, "Boot Failed"); + // We do not throw the exception. It will be rethrown by BootFailedMiddleware } } } diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs index 7456fcc1a4..02d4375186 100644 --- a/src/Umbraco.Infrastructure/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/RuntimeState.cs @@ -116,7 +116,8 @@ namespace Umbraco.Cms.Core // else it is bad enough that we want to throw Reason = RuntimeLevelReason.BootFailedCannotConnectToDatabase; - throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); + BootFailedException =new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); + throw BootFailedException; } case UmbracoDatabaseState.NotInstalled: { @@ -133,7 +134,7 @@ namespace Umbraco.Cms.Core // although the files version matches the code version, the database version does not // which means the local files have been upgraded but not the database - need to upgrade _logger.LogDebug("Has not reached the final upgrade step, need to upgrade Umbraco."); - Level = RuntimeLevel.Upgrade; + Level = _unattendedSettings.Value.UpgradeUnattended ? RuntimeLevel.Run : RuntimeLevel.Upgrade; Reason = RuntimeLevelReason.UpgradeMigrations; } break; @@ -192,7 +193,8 @@ namespace Umbraco.Cms.Core // else it is bad enough that we want to throw Reason = RuntimeLevelReason.BootFailedCannotCheckUpgradeState; - throw new BootFailedException("Could not check the upgrade state.", e); + BootFailedException = new BootFailedException("Could not check the upgrade state.", e); + throw BootFailedException; } } @@ -251,9 +253,12 @@ namespace Umbraco.Cms.Core _logger.LogInformation(ex, "Error during unattended install."); database.AbortTransaction(); - throw new UnattendedInstallException( + var innerException = new UnattendedInstallException( "The database configuration failed with the following message: " + ex.Message + "\n Please check log file for additional information (can be found in '/App_Data/Logs/')"); + BootFailedException = new BootFailedException(innerException.Message, innerException); + + throw BootFailedException; } } } diff --git a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs index 42b9b64ff4..50dbe7a6ac 100644 --- a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs @@ -121,6 +121,7 @@ namespace Umbraco.Extensions if (!app.UmbracoCanBoot()) { + app.UseStaticFiles(); // We need static files to show the nice error page. app.UseMiddleware(); } else diff --git a/src/Umbraco.Web.Common/Middleware/BootFailedMiddleware.cs b/src/Umbraco.Web.Common/Middleware/BootFailedMiddleware.cs index 476bb272b2..4f2b35bc93 100644 --- a/src/Umbraco.Web.Common/Middleware/BootFailedMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/BootFailedMiddleware.cs @@ -1,7 +1,10 @@ +using System.IO; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Exceptions; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Web.Common.Middleware @@ -12,10 +15,12 @@ namespace Umbraco.Cms.Web.Common.Middleware public class BootFailedMiddleware : IMiddleware { private readonly IRuntimeState _runtimeState; + private readonly IHostingEnvironment _hostingEnvironment; - public BootFailedMiddleware(IRuntimeState runtimeState) + public BootFailedMiddleware(IRuntimeState runtimeState, IHostingEnvironment hostingEnvironment) { _runtimeState = runtimeState; + _hostingEnvironment = hostingEnvironment; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) @@ -23,12 +28,35 @@ namespace Umbraco.Cms.Web.Common.Middleware if (_runtimeState.Level == RuntimeLevel.BootFailed) { // short circuit - BootFailedException.Rethrow(_runtimeState.BootFailedException); + // + + if (_hostingEnvironment.IsDebugMode) + { + BootFailedException.Rethrow(_runtimeState.BootFailedException); + } + else // Print a nice error page + { + context.Response.Clear(); + context.Response.StatusCode = 500; + + var file = GetBootErrorFileName(); + + var viewContent = await File.ReadAllTextAsync(file); + await context.Response.WriteAsync(viewContent, Encoding.UTF8); + } } else { await next(context); } + + } + private string GetBootErrorFileName() + { + var fileName = _hostingEnvironment.MapPathWebRoot("~/config/errors/BootFailed.html"); + if (File.Exists(fileName)) return fileName; + + return _hostingEnvironment.MapPathWebRoot("~/umbraco/views/errors/BootFailed.html"); } } }