mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
LN Address - db schema fix method (#3638)
* LN Address - db schema fix method * pr changes * shorten
This commit is contained in:
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -18,7 +17,6 @@ using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
@@ -26,7 +24,6 @@ using BTCPayServer.Services.Stores;
|
||||
using LNURL;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Crypto;
|
||||
@@ -44,9 +41,10 @@ namespace BTCPayServer
|
||||
private readonly LightningLikePaymentHandler _lightningLikePaymentHandler;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly AppService _appService;
|
||||
|
||||
private readonly UIInvoiceController _invoiceController;
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly LightningAddressService _lightningAddressService;
|
||||
|
||||
public UILNURLController(InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator,
|
||||
@@ -55,8 +53,8 @@ namespace BTCPayServer
|
||||
StoreRepository storeRepository,
|
||||
AppService appService,
|
||||
UIInvoiceController invoiceController,
|
||||
SettingsRepository settingsRepository,
|
||||
LinkGenerator linkGenerator)
|
||||
LinkGenerator linkGenerator,
|
||||
LightningAddressService lightningAddressService)
|
||||
{
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
@@ -65,8 +63,8 @@ namespace BTCPayServer
|
||||
_storeRepository = storeRepository;
|
||||
_appService = appService;
|
||||
_invoiceController = invoiceController;
|
||||
_settingsRepository = settingsRepository;
|
||||
_linkGenerator = linkGenerator;
|
||||
_lightningAddressService = lightningAddressService;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +88,7 @@ namespace BTCPayServer
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(itemCode))
|
||||
{
|
||||
return NotFound();
|
||||
@@ -133,7 +132,7 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
return await GetLNURL(cryptoCode, app.StoreDataId, currencyCode, null, null,
|
||||
() => (null, new List<string> { AppService.GetAppInternalTag(appId) }, item.Price.Value, true));
|
||||
() => (null, new List<string> {AppService.GetAppInternalTag(appId)}, item.Price.Value, true));
|
||||
}
|
||||
|
||||
public class EditLightningAddressVM
|
||||
@@ -146,7 +145,7 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
public EditLightningAddressItem Add { get; set; }
|
||||
public List<EditLightningAddressItem> Items { get; set; } = new List<EditLightningAddressItem>();
|
||||
public List<EditLightningAddressItem> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class LightningAddressSettings
|
||||
@@ -154,8 +153,7 @@ namespace BTCPayServer
|
||||
public class LightningAddressItem
|
||||
{
|
||||
public string StoreId { get; set; }
|
||||
[Display(Name = "Invoice currency")]
|
||||
public string CurrencyCode { get; set; }
|
||||
[Display(Name = "Invoice currency")] public string CurrencyCode { get; set; }
|
||||
|
||||
[Display(Name = "Min sats")]
|
||||
[Range(1, double.PositiveInfinity)]
|
||||
@@ -178,22 +176,18 @@ namespace BTCPayServer
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<LightningAddressSettings> GetSettings()
|
||||
{
|
||||
return await _settingsRepository.GetSettingAsync<LightningAddressSettings>(nameof(LightningAddressSettings)) ??
|
||||
new LightningAddressSettings();
|
||||
}
|
||||
|
||||
[HttpGet("~/.well-known/lnurlp/{username}")]
|
||||
public async Task<IActionResult> ResolveLightningAddress(string username)
|
||||
{
|
||||
var lightningAddressSettings = await GetSettings();
|
||||
if (!lightningAddressSettings.Items.TryGetValue(username.ToLowerInvariant(), out var item))
|
||||
var lightningAddressSettings = await _lightningAddressService.ResolveByAddress(username);
|
||||
if (lightningAddressSettings is null)
|
||||
{
|
||||
return NotFound("Unknown username");
|
||||
}
|
||||
|
||||
return await GetLNURL("BTC", item.StoreId, item.CurrencyCode, item.Min, item.Max,
|
||||
var blob = lightningAddressSettings.Blob.GetBlob<LightningAddressDataBlob>();
|
||||
return await GetLNURL("BTC", lightningAddressSettings.StoreDataId, blob.CurrencyCode, blob.Min, blob.Max,
|
||||
() => (username, null, null, true));
|
||||
}
|
||||
|
||||
@@ -250,7 +244,7 @@ namespace BTCPayServer
|
||||
Amount = invoiceAmount,
|
||||
Checkout = new InvoiceDataBase.CheckoutOptions
|
||||
{
|
||||
PaymentMethods = new[] { pmi.ToStringNormalized() },
|
||||
PaymentMethods = new[] {pmi.ToStringNormalized()},
|
||||
Expiration = blob.InvoiceExpiration < TimeSpan.FromMinutes(2)
|
||||
? blob.InvoiceExpiration
|
||||
: TimeSpan.FromMinutes(2)
|
||||
@@ -273,10 +267,10 @@ namespace BTCPayServer
|
||||
await _invoiceRepository.UpdateInvoicePaymentMethod(i.Id, pm);
|
||||
}
|
||||
|
||||
lnurlMetadata.Add(new[] { "text/plain", i.Id });
|
||||
lnurlMetadata.Add(new[] {"text/plain", i.Id});
|
||||
if (!string.IsNullOrEmpty(username))
|
||||
{
|
||||
lnurlMetadata.Add(new[] { "text/identifier", lnAddress });
|
||||
lnurlMetadata.Add(new[] {"text/identifier", lnAddress});
|
||||
}
|
||||
|
||||
return Ok(new LNURLPayRequest
|
||||
@@ -292,7 +286,7 @@ namespace BTCPayServer
|
||||
Callback = new Uri(_linkGenerator.GetUriByAction(
|
||||
action: nameof(GetLNURLForInvoice),
|
||||
controller: "UILNURL",
|
||||
values: new { cryptoCode, invoiceId = i.Id }, Request.Scheme, Request.Host, Request.PathBase))
|
||||
values: new {cryptoCode, invoiceId = i.Id}, Request.Scheme, Request.Host, Request.PathBase))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,6 +300,7 @@ namespace BTCPayServer
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (comment is not null)
|
||||
comment = comment.Truncate(2000);
|
||||
|
||||
@@ -337,20 +332,16 @@ namespace BTCPayServer
|
||||
|
||||
List<string[]> lnurlMetadata = new List<string[]>();
|
||||
|
||||
lnurlMetadata.Add(new[] { "text/plain", i.Id });
|
||||
lnurlMetadata.Add(new[] {"text/plain", i.Id});
|
||||
if (!string.IsNullOrEmpty(paymentMethodDetails.ConsumedLightningAddress))
|
||||
{
|
||||
lnurlMetadata.Add(new[] { "text/identifier", paymentMethodDetails.ConsumedLightningAddress });
|
||||
lnurlMetadata.Add(new[] {"text/identifier", paymentMethodDetails.ConsumedLightningAddress});
|
||||
}
|
||||
|
||||
var metadata = JsonConvert.SerializeObject(lnurlMetadata);
|
||||
if (amount.HasValue && (amount < min || amount > max))
|
||||
{
|
||||
return BadRequest(new LNUrlStatusResponse
|
||||
{
|
||||
Status = "ERROR",
|
||||
Reason = "Amount is out of bounds."
|
||||
});
|
||||
return BadRequest(new LNUrlStatusResponse {Status = "ERROR", Reason = "Amount is out of bounds."});
|
||||
}
|
||||
|
||||
if (amount.HasValue && string.IsNullOrEmpty(paymentMethodDetails.BOLT11) ||
|
||||
@@ -379,7 +370,7 @@ namespace BTCPayServer
|
||||
descriptionHash,
|
||||
i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow));
|
||||
if (!BOLT11PaymentRequest.Parse(invoice.BOLT11, network.NBitcoinNetwork)
|
||||
.VerifyDescriptionHash(metadata))
|
||||
.VerifyDescriptionHash(metadata))
|
||||
{
|
||||
return BadRequest(new LNUrlStatusResponse
|
||||
{
|
||||
@@ -413,9 +404,7 @@ namespace BTCPayServer
|
||||
paymentMethodDetails, pmi));
|
||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||
{
|
||||
Disposable = true,
|
||||
Routes = Array.Empty<string>(),
|
||||
Pr = paymentMethodDetails.BOLT11
|
||||
Disposable = true, Routes = Array.Empty<string>(), Pr = paymentMethodDetails.BOLT11
|
||||
});
|
||||
}
|
||||
|
||||
@@ -430,9 +419,7 @@ namespace BTCPayServer
|
||||
|
||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||
{
|
||||
Disposable = true,
|
||||
Routes = Array.Empty<string>(),
|
||||
Pr = paymentMethodDetails.BOLT11
|
||||
Disposable = true, Routes = Array.Empty<string>(), Pr = paymentMethodDetails.BOLT11
|
||||
});
|
||||
}
|
||||
|
||||
@@ -452,8 +439,7 @@ namespace BTCPayServer
|
||||
|
||||
return BadRequest(new LNUrlStatusResponse
|
||||
{
|
||||
Status = "ERROR",
|
||||
Reason = "Invoice not in a valid payable state"
|
||||
Status = "ERROR", Reason = "Invoice not in a valid payable state"
|
||||
});
|
||||
}
|
||||
|
||||
@@ -463,37 +449,39 @@ namespace BTCPayServer
|
||||
[HttpGet("~/stores/{storeId}/integrations/lightning-address")]
|
||||
public async Task<IActionResult> EditLightningAddress(string storeId)
|
||||
{
|
||||
if (ControllerContext.HttpContext.GetStoreData().GetEnabledPaymentIds(_btcPayNetworkProvider).All(id => id.PaymentType != LNURLPayPaymentType.Instance))
|
||||
if (ControllerContext.HttpContext.GetStoreData().GetEnabledPaymentIds(_btcPayNetworkProvider)
|
||||
.All(id => id.PaymentType != LNURLPayPaymentType.Instance))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Message = "LNURL is required for lightning addresses but has not yet been enabled.",
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.GeneralSettings), "UIStores", new { storeId });
|
||||
}
|
||||
var lightningAddressSettings = await GetSettings();
|
||||
if (lightningAddressSettings.StoreToItemMap.TryGetValue(storeId, out var addresses))
|
||||
{
|
||||
return View(new EditLightningAddressVM
|
||||
{
|
||||
Items = addresses.Select(s => new EditLightningAddressVM.EditLightningAddressItem
|
||||
{
|
||||
Max = lightningAddressSettings.Items[s].Max,
|
||||
Min = lightningAddressSettings.Items[s].Min,
|
||||
CurrencyCode = lightningAddressSettings.Items[s].CurrencyCode,
|
||||
StoreId = lightningAddressSettings.Items[s].StoreId,
|
||||
Username = s,
|
||||
}).ToList()
|
||||
});
|
||||
return RedirectToAction(nameof(UIStoresController.GeneralSettings), "UIStores", new {storeId});
|
||||
}
|
||||
|
||||
var addresses =
|
||||
await _lightningAddressService.Get(new LightningAddressQuery() {StoreIds = new[] {storeId}});
|
||||
|
||||
return View(new EditLightningAddressVM
|
||||
{
|
||||
Items = new List<EditLightningAddressVM.EditLightningAddressItem>()
|
||||
Items = addresses.Select(s =>
|
||||
{
|
||||
var blob = s.Blob.GetBlob<LightningAddressDataBlob>();
|
||||
return new EditLightningAddressVM.EditLightningAddressItem
|
||||
{
|
||||
Max = blob.Max,
|
||||
Min = blob.Min,
|
||||
CurrencyCode = blob.CurrencyCode,
|
||||
StoreId = storeId,
|
||||
Username = s.Username,
|
||||
};
|
||||
}
|
||||
).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpPost("~/stores/{storeId}/integrations/lightning-address")]
|
||||
@@ -502,69 +490,71 @@ namespace BTCPayServer
|
||||
{
|
||||
if (command == "add")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(vm.Add.CurrencyCode) && currencyNameTable.GetCurrencyData(vm.Add.CurrencyCode, false) is null)
|
||||
if (!string.IsNullOrEmpty(vm.Add.CurrencyCode) &&
|
||||
currencyNameTable.GetCurrencyData(vm.Add.CurrencyCode, false) is null)
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.CurrencyCode, "Currency is invalid", this);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
var lightningAddressSettings = await GetSettings();
|
||||
if (lightningAddressSettings.Items.ContainsKey(vm.Add.Username.ToLowerInvariant()))
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, "Username is already taken", this);
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
|
||||
if (lightningAddressSettings.StoreToItemMap.TryGetValue(storeId, out var ids))
|
||||
if (await _lightningAddressService.Set(new LightningAddressData()
|
||||
{
|
||||
StoreDataId = storeId,
|
||||
Username = vm.Add.Username,
|
||||
Blob = new LightningAddressDataBlob()
|
||||
{
|
||||
Max = vm.Add.Max, Min = vm.Add.Min, CurrencyCode = vm.Add.CurrencyCode
|
||||
}.SerializeBlob()
|
||||
}))
|
||||
{
|
||||
ids = ids.Concat(new[] { vm.Add.Username.ToLowerInvariant() }).ToArray();
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Lightning address added successfully."
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ids = new[] { vm.Add.Username.ToLowerInvariant() };
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, "Username is already taken", this);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
|
||||
lightningAddressSettings.StoreToItemMap.AddOrReplace(storeId, ids);
|
||||
vm.Add.StoreId = storeId;
|
||||
lightningAddressSettings.Items.TryAdd(vm.Add.Username.ToLowerInvariant(), vm.Add);
|
||||
await _settingsRepository.UpdateSetting(lightningAddressSettings, nameof(LightningAddressSettings));
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = "Lightning address added successfully."
|
||||
});
|
||||
|
||||
return RedirectToAction("EditLightningAddress");
|
||||
}
|
||||
|
||||
if (command.StartsWith("remove", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var lightningAddressSettings = await GetSettings();
|
||||
var index = int.Parse(
|
||||
command.Substring(command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1),
|
||||
CultureInfo.InvariantCulture);
|
||||
if (lightningAddressSettings.StoreToItemMap.TryGetValue(storeId, out var addresses))
|
||||
var index = command.Substring(command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1);
|
||||
if (await _lightningAddressService.Remove(index, storeId))
|
||||
{
|
||||
var addressToRemove = addresses[index];
|
||||
addresses = addresses.Where(s => s != addressToRemove).ToArray();
|
||||
lightningAddressSettings.StoreToItemMap.AddOrReplace(storeId, addresses);
|
||||
lightningAddressSettings.Items.TryRemove(addressToRemove, out _);
|
||||
await _settingsRepository.UpdateSetting(lightningAddressSettings, nameof(LightningAddressSettings));
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Message = $"Lightning address {addressToRemove} removed successfully."
|
||||
Message = $"Lightning address {index} removed successfully."
|
||||
});
|
||||
return RedirectToAction("EditLightningAddress");
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.Username, "Username could not be removed", this);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
|
||||
return RedirectToAction("EditLightningAddress");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user