diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index 8152717a2..833891f28 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -143,8 +143,8 @@ namespace BTCPayServer.Tests s.Driver.AssertNoError(); Assert.Contains("/login", s.Driver.Url); - s.GoToUrl("/UIManage/Index"); - Assert.Contains("ReturnUrl=%2FUIManage%2FIndex", s.Driver.Url); + s.GoToUrl("/account"); + Assert.Contains("ReturnUrl=%2Faccount", s.Driver.Url); // We should be redirected to login //Same User Can Log Back In @@ -153,7 +153,7 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id("LoginButton")).Click(); // We should be redirected to invoice - Assert.EndsWith("/UIManage/Index", s.Driver.Url); + Assert.EndsWith("/account", s.Driver.Url); // Should not be able to reach server settings s.GoToUrl("/server/users"); diff --git a/BTCPayServer/Controllers/UIManageController.APIKeys.cs b/BTCPayServer/Controllers/UIManageController.APIKeys.cs index b86ef4a48..7d605c67a 100644 --- a/BTCPayServer/Controllers/UIManageController.APIKeys.cs +++ b/BTCPayServer/Controllers/UIManageController.APIKeys.cs @@ -44,7 +44,7 @@ namespace BTCPayServer.Controllers DescriptionHtml = true, Description = $"Any application using the API key {key.Label ?? key.Id} will immediately lose access.", Action = "Delete", - ActionUrl = Url.ActionLink(nameof(DeleteAPIKeyPost), values: new { id }) + ActionName = nameof(DeleteAPIKeyPost) }); } diff --git a/BTCPayServer/Controllers/UIManageController.Notifications.cs b/BTCPayServer/Controllers/UIManageController.Notifications.cs index 12aca90ad..829c17497 100644 --- a/BTCPayServer/Controllers/UIManageController.Notifications.cs +++ b/BTCPayServer/Controllers/UIManageController.Notifications.cs @@ -13,7 +13,7 @@ namespace BTCPayServer.Controllers { public partial class UIManageController { - [HttpGet("notifications")] + [HttpGet("/notifications/settings")] public async Task NotificationSettings([FromServices] IEnumerable notificationHandlers) { var user = await _userManager.GetUserAsync(User); @@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers return View(new NotificationSettingsViewModel() { DisabledNotifications = notifications }); } - [HttpPost("notifications")] + [HttpPost("/notifications/settings")] public async Task NotificationSettings(NotificationSettingsViewModel vm, string command) { var user = await _userManager.GetUserAsync(User); diff --git a/BTCPayServer/Controllers/UIManageController.cs b/BTCPayServer/Controllers/UIManageController.cs index 44940d199..d6a83f29a 100644 --- a/BTCPayServer/Controllers/UIManageController.cs +++ b/BTCPayServer/Controllers/UIManageController.cs @@ -23,7 +23,7 @@ namespace BTCPayServer.Controllers { [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewProfile)] - [Route("[controller]/[action]")] + [Route("account/{action:lowercase=Index}")] public partial class UIManageController : Controller { private readonly UserManager _userManager; diff --git a/BTCPayServer/Controllers/UINotificationsController.cs b/BTCPayServer/Controllers/UINotificationsController.cs index cb6bd3629..4a57d00d1 100644 --- a/BTCPayServer/Controllers/UINotificationsController.cs +++ b/BTCPayServer/Controllers/UINotificationsController.cs @@ -20,7 +20,7 @@ namespace BTCPayServer.Controllers { [BitpayAPIConstraint(false)] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewNotificationsForUser)] - [Route("[controller]/[action]")] + [Route("notifications/{action:lowercase=Index}")] public class UINotificationsController : Controller { private readonly BTCPayServerEnvironment _env; diff --git a/BTCPayServer/Hosting/LowercaseTransformer.cs b/BTCPayServer/Hosting/LowercaseTransformer.cs new file mode 100644 index 000000000..321c4d75c --- /dev/null +++ b/BTCPayServer/Hosting/LowercaseTransformer.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace BTCPayServer.Hosting +{ + public class LowercaseTransformer : IOutboundParameterTransformer + { + public static void Register(IServiceCollection services) + { + services.AddRouting(opts => + { + opts.ConstraintMap["lowercase"] = typeof(LowercaseTransformer); + }); + } + + public string TransformOutbound(object value) + { + if (value is not string str) + return null; + return str.ToLowerInvariant(); + } + } +} diff --git a/BTCPayServer/Hosting/Startup.cs b/BTCPayServer/Hosting/Startup.cs index 44219eb2d..5ee0ec801 100644 --- a/BTCPayServer/Hosting/Startup.cs +++ b/BTCPayServer/Hosting/Startup.cs @@ -141,6 +141,8 @@ namespace BTCPayServer.Hosting .AddRazorRuntimeCompilation() .AddPlugins(services, Configuration, LoggerFactory) .AddControllersAsServices(); + + LowercaseTransformer.Register(services); ValidateControllerNameTransformer.Register(services); services.TryAddScoped(); @@ -270,7 +272,7 @@ namespace BTCPayServer.Hosting PaymentRequestHub.Register(endpoints); endpoints.MapRazorPages(); endpoints.MapControllers(); - endpoints.MapControllerRoute("default", "{controller:validate=UIHome}/{action=Index}/{id?}"); + endpoints.MapControllerRoute("default", "{controller:validate=UIHome}/{action:lowercase=Index}/{id?}"); }); app.UsePlugins(); } diff --git a/BTCPayServer/Models/ConfirmModel.cs b/BTCPayServer/Models/ConfirmModel.cs index ff49209d0..e77f1bdf7 100644 --- a/BTCPayServer/Models/ConfirmModel.cs +++ b/BTCPayServer/Models/ConfirmModel.cs @@ -8,26 +8,27 @@ namespace BTCPayServer.Models public ConfirmModel() { } - public ConfirmModel(string title, string desc, string action = null, string buttonClass = ButtonClassDefault, string actionUrl = null) + public ConfirmModel(string title, string desc, string action = null, string buttonClass = ButtonClassDefault, string actionName = null, string controllerName = null) { Title = title; Description = desc; Action = action; + ActionName = actionName; + ControllerName = controllerName; ButtonClass = buttonClass; if (Description.Contains("", StringComparison.InvariantCultureIgnoreCase)) { DescriptionHtml = true; } - - ActionUrl = actionUrl; } public string Title { get; set; } public string Description { get; set; } public bool DescriptionHtml { get; set; } public string Action { get; set; } + public string ActionName { get; set; } + public string ControllerName { get; set; } public string ButtonClass { get; set; } = ButtonClassDefault; - public string ActionUrl { get; set; } } } diff --git a/BTCPayServer/Views/Shared/ConfirmModal.cshtml b/BTCPayServer/Views/Shared/ConfirmModal.cshtml index 19342ce9e..7c0dfe1b5 100644 --- a/BTCPayServer/Views/Shared/ConfirmModal.cshtml +++ b/BTCPayServer/Views/Shared/ConfirmModal.cshtml @@ -1,5 +1,13 @@ -@model ConfirmModel - +@model ConfirmModel +@inject LinkGenerator linkGenerator +@{ + string actionUrl = null; + if (Model.ActionName is not null) + { + var controllerName = Model.ControllerName ?? ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)this.Url.ActionContext.ActionDescriptor).ControllerName; + actionUrl = linkGenerator.GetPathByAction(Model.ActionName, controllerName, pathBase: this.Context.Request.PathBase); + } +}