From 7341be76bb9a4a61f74bda5f0b8cea6530f1a6dc Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Aug 2018 13:16:24 -0500 Subject: [PATCH] Extracting public portion of app controller --- BTCPayServer/BTCPayServer.csproj | 4 + .../Controllers/AppsController.PointOfSale.cs | 153 +-------------- BTCPayServer/Controllers/AppsController.cs | 45 ++--- .../Controllers/AppsPublicController.cs | 182 ++++++++++++++++++ BTCPayServer/Hosting/BTCPayServerServices.cs | 2 + BTCPayServer/Views/Apps/ListApps.cshtml | 2 +- .../ViewPointOfSale.cshtml | 2 +- 7 files changed, 215 insertions(+), 175 deletions(-) create mode 100644 BTCPayServer/Controllers/AppsPublicController.cs rename BTCPayServer/Views/{Apps => AppsPublic}/ViewPointOfSale.cshtml (97%) diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index d0839c056..f84b96672 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -121,6 +121,10 @@ + + PreserveNewest + $(IncludeRazorContentInPack) + $(IncludeRazorContentInPack) diff --git a/BTCPayServer/Controllers/AppsController.PointOfSale.cs b/BTCPayServer/Controllers/AppsController.PointOfSale.cs index bd6dadad7..c65f50329 100644 --- a/BTCPayServer/Controllers/AppsController.PointOfSale.cs +++ b/BTCPayServer/Controllers/AppsController.PointOfSale.cs @@ -1,25 +1,11 @@ -using System; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; +using System.Text; +using System.Text.Encodings.Web; using System.Threading.Tasks; using BTCPayServer.Data; -using BTCPayServer.Models; using BTCPayServer.Models.AppViewModels; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using NBitcoin.DataEncoders; -using NBitcoin; using BTCPayServer.Services.Apps; -using Newtonsoft.Json; -using YamlDotNet.RepresentationModel; -using System.IO; -using BTCPayServer.Services.Rates; -using System.Globalization; -using System.Text; -using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace BTCPayServer.Controllers { @@ -87,7 +73,7 @@ namespace BTCPayServer.Controllers } try { - var items = Parse(settings.Template, settings.Currency); + var items = _AppsHelper.Parse(settings.Template, settings.Currency); var builder = new StringBuilder(); builder.AppendLine($"
"); builder.AppendLine($" "); @@ -109,11 +95,11 @@ namespace BTCPayServer.Controllers [Route("{appId}/settings/pos")] public async Task UpdatePointOfSale(string appId, UpdatePointOfSaleViewModel vm) { - if (_Currencies.GetCurrencyData(vm.Currency, false) == null) + if (_AppsHelper.GetCurrencyData(vm.Currency, false) == null) ModelState.AddModelError(nameof(vm.Currency), "Invalid currency"); try { - Parse(vm.Template, vm.Currency); + _AppsHelper.Parse(vm.Template, vm.Currency); } catch { @@ -138,131 +124,6 @@ namespace BTCPayServer.Controllers return RedirectToAction(nameof(ListApps)); } - [HttpGet] - [Route("{appId}/pos")] - public async Task ViewPointOfSale(string appId) - { - var app = await GetApp(appId, AppType.PointOfSale); - if (app == null) - return NotFound(); - var settings = app.GetSettings(); - var currency = _Currencies.GetCurrencyData(settings.Currency, false); - double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility)); - - return View(new ViewPointOfSaleViewModel() - { - Title = settings.Title, - Step = step.ToString(CultureInfo.InvariantCulture), - ShowCustomAmount = settings.ShowCustomAmount, - Items = Parse(settings.Template, settings.Currency) - }); - } - - private async Task GetApp(string appId, AppType appType) - { - using (var ctx = _ContextFactory.CreateContext()) - { - return await ctx.Apps - .Where(us => us.Id == appId && - us.AppType == appType.ToString()) - .FirstOrDefaultAsync(); - } - } - - private ViewPointOfSaleViewModel.Item[] Parse(string template, string currency) - { - var input = new StringReader(template); - YamlStream stream = new YamlStream(); - stream.Load(input); - var root = (YamlMappingNode)stream.Documents[0].RootNode; - return root - .Children - .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode }) - .Where(kv => kv.Value != null) - .Select(c => new ViewPointOfSaleViewModel.Item() - { - Id = c.Key, - Title = c.Value.Children - .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode }) - .Where(kv => kv.Value != null) - .Where(cc => cc.Key == "title") - .FirstOrDefault()?.Value?.Value ?? c.Key, - Price = c.Value.Children - .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode }) - .Where(kv => kv.Value != null) - .Where(cc => cc.Key == "price") - .Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice() - { - Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture), - Formatted = FormatCurrency(cc.Value.Value, currency) - }) - .Single() - }) - .ToArray(); - } - - string FormatCurrency(string price, string currency) - { - return decimal.Parse(price, CultureInfo.InvariantCulture).ToString("C", _Currencies.GetCurrencyProvider(currency)); - } - - [HttpPost] - [Route("{appId}/pos")] - [IgnoreAntiforgeryToken] - [EnableCors(CorsPolicies.All)] - public async Task ViewPointOfSale(string appId, - decimal amount, - string email, - string orderId, - string notificationUrl, - string redirectUrl, - string choiceKey) - { - var app = await GetApp(appId, AppType.PointOfSale); - if (string.IsNullOrEmpty(choiceKey) && amount <= 0) - { - return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId }); - } - if (app == null) - return NotFound(); - var settings = app.GetSettings(); - if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount) - { - return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId }); - } - string title = null; - var price = 0.0m; - if (!string.IsNullOrEmpty(choiceKey)) - { - var choices = Parse(settings.Template, settings.Currency); - var choice = choices.FirstOrDefault(c => c.Id == choiceKey); - if (choice == null) - return NotFound(); - title = choice.Title; - price = choice.Price.Value; - } - else - { - if (!settings.ShowCustomAmount) - return NotFound(); - price = amount; - title = settings.Title; - } - var store = await GetStore(app); - var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice() - { - ItemDesc = title, - Currency = settings.Currency, - Price = price, - BuyerEmail = email, - OrderId = orderId, - NotificationURL = notificationUrl, - RedirectURL = redirectUrl, - FullNotifications = true - }, store, HttpContext.Request.GetAbsoluteRoot()); - return Redirect(invoice.Data.Url); - } - private async Task UpdateAppSettings(AppData app) { using (var ctx = _ContextFactory.CreateContext()) diff --git a/BTCPayServer/Controllers/AppsController.cs b/BTCPayServer/Controllers/AppsController.cs index 4677e2480..6d3af112a 100644 --- a/BTCPayServer/Controllers/AppsController.cs +++ b/BTCPayServer/Controllers/AppsController.cs @@ -5,8 +5,9 @@ using System.Threading.Tasks; using BTCPayServer.Data; using BTCPayServer.Models; using BTCPayServer.Models.AppViewModels; +using BTCPayServer.Security; using BTCPayServer.Services.Apps; -using BTCPayServer.Services.Rates; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -19,28 +20,26 @@ namespace BTCPayServer.Controllers [Route("apps")] public partial class AppsController : Controller { - ApplicationDbContextFactory _ContextFactory; - UserManager _UserManager; - CurrencyNameTable _Currencies; - InvoiceController _InvoiceController; - BTCPayNetworkProvider _NetworkProvider; + public AppsController( + UserManager userManager, + ApplicationDbContextFactory contextFactory, + BTCPayNetworkProvider networkProvider, + AppsHelper appsHelper) + { + _UserManager = userManager; + _ContextFactory = contextFactory; + _NetworkProvider = networkProvider; + _AppsHelper = appsHelper; + } + + private UserManager _UserManager; + private ApplicationDbContextFactory _ContextFactory; + private BTCPayNetworkProvider _NetworkProvider; + private AppsHelper _AppsHelper; [TempData] public string StatusMessage { get; set; } - public AppsController( - UserManager userManager, - ApplicationDbContextFactory contextFactory, - CurrencyNameTable currencies, - InvoiceController invoiceController, - BTCPayNetworkProvider networkProvider) - { - _InvoiceController = invoiceController; - _UserManager = userManager; - _ContextFactory = contextFactory; - _Currencies = currencies; - _NetworkProvider = networkProvider; - } public async Task ListApps() { var apps = await GetAllApps(); @@ -201,13 +200,5 @@ namespace BTCPayServer.Controllers { return _UserManager.GetUserId(User); } - - private async Task GetStore(AppData app) - { - using (var ctx = _ContextFactory.CreateContext()) - { - return await ctx.Stores.FirstOrDefaultAsync(s => s.Id == app.StoreDataId); - } - } } } diff --git a/BTCPayServer/Controllers/AppsPublicController.cs b/BTCPayServer/Controllers/AppsPublicController.cs new file mode 100644 index 000000000..dceca27d4 --- /dev/null +++ b/BTCPayServer/Controllers/AppsPublicController.cs @@ -0,0 +1,182 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Data; +using BTCPayServer.Models.AppViewModels; +using BTCPayServer.Services.Apps; +using BTCPayServer.Services.Rates; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using YamlDotNet.RepresentationModel; +using static BTCPayServer.Controllers.AppsController; + +namespace BTCPayServer.Controllers +{ + public class AppsPublicController : Controller + { + public AppsPublicController(AppsHelper appsHelper, InvoiceController invoiceController) + { + _AppsHelper = appsHelper; + _InvoiceController = invoiceController; + } + + private AppsHelper _AppsHelper; + private InvoiceController _InvoiceController; + + [HttpGet] + [Route("/apps/{appId}/pos")] + public async Task ViewPointOfSale(string appId) + { + var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale); + if (app == null) + return NotFound(); + var settings = app.GetSettings(); + var currency = _AppsHelper.GetCurrencyData(settings.Currency, false); + double step = currency == null ? 1 : Math.Pow(10, -(currency.Divisibility)); + + return View(new ViewPointOfSaleViewModel() + { + Title = settings.Title, + Step = step.ToString(CultureInfo.InvariantCulture), + ShowCustomAmount = settings.ShowCustomAmount, + Items = _AppsHelper.Parse(settings.Template, settings.Currency) + }); + } + + [HttpPost] + [Route("/apps/{appId}/pos")] + [IgnoreAntiforgeryToken] + [EnableCors(CorsPolicies.All)] + public async Task ViewPointOfSale(string appId, + decimal amount, + string email, + string orderId, + string notificationUrl, + string redirectUrl, + string choiceKey) + { + var app = await _AppsHelper.GetApp(appId, AppType.PointOfSale); + if (string.IsNullOrEmpty(choiceKey) && amount <= 0) + { + return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId }); + } + if (app == null) + return NotFound(); + var settings = app.GetSettings(); + if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount) + { + return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId }); + } + string title = null; + var price = 0.0m; + if (!string.IsNullOrEmpty(choiceKey)) + { + var choices = _AppsHelper.Parse(settings.Template, settings.Currency); + var choice = choices.FirstOrDefault(c => c.Id == choiceKey); + if (choice == null) + return NotFound(); + title = choice.Title; + price = choice.Price.Value; + } + else + { + if (!settings.ShowCustomAmount) + return NotFound(); + price = amount; + title = settings.Title; + } + var store = await _AppsHelper.GetStore(app); + var invoice = await _InvoiceController.CreateInvoiceCore(new NBitpayClient.Invoice() + { + ItemDesc = title, + Currency = settings.Currency, + Price = price, + BuyerEmail = email, + OrderId = orderId, + NotificationURL = notificationUrl, + RedirectURL = redirectUrl, + FullNotifications = true + }, store, HttpContext.Request.GetAbsoluteRoot()); + return Redirect(invoice.Data.Url); + } + } + + + public class AppsHelper + { + ApplicationDbContextFactory _ContextFactory; + CurrencyNameTable _Currencies; + + public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies) + { + _ContextFactory = contextFactory; + _Currencies = currencies; + + } + + public async Task GetApp(string appId, AppType appType) + { + using (var ctx = _ContextFactory.CreateContext()) + { + return await ctx.Apps + .Where(us => us.Id == appId && + us.AppType == appType.ToString()) + .FirstOrDefaultAsync(); + } + } + + public async Task GetStore(AppData app) + { + using (var ctx = _ContextFactory.CreateContext()) + { + return await ctx.Stores.FirstOrDefaultAsync(s => s.Id == app.StoreDataId); + } + } + + public ViewPointOfSaleViewModel.Item[] Parse(string template, string currency) + { + var input = new StringReader(template); + YamlStream stream = new YamlStream(); + stream.Load(input); + var root = (YamlMappingNode)stream.Documents[0].RootNode; + return root + .Children + .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode }) + .Where(kv => kv.Value != null) + .Select(c => new ViewPointOfSaleViewModel.Item() + { + Id = c.Key, + Title = c.Value.Children + .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode }) + .Where(kv => kv.Value != null) + .Where(cc => cc.Key == "title") + .FirstOrDefault()?.Value?.Value ?? c.Key, + Price = c.Value.Children + .Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode }) + .Where(kv => kv.Value != null) + .Where(cc => cc.Key == "price") + .Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice() + { + Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture), + Formatted = FormatCurrency(cc.Value.Value, currency) + }) + .Single() + }) + .ToArray(); + } + + public string FormatCurrency(string price, string currency) + { + return decimal.Parse(price, CultureInfo.InvariantCulture).ToString("C", _Currencies.GetCurrencyProvider(currency)); + } + + public CurrencyData GetCurrencyData(string currency, bool useFallback) + { + return _Currencies.GetCurrencyData(currency, useFallback); + } + } +} diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index d511fb90c..80569bec4 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -95,6 +95,8 @@ namespace BTCPayServer.Hosting return opts.NetworkProvider; }); + services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/BTCPayServer/Views/Apps/ListApps.cshtml b/BTCPayServer/Views/Apps/ListApps.cshtml index 6f82944aa..b5aaa7e36 100644 --- a/BTCPayServer/Views/Apps/ListApps.cshtml +++ b/BTCPayServer/Views/Apps/ListApps.cshtml @@ -53,7 +53,7 @@ { Settings - } - View - + View - Remove diff --git a/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml b/BTCPayServer/Views/AppsPublic/ViewPointOfSale.cshtml similarity index 97% rename from BTCPayServer/Views/Apps/ViewPointOfSale.cshtml rename to BTCPayServer/Views/AppsPublic/ViewPointOfSale.cshtml index a4445ca83..29af7f137 100644 --- a/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml +++ b/BTCPayServer/Views/AppsPublic/ViewPointOfSale.cshtml @@ -1,6 +1,6 @@ @inject BTCPayServer.HostedServices.CssThemeManager themeManager -@model ViewPointOfSaleViewModel +@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel @{ ViewData["Title"] = Model.Title; Layout = null;